static UA_StatusCode socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeout) { response->data = malloc(connection->localConf.recvBufferSize); if(!response->data) { UA_ByteString_init(response); return UA_STATUSCODE_GOOD; /* not enough memory retry */ } struct timeval tmptv = {0, timeout * 1000}; if(0 != setsockopt(connection->sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tmptv, sizeof(struct timeval))){ free(response->data); UA_ByteString_init(response); socket_close(connection); return UA_STATUSCODE_BADINTERNALERROR; } int ret = recv(connection->sockfd, (char*)response->data, connection->localConf.recvBufferSize, 0); if(ret == 0) { free(response->data); UA_ByteString_init(response); socket_close(connection); return UA_STATUSCODE_BADCONNECTIONCLOSED; /* ret == 0 -> server has closed the connection */ } else if(ret < 0) { free(response->data); UA_ByteString_init(response); #ifdef _WIN32 if(WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK) { #else if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { #endif return UA_STATUSCODE_GOOD; /* retry */ } else { socket_close(connection); return UA_STATUSCODE_BADCONNECTIONCLOSED; } } response->length = ret; *response = UA_Connection_completeMessages(connection, *response); return UA_STATUSCODE_GOOD; } static UA_StatusCode socket_set_nonblocking(UA_Int32 sockfd) { #ifdef _WIN32 u_long iMode = 1; if(ioctlsocket(sockfd, FIONBIO, &iMode) != NO_ERROR) return UA_STATUSCODE_BADINTERNALERROR; #else int opts = fcntl(sockfd, F_GETFL); if(opts < 0 || fcntl(sockfd, F_SETFL, opts|O_NONBLOCK) < 0) return UA_STATUSCODE_BADINTERNALERROR; #endif return UA_STATUSCODE_GOOD; }
static UA_StatusCode ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl) { ServerNetworkLayerTCP *layer = nl->handle; #ifdef _WIN32 if((layer->serversockfd = socket(PF_INET, SOCK_STREAM,0)) == (UA_Int32)INVALID_SOCKET) { UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error opening socket, code: %d", WSAGetLastError()); return UA_STATUSCODE_BADINTERNALERROR; } #else if((layer->serversockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error opening socket"); return UA_STATUSCODE_BADINTERNALERROR; } #endif const struct sockaddr_in serv_addr = {.sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY, .sin_port = htons(layer->port), .sin_zero = {0}}; int optval = 1; if(setsockopt(layer->serversockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(optval)) == -1) { UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error during setting of socket options"); CLOSESOCKET(layer->serversockfd); return UA_STATUSCODE_BADINTERNALERROR; } if(bind(layer->serversockfd, (const struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error during socket binding"); CLOSESOCKET(layer->serversockfd); return UA_STATUSCODE_BADINTERNALERROR; } socket_set_nonblocking(layer->serversockfd); listen(layer->serversockfd, MAXBACKLOG); UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "TCP network layer listening on %.*s", nl->discoveryUrl.length, nl->discoveryUrl.data); return UA_STATUSCODE_GOOD; } static size_t ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt16 timeout) { ServerNetworkLayerTCP *layer = nl->handle; fd_set fdset; UA_Int32 highestfd = setFDSet(layer, &fdset); struct timeval tmptv = {0, timeout}; UA_Int32 resultsize; resultsize = select(highestfd+1, &fdset, NULL, NULL, &tmptv); if(resultsize < 0) { *jobs = NULL; return 0; } /* accept new connections (can only be a single one) */ if(FD_ISSET(layer->serversockfd, &fdset)) { resultsize--; struct sockaddr_in cli_addr; socklen_t cli_len = sizeof(cli_addr); int newsockfd = accept(layer->serversockfd, (struct sockaddr *) &cli_addr, &cli_len); int i = 1; setsockopt(newsockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&i, sizeof(i)); if(newsockfd >= 0) { socket_set_nonblocking(newsockfd); ServerNetworkLayerTCP_add(layer, newsockfd); } } /* alloc enough space for a cleanup-connection and free-connection job per resulted socket */ if(resultsize == 0) return 0; UA_Job *js = malloc(sizeof(UA_Job) * resultsize * 2); if(!js) return 0; /* read from established sockets */ size_t j = 0; UA_ByteString buf = UA_BYTESTRING_NULL; for(size_t i = 0; i < layer->mappingsSize && j < (size_t)resultsize; i++) { if(!(FD_ISSET(layer->mappings[i].sockfd, &fdset))) continue; UA_StatusCode retval = socket_recv(layer->mappings[i].connection, &buf, 0); if(retval == UA_STATUSCODE_GOOD) { UA_Boolean realloced = UA_FALSE; retval = UA_Connection_completeMessages(layer->mappings[i].connection, &buf, &realloced); if(retval != UA_STATUSCODE_GOOD || buf.length == 0) continue; js[j].job.binaryMessage.connection = layer->mappings[i].connection; js[j].job.binaryMessage.message = buf; if(!realloced) js[j].type = UA_JOBTYPE_BINARYMESSAGE_NETWORKLAYER; else js[j].type = UA_JOBTYPE_BINARYMESSAGE_ALLOCATED; j++; } else if (retval == UA_STATUSCODE_BADCONNECTIONCLOSED) { UA_Connection *c = layer->mappings[i].connection; /* the socket was closed from remote */ js[j].type = UA_JOBTYPE_DETACHCONNECTION; js[j].job.closeConnection = layer->mappings[i].connection; layer->mappings[i] = layer->mappings[layer->mappingsSize-1]; layer->mappingsSize--; j++; js[j].type = UA_JOBTYPE_METHODCALL_DELAYED; js[j].job.methodCall.method = FreeConnectionCallback; js[j].job.methodCall.data = c; j++; } } if(j == 0) { free(js); js = NULL; } *jobs = js; return j; }