/** * Triggering an event * ^^^^^^^^^^^^^^^^^^^ * First a node representing an event is generated using ``setUpEvent``. Once our event is good to go, we specify * a node which emits the event - in this case the server node. We can use ``UA_Server_triggerEvent`` to trigger our * event onto said node. Passing ``NULL`` as the second-last argument means we will not receive the `EventId`. * The last boolean argument states whether the node should be deleted. */ static UA_StatusCode generateEventMethodCallback(UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output) { UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Creating event"); /* set up event */ UA_NodeId eventNodeId; UA_StatusCode retval = setUpEvent(server, &eventNodeId); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Creating event failed. StatusCode %s", UA_StatusCode_name(retval)); return retval; } retval = UA_Server_triggerEvent(server, eventNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), NULL, UA_TRUE); if(retval != UA_STATUSCODE_GOOD) UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Triggering event failed. StatusCode %s", UA_StatusCode_name(retval)); return retval; }
/** * Setting up an event * ^^^^^^^^^^^^^^^^^^^ * In order to set up the event, we can first use ``UA_Server_createEvent`` to give us a node representation of the event. * All we need for this is our `EventType`. Once we have our event node, which is saved internally as an `ObjectNode`, * we can define the attributes the event has the same way we would define the attributes of an object node. It is not * necessary to define the attributes `EventId`, `ReceiveTime`, `SourceNode` or `EventType` since these are set * automatically by the server. In this example, we will be setting the fields 'Message' and 'Severity' in addition * to `Time` which is needed to make the example UaExpert compliant. */ static UA_StatusCode setUpEvent(UA_Server *server, UA_NodeId *outId) { UA_StatusCode retval = UA_Server_createEvent(server, eventType, outId); if (retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "createEvent failed. StatusCode %s", UA_StatusCode_name(retval)); return retval; } /* Set the Event Attributes */ /* Setting the Time is required or else the event will not show up in UAExpert! */ UA_DateTime eventTime = UA_DateTime_now(); UA_Server_writeObjectProperty_scalar(server, *outId, UA_QUALIFIEDNAME(0, "Time"), &eventTime, &UA_TYPES[UA_TYPES_DATETIME]); UA_UInt16 eventSeverity = 100; UA_Server_writeObjectProperty_scalar(server, *outId, UA_QUALIFIEDNAME(0, "Severity"), &eventSeverity, &UA_TYPES[UA_TYPES_UINT16]); UA_LocalizedText eventMessage = UA_LOCALIZEDTEXT("en-US", "An event has been generated."); UA_Server_writeObjectProperty_scalar(server, *outId, UA_QUALIFIEDNAME(0, "Message"), &eventMessage, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); UA_String eventSourceName = UA_STRING("Server"); UA_Server_writeObjectProperty_scalar(server, *outId, UA_QUALIFIEDNAME(0, "SourceName"), &eventSourceName, &UA_TYPES[UA_TYPES_STRING]); return UA_STATUSCODE_GOOD; }
static void processJob(UA_Server *server, UA_Job *job) { UA_ASSERT_RCU_UNLOCKED(); UA_RCU_LOCK(); switch(job->type) { case UA_JOBTYPE_NOTHING: break; case UA_JOBTYPE_DETACHCONNECTION: UA_Connection_detachSecureChannel(job->job.closeConnection); break; case UA_JOBTYPE_BINARYMESSAGE_NETWORKLAYER: UA_Server_processBinaryMessage(server, job->job.binaryMessage.connection, &job->job.binaryMessage.message); UA_Connection *connection = job->job.binaryMessage.connection; connection->releaseRecvBuffer(connection, &job->job.binaryMessage.message); break; case UA_JOBTYPE_BINARYMESSAGE_ALLOCATED: UA_Server_processBinaryMessage(server, job->job.binaryMessage.connection, &job->job.binaryMessage.message); UA_ByteString_deleteMembers(&job->job.binaryMessage.message); break; case UA_JOBTYPE_METHODCALL: case UA_JOBTYPE_METHODCALL_DELAYED: job->job.methodCall.method(server, job->job.methodCall.data); break; default: UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "Trying to execute a job of unknown type"); break; } UA_RCU_UNLOCK(); }
static UA_StatusCode readTemperature(void *handle, const UA_NodeId nodeId, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) { if(range) { value->hasStatus = true; value->status = UA_STATUSCODE_BADINDEXRANGEINVALID; return UA_STATUSCODE_GOOD; } rewind(temperatureFile); fflush(temperatureFile); UA_Double currentTemperature; if(fscanf(temperatureFile, "%lf", ¤tTemperature) != 1){ UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "Can not parse temperature"); exit(1); } currentTemperature /= 1000.0; value->sourceTimestamp = UA_DateTime_now(); value->hasSourceTimestamp = true; UA_Variant_setScalarCopy(&value->value, ¤tTemperature, &UA_TYPES[UA_TYPES_DOUBLE]); value->hasValue = true; return UA_STATUSCODE_GOOD; }
static int run(UA_String *transportProfile, UA_NetworkAddressUrlDataType *networkAddressUrl) { signal(SIGINT, stopHandler); signal(SIGTERM, stopHandler); UA_Server *server = UA_Server_new(); UA_ServerConfig *config = UA_Server_getConfig(server); UA_ServerConfig_setMinimal(config, 4801, NULL); /* Details about the PubSubTransportLayer can be found inside the * tutorial_pubsub_connection */ config->pubsubTransportLayers = (UA_PubSubTransportLayer *) UA_calloc(2, sizeof(UA_PubSubTransportLayer)); if(!config->pubsubTransportLayers) { UA_Server_delete(server); return EXIT_FAILURE; } config->pubsubTransportLayers[0] = UA_PubSubTransportLayerUDPMP(); config->pubsubTransportLayersSize++; #ifdef UA_ENABLE_PUBSUB_ETH_UADP config->pubsubTransportLayers[1] = UA_PubSubTransportLayerEthernet(); config->pubsubTransportLayersSize++; #endif UA_PubSubConnectionConfig connectionConfig; memset(&connectionConfig, 0, sizeof(connectionConfig)); connectionConfig.name = UA_STRING("UADP Connection 1"); connectionConfig.transportProfileUri = *transportProfile; connectionConfig.enabled = UA_TRUE; UA_Variant_setScalar(&connectionConfig.address, networkAddressUrl, &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]); UA_NodeId connectionIdent; UA_StatusCode retval = UA_Server_addPubSubConnection(server, &connectionConfig, &connectionIdent); if(retval == UA_STATUSCODE_GOOD) UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "The PubSub Connection was created successfully!"); /* The following lines register the listening on the configured multicast * address and configure a repeated job, which is used to handle received * messages. */ UA_PubSubConnection *connection = UA_PubSubConnection_findConnectionbyId(server, connectionIdent); if(connection != NULL) { UA_StatusCode rv = connection->channel->regist(connection->channel, NULL, NULL); if (rv == UA_STATUSCODE_GOOD) { UA_UInt64 subscriptionCallbackId; UA_Server_addRepeatedCallback(server, (UA_ServerCallback)subscriptionPollingCallback, connection, 100, &subscriptionCallbackId); } else { UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "register channel failed: %s!", UA_StatusCode_name(rv)); } } retval |= UA_Server_run(server, &running); UA_Server_delete(server); return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;; }
/** Accesses only the sockfd in the handle. Can be run from parallel threads. */ static UA_StatusCode sendUDP(UA_Connection *connection, UA_ByteString *buf) { UDPConnection *udpc = (UDPConnection*)connection; ServerNetworkLayerUDP *layer = (ServerNetworkLayerUDP*)connection->handle; size_t nWritten = 0; struct sockaddr_in *sin = NULL; if (udpc->from.sa_family == AF_INET) { #if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__)) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" #endif sin = (struct sockaddr_in *) &udpc->from; #if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__)) #pragma GCC diagnostic pop #endif } else { UA_ByteString_deleteMembers(buf); return UA_STATUSCODE_BADINTERNALERROR; } while (nWritten < (size_t)buf->length) { UA_Int32 n = sendto(layer->serversockfd, buf->data, buf->length, 0, (struct sockaddr*)sin, sizeof(struct sockaddr_in)); if(n == -1L) { UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_COMMUNICATION, "UDP send error %i", errno); UA_ByteString_deleteMembers(buf); return UA_STATUSCODE_BADINTERNALERROR; } nWritten += n; } UA_ByteString_deleteMembers(buf); return UA_STATUSCODE_GOOD; }
static UA_StatusCode readTemperature(void *handle, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) { if(range) { value->hasStatus = UA_TRUE; value->status = UA_STATUSCODE_BADINDEXRANGEINVALID; return UA_STATUSCODE_GOOD; } UA_Double* currentTemperature = UA_Double_new(); if(!currentTemperature) return UA_STATUSCODE_BADOUTOFMEMORY; fseek(temperatureFile, 0, SEEK_SET); if(fscanf(temperatureFile, "%lf", currentTemperature) != 1){ UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "Can not parse temperature"); exit(1); } *currentTemperature /= 1000.0; value->value.type = &UA_TYPES[UA_TYPES_DOUBLE]; value->value.arrayLength = -1; value->value.data = currentTemperature; value->value.arrayDimensionsSize = -1; value->value.arrayDimensions = NULL; value->hasValue = UA_TRUE; return UA_STATUSCODE_GOOD; }
int main(void) { signal(SIGINT, stopHandler); signal(SIGTERM, stopHandler); UA_ServerConfig *config = UA_ServerConfig_new_minimal(4801, NULL); /* Details about the PubSubTransportLayer can be found inside the * tutorial_pubsub_connection */ config->pubsubTransportLayers = (UA_PubSubTransportLayer *) UA_malloc(sizeof(UA_PubSubTransportLayer)); if (!config->pubsubTransportLayers) { UA_ServerConfig_delete(config); return -1; } config->pubsubTransportLayers[0] = UA_PubSubTransportLayerUDPMP(); config->pubsubTransportLayersSize++; UA_Server *server = UA_Server_new(config); UA_PubSubConnectionConfig connectionConfig; memset(&connectionConfig, 0, sizeof(connectionConfig)); connectionConfig.name = UA_STRING("UDP-UADP Connection 1"); connectionConfig.transportProfileUri = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp"); connectionConfig.enabled = UA_TRUE; UA_NetworkAddressUrlDataType networkAddressUrl = {UA_STRING_NULL , UA_STRING("opc.udp://224.0.0.22:4840/")}; UA_Variant_setScalar(&connectionConfig.address, &networkAddressUrl, &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]); UA_NodeId connectionIdent; UA_StatusCode retval = UA_Server_addPubSubConnection(server, &connectionConfig, &connectionIdent); if(retval == UA_STATUSCODE_GOOD) UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "The PubSub Connection was created successfully!"); /* The following lines register the listening on the configured multicast * address and configure a repeated job, which is used to handle received * messages. */ UA_PubSubConnection *connection = UA_PubSubConnection_findConnectionbyId(server, connectionIdent); if(connection != NULL) { UA_StatusCode rv = connection->channel->regist(connection->channel, NULL); if (rv == UA_STATUSCODE_GOOD) { UA_UInt64 subscriptionCallbackId; UA_Server_addRepeatedCallback(server, (UA_ServerCallback)subscriptionPollingCallback, connection, 100, &subscriptionCallbackId); } else { UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "register channel failed: %s!", UA_StatusCode_name(rv)); } } retval |= UA_Server_run(server, &running); UA_Server_delete(server); UA_ServerConfig_delete(config); return (int)retval; }
/* we have no networklayer. instead, attach the reusable buffer to the handle */ UA_Connection UA_ClientConnectionTCP(UA_ConnectionConfig localConf, const char *endpointUrl, UA_Logger logger) { UA_Connection connection; UA_Connection_init(&connection); connection.localConf = localConf; //socket_set_nonblocking(connection.sockfd); connection.send = socket_write; connection.recv = socket_recv; connection.close = ClientNetworkLayerClose; connection.getSendBuffer = ClientNetworkLayerGetBuffer; connection.releaseSendBuffer = ClientNetworkLayerReleaseBuffer; connection.releaseRecvBuffer = ClientNetworkLayerReleaseBuffer; size_t urlLength = strlen(endpointUrl); if(urlLength < 11 || urlLength >= 512) { UA_LOG_WARNING((*logger), UA_LOGCATEGORY_NETWORK, "Server url size invalid"); return connection; } if(strncmp(endpointUrl, "opc.tcp://", 10) != 0) { UA_LOG_WARNING((*logger), UA_LOGCATEGORY_NETWORK, "Server url does not begin with opc.tcp://"); return connection; } UA_UInt16 portpos = 9; UA_UInt16 port; for(port = 0; portpos < urlLength-1; portpos++) { if(endpointUrl[portpos] == ':') { char *endPtr = NULL; unsigned long int tempulong = strtoul(&endpointUrl[portpos+1], &endPtr, 10); if (ERANGE != errno && tempulong < UINT16_MAX && endPtr != &endpointUrl[portpos+1]) port = (UA_UInt16)tempulong; break; } } if(port == 0) { UA_LOG_WARNING((*logger), UA_LOGCATEGORY_NETWORK, "Port invalid"); return connection; } char hostname[512]; for(int i=10; i < portpos; i++) hostname[i-10] = endpointUrl[i]; hostname[portpos-10] = 0; #ifdef _WIN32 WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(2, 2); WSAStartup(wVersionRequested, &wsaData); if((connection.sockfd = socket(PF_INET, SOCK_STREAM,0)) == (UA_Int32)INVALID_SOCKET) { #else if((connection.sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { #endif UA_LOG_WARNING((*logger), UA_LOGCATEGORY_NETWORK, "Could not create socket"); return connection; } struct hostent *server = gethostbyname(hostname); if(!server) { UA_LOG_WARNING((*logger), UA_LOGCATEGORY_NETWORK, "DNS lookup of %s failed", hostname); return connection; } struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); memcpy((char *)&server_addr.sin_addr.s_addr, (char *)server->h_addr_list[0], server->h_length); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); connection.state = UA_CONNECTION_OPENING; if(connect(connection.sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { ClientNetworkLayerClose(&connection); UA_LOG_WARNING((*logger), UA_LOGCATEGORY_NETWORK, "Connection failed"); return connection; } #ifdef SO_NOSIGPIPE int val = 1; if(setsockopt(connection.sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof(val)) < 0) { UA_LOG_WARNING((*logger), UA_LOGCATEGORY_NETWORK, "Couldn't set SO_NOSIGPIPE"); return connection; } #endif return connection; }
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; }
/* we have no networklayer. instead, attach the reusable buffer to the handle */ UA_Connection ClientNetworkLayerTCP_connect(UA_ConnectionConfig localConf, char *endpointUrl, UA_Logger *logger) { UA_Connection connection; UA_Connection_init(&connection); connection.localConf = localConf; #ifndef UA_MULTITHREADING connection.handle = UA_ByteString_new(); UA_ByteString_newMembers(connection.handle, localConf.maxMessageSize); #endif size_t urlLength = strlen(endpointUrl); if(urlLength < 11 || urlLength >= 512) { UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Server url size invalid"); return connection; } if(strncmp(endpointUrl, "opc.tcp://", 10) != 0) { UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Server url does not begin with opc.tcp://"); return connection; } UA_UInt16 portpos = 9; UA_UInt16 port = 0; for(;portpos < urlLength-1; portpos++) { if(endpointUrl[portpos] == ':') { port = atoi(&endpointUrl[portpos+1]); break; } } if(port == 0) { UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Port invalid"); return connection; } char hostname[512]; for(int i=10; i < portpos; i++) hostname[i-10] = endpointUrl[i]; hostname[portpos-10] = 0; #ifdef _WIN32 WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(2, 2); WSAStartup(wVersionRequested, &wsaData); if((connection.sockfd = socket(PF_INET, SOCK_STREAM,0)) == (UA_Int32)INVALID_SOCKET) { #else if((connection.sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { #endif UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Could not create socket"); return connection; } struct hostent *server = gethostbyname(hostname); if(server == NULL) { UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "DNS lookup of %s failed", hostname); return connection; } struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); memcpy((char *)&server_addr.sin_addr.s_addr, (char *)server->h_addr_list[0], server->h_length); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); connection.state = UA_CONNECTION_OPENING; if(connect(connection.sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { ClientNetworkLayerClose(&connection); UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Connection failed"); return connection; } #ifdef SO_NOSIGPIPE int val = 1; if (setsockopt(connection.sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof(val)) < 0) { UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Couldn't set SO_NOSIGPIPE"); } #endif //socket_set_nonblocking(connection.sockfd); connection.write = socket_write; connection.recv = socket_recv; connection.close = ClientNetworkLayerClose; connection.getBuffer = ClientNetworkLayerGetBuffer; connection.releaseBuffer = ClientNetworkLayerReleaseBuffer; return connection; }
int main(int argc, char **argv) { signal(SIGINT, stopHandler); /* catches ctrl-c */ signal(SIGTERM, stopHandler); UA_ServerConfig *config; #ifdef UA_ENABLE_ENCRYPTION if(argc < 3) { UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Missing arguments for encryption support. " "Arguments are <server-certificate.der> " "<private-key.der> [<trustlist1.crl>, ...]"); config = UA_ServerConfig_new_minimal(4840, NULL); } else { /* Load certificate and private key */ UA_ByteString certificate = loadFile(argv[1]); if(certificate.length == 0) { UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unable to load file %s.", argv[1]); return EXIT_FAILURE; } UA_ByteString privateKey = loadFile(argv[2]); if(privateKey.length == 0) { UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Unable to load file %s.", argv[2]); return EXIT_FAILURE; } /* Load the trustlist */ size_t trustListSize = 0; if(argc > 3) trustListSize = (size_t)argc-3; UA_STACKARRAY(UA_ByteString, trustList, trustListSize); for(size_t i = 0; i < trustListSize; i++) trustList[i] = loadFile(argv[i+3]); /* Loading of a revocation list currently unsupported */ UA_ByteString *revocationList = NULL; size_t revocationListSize = 0; config = UA_ServerConfig_new_allSecurityPolicies(4840, &certificate, &privateKey, trustList, trustListSize, revocationList, revocationListSize); UA_ByteString_clear(&certificate); UA_ByteString_clear(&privateKey); for(size_t i = 0; i < trustListSize; i++) UA_ByteString_clear(&trustList[i]); } #else UA_ByteString certificate = UA_BYTESTRING_NULL; if(argc < 2) { UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Missing argument for the server certificate"); } else { certificate = loadFile(argv[1]); } config = UA_ServerConfig_new_minimal(4840, &certificate); UA_ByteString_clear(&certificate); #endif if(!config) { UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Could not create the server config"); return EXIT_FAILURE; } /* Override with a custom access control policy */ config->accessControl.getUserAccessLevel = getUserAccessLevel_disallowSpecific; /* uncomment next line to add a custom hostname */ // UA_ServerConfig_set_customHostname(config, UA_STRING("custom")); UA_Server *server = UA_Server_new(config); if(server == NULL) return EXIT_FAILURE; setInformationModel(server); /* run server */ UA_StatusCode retval = UA_Server_run(server, &running); UA_Server_delete(server); UA_ServerConfig_delete(config); return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE; }
static UA_StatusCode register_server_with_discovery_server(UA_Server *server, const char* discoveryServerUrl, const UA_Boolean isUnregister, const char* semaphoreFilePath) { if(!discoveryServerUrl) { UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER, "No discovery server url provided"); return UA_STATUSCODE_BADINTERNALERROR; } /* Create the client */ UA_Client *client = UA_Client_new(UA_ClientConfig_default); if(!client) return UA_STATUSCODE_BADOUTOFMEMORY; /* Connect the client */ UA_StatusCode retval = UA_Client_connect(client, discoveryServerUrl); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_CLIENT, "Connecting to the discovery server failed with statuscode %s", UA_StatusCode_name(retval)); UA_Client_delete(client); return retval; } /* Prepare the request. Do not cleanup the request after the service call, * as the members are stack-allocated or point into the server config. */ UA_RegisterServer2Request request; UA_RegisterServer2Request_init(&request); request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; request.server.isOnline = !isUnregister; request.server.serverUri = server->config.applicationDescription.applicationUri; request.server.productUri = server->config.applicationDescription.productUri; request.server.serverType = server->config.applicationDescription.applicationType; request.server.gatewayServerUri = server->config.applicationDescription.gatewayServerUri; if(semaphoreFilePath) { #ifdef UA_ENABLE_DISCOVERY_SEMAPHORE request.server.semaphoreFilePath = UA_STRING((char*)(uintptr_t)semaphoreFilePath); /* dirty cast */ #else UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_CLIENT, "Ignoring semaphore file path. open62541 not compiled " "with UA_ENABLE_DISCOVERY_SEMAPHORE=ON"); #endif } request.server.serverNames = &server->config.applicationDescription.applicationName; request.server.serverNamesSize = 1; /* Copy the discovery urls from the server config and the network layers*/ size_t config_discurls = server->config.applicationDescription.discoveryUrlsSize; size_t nl_discurls = server->config.networkLayersSize; size_t total_discurls = config_discurls * nl_discurls; request.server.discoveryUrls = (UA_String*)UA_alloca(sizeof(UA_String) * total_discurls); request.server.discoveryUrlsSize = config_discurls + nl_discurls; for(size_t i = 0; i < config_discurls; ++i) request.server.discoveryUrls[i] = server->config.applicationDescription.discoveryUrls[i]; /* TODO: Add nl only if discoveryUrl not already present */ for(size_t i = 0; i < nl_discurls; ++i) { UA_ServerNetworkLayer *nl = &server->config.networkLayers[i]; request.server.discoveryUrls[config_discurls + i] = nl->discoveryUrl; } UA_MdnsDiscoveryConfiguration mdnsConfig; UA_MdnsDiscoveryConfiguration_init(&mdnsConfig); request.discoveryConfigurationSize = 1; request.discoveryConfiguration = UA_ExtensionObject_new(); UA_ExtensionObject_init(&request.discoveryConfiguration[0]); request.discoveryConfiguration[0].encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; request.discoveryConfiguration[0].content.decoded.type = &UA_TYPES[UA_TYPES_MDNSDISCOVERYCONFIGURATION]; request.discoveryConfiguration[0].content.decoded.data = &mdnsConfig; mdnsConfig.mdnsServerName = server->config.mdnsServerName; mdnsConfig.serverCapabilities = server->config.serverCapabilities; mdnsConfig.serverCapabilitiesSize = server->config.serverCapabilitiesSize; // First try with RegisterServer2, if that isn't implemented, use RegisterServer UA_RegisterServer2Response response; UA_RegisterServer2Response_init(&response); __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_REGISTERSERVER2REQUEST], &response, &UA_TYPES[UA_TYPES_REGISTERSERVER2RESPONSE]); UA_StatusCode serviceResult = response.responseHeader.serviceResult; UA_RegisterServer2Response_deleteMembers(&response); UA_ExtensionObject_delete(request.discoveryConfiguration); if(serviceResult == UA_STATUSCODE_BADNOTIMPLEMENTED || serviceResult == UA_STATUSCODE_BADSERVICEUNSUPPORTED) { /* Try RegisterServer */ UA_RegisterServerRequest request_fallback; UA_RegisterServerRequest_init(&request_fallback); /* Copy from RegisterServer2 request */ request_fallback.requestHeader = request.requestHeader; request_fallback.server = request.server; UA_RegisterServerResponse response_fallback; UA_RegisterServerResponse_init(&response_fallback); __UA_Client_Service(client, &request_fallback, &UA_TYPES[UA_TYPES_REGISTERSERVERREQUEST], &response_fallback, &UA_TYPES[UA_TYPES_REGISTERSERVERRESPONSE]); serviceResult = response_fallback.responseHeader.serviceResult; UA_RegisterServerResponse_deleteMembers(&response_fallback); } if(serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_CLIENT, "RegisterServer/RegisterServer2 failed with statuscode %s", UA_StatusCode_name(serviceResult)); } UA_Client_disconnect(client); UA_Client_delete(client); return serviceResult; }
static UA_StatusCode register_server_with_discovery_server(UA_Server *server, UA_Client *client, const UA_Boolean isUnregister, const char* semaphoreFilePath) { /* Prepare the request. Do not cleanup the request after the service call, * as the members are stack-allocated or point into the server config. */ UA_RegisterServer2Request request; UA_RegisterServer2Request_init(&request); request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; request.server.isOnline = !isUnregister; request.server.serverUri = server->config.applicationDescription.applicationUri; request.server.productUri = server->config.applicationDescription.productUri; request.server.serverType = server->config.applicationDescription.applicationType; request.server.gatewayServerUri = server->config.applicationDescription.gatewayServerUri; if(semaphoreFilePath) { #ifdef UA_ENABLE_DISCOVERY_SEMAPHORE request.server.semaphoreFilePath = UA_STRING((char*)(uintptr_t)semaphoreFilePath); /* dirty cast */ #else UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_CLIENT, "Ignoring semaphore file path. open62541 not compiled " "with UA_ENABLE_DISCOVERY_SEMAPHORE=ON"); #endif } request.server.serverNames = &server->config.applicationDescription.applicationName; request.server.serverNamesSize = 1; /* Copy the discovery urls from the server config and the network layers*/ size_t config_discurls = server->config.applicationDescription.discoveryUrlsSize; size_t nl_discurls = server->config.networkLayersSize; size_t total_discurls = config_discurls + nl_discurls; UA_STACKARRAY(UA_String, urlsBuf, total_discurls); request.server.discoveryUrls = urlsBuf; request.server.discoveryUrlsSize = total_discurls; for(size_t i = 0; i < config_discurls; ++i) request.server.discoveryUrls[i] = server->config.applicationDescription.discoveryUrls[i]; /* TODO: Add nl only if discoveryUrl not already present */ for(size_t i = 0; i < nl_discurls; ++i) { UA_ServerNetworkLayer *nl = &server->config.networkLayers[i]; request.server.discoveryUrls[config_discurls + i] = nl->discoveryUrl; } #ifdef UA_ENABLE_DISCOVERY_MULTICAST request.discoveryConfigurationSize = 1; request.discoveryConfiguration = UA_ExtensionObject_new(); UA_ExtensionObject_init(&request.discoveryConfiguration[0]); // Set to NODELETE so that we can just use a pointer to the mdns config request.discoveryConfiguration[0].encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; request.discoveryConfiguration[0].content.decoded.type = &UA_TYPES[UA_TYPES_MDNSDISCOVERYCONFIGURATION]; request.discoveryConfiguration[0].content.decoded.data = &server->config.discovery.mdns; #endif // First try with RegisterServer2, if that isn't implemented, use RegisterServer UA_RegisterServer2Response response; __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_REGISTERSERVER2REQUEST], &response, &UA_TYPES[UA_TYPES_REGISTERSERVER2RESPONSE]); UA_StatusCode serviceResult = response.responseHeader.serviceResult; UA_RegisterServer2Response_deleteMembers(&response); UA_Array_delete(request.discoveryConfiguration, request.discoveryConfigurationSize, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]); request.discoveryConfiguration = NULL; request.discoveryConfigurationSize = 0; if(serviceResult == UA_STATUSCODE_BADNOTIMPLEMENTED || serviceResult == UA_STATUSCODE_BADSERVICEUNSUPPORTED) { /* Try RegisterServer */ UA_RegisterServerRequest request_fallback; UA_RegisterServerRequest_init(&request_fallback); /* Copy from RegisterServer2 request */ request_fallback.requestHeader = request.requestHeader; request_fallback.server = request.server; UA_RegisterServerResponse response_fallback; __UA_Client_Service(client, &request_fallback, &UA_TYPES[UA_TYPES_REGISTERSERVERREQUEST], &response_fallback, &UA_TYPES[UA_TYPES_REGISTERSERVERRESPONSE]); serviceResult = response_fallback.responseHeader.serviceResult; UA_RegisterServerResponse_deleteMembers(&response_fallback); } if(serviceResult != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_CLIENT, "RegisterServer/RegisterServer2 failed with statuscode %s", UA_StatusCode_name(serviceResult)); } return serviceResult; }
static UA_StatusCode ServerNetworkLayerUDP_start(ServerNetworkLayerUDP *layer, UA_Logger logger) { layer->layer.logger = logger; layer->serversockfd = socket(PF_INET, SOCK_DGRAM, 0); if(layer->serversockfd < 0) { UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_COMMUNICATION, "Error opening socket"); return UA_STATUSCODE_BADINTERNALERROR; } 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->layer.logger, UA_LOGCATEGORY_COMMUNICATION, "Could not setsockopt"); CLOSESOCKET(layer->serversockfd); return UA_STATUSCODE_BADINTERNALERROR; } if(bind(layer->serversockfd, (const struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_COMMUNICATION, "Could not bind the socket"); CLOSESOCKET(layer->serversockfd); return UA_STATUSCODE_BADINTERNALERROR; } socket_set_nonblocking(layer->serversockfd); UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_COMMUNICATION, "Listening for UDP connections on %s:%d", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port)); return UA_STATUSCODE_GOOD; } static size_t ServerNetworkLayerUDP_getJobs(ServerNetworkLayerUDP *layer, UA_Job **jobs, UA_UInt16 timeout) { UA_Job *items = NULL; setFDSet(layer); struct timeval tmptv = {0, timeout}; UA_Int32 resultsize = select(layer->serversockfd+1, &layer->fdset, NULL, NULL, &tmptv); if(resultsize <= 0 || !FD_ISSET(layer->serversockfd, &layer->fdset)) { *jobs = items; return 0; } items = malloc(sizeof(UA_Job)*resultsize); // read from established sockets UA_Int32 j = 0; UA_ByteString buf = {-1, NULL}; if(!buf.data) { buf.data = malloc(sizeof(UA_Byte) * layer->conf.recvBufferSize); if(!buf.data) UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_COMMUNICATION, "malloc failed"); } struct sockaddr sender; socklen_t sendsize = sizeof(sender); bzero(&sender, sizeof(sender)); buf.length = recvfrom(layer->serversockfd, buf.data, layer->conf.recvBufferSize, 0, &sender, &sendsize); if (buf.length <= 0) { } else { UDPConnection *c = malloc(sizeof(UDPConnection)); if(!c) return UA_STATUSCODE_BADINTERNALERROR; UA_Connection_init(&c->connection); c->from = sender; c->fromlen = sendsize; // c->sockfd = newsockfd; c->connection.getSendBuffer = GetMallocedBuffer; c->connection.releaseSendBuffer = ReleaseMallocedBuffer; c->connection.releaseRecvBuffer = ReleaseMallocedBuffer; c->connection.handle = layer; c->connection.send = sendUDP; c->connection.close = closeConnectionUDP; c->connection.localConf = layer->conf; c->connection.state = UA_CONNECTION_OPENING; items[j].type = UA_JOBTYPE_BINARYMESSAGE_NETWORKLAYER; items[j].job.binaryMessage.message = buf; items[j].job.binaryMessage.connection = (UA_Connection*)c; buf.data = NULL; j++; } if(buf.data) free(buf.data); if(j == 0) { free(items); *jobs = NULL; } else *jobs = items; return j; }