int main(int argc, char **argv) { signal(SIGINT, stopHandler); signal(SIGTERM, stopHandler); UA_Server *server = UA_Server_new(); UA_ServerConfig *config = UA_Server_getConfig(server); UA_ServerConfig_setDefault(config); config->customDataTypes = &customTypesArray; UA_StatusCode retval; /* create nodes from nodeset */ if(namespace_testnodeset_generated(server) != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Could not add the example nodeset. " "Check previous output for any error."); retval = UA_STATUSCODE_BADUNEXPECTEDERROR; } else { UA_Variant out; UA_Variant_init(&out); UA_Server_readValue(server, UA_NODEID_NUMERIC(2, 10002), &out); UA_Point *p = (UA_Point *)out.data; printf("point 2d x: %f y: %f \n", p->x, p->y); retval = UA_Server_run(server, &running); } UA_Server_delete(server); return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE; }
int main(void) { signal(SIGINT, stopHandler); /* catches ctrl-c */ UA_ClientConfig config = UA_ClientConfig_default; /* default timeout is 5 seconds. Set it to 1 second here for demo */ config.timeout = 1000; UA_Client *client = UA_Client_new(config); /* Read the value attribute of the node. UA_Client_readValueAttribute is a * wrapper for the raw read service available as UA_Client_Service_read. */ UA_Variant value; /* Variants can hold scalar values and arrays of any type */ UA_Variant_init(&value); /* Endless loop reading the server time */ while (running) { /* if already connected, this will return GOOD and do nothing */ /* if the connection is closed/errored, the connection will be reset and then reconnected */ /* Alternatively you can also use UA_Client_getState to get the current state */ UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840"); if (retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_CLIENT, "Not connected. Retrying to connect in 1 second"); /* The connect may timeout after 1 second (see above) or it may fail immediately on network errors */ /* E.g. name resolution errors or unreachable network. Thus there should be a small sleep here */ UA_sleep_ms(1000); continue; } /* NodeId of the variable holding the current time */ const UA_NodeId nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME); retval = UA_Client_readValueAttribute(client, nodeId, &value); if (retval == UA_STATUSCODE_BADCONNECTIONCLOSED) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_CLIENT, "Connection was closed. Reconnecting ..."); continue; } if (retval == UA_STATUSCODE_GOOD && UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME])) { UA_DateTime raw_date = *(UA_DateTime *) value.data; UA_String string_date = UA_DateTime_toString(raw_date); UA_LOG_INFO(logger, UA_LOGCATEGORY_CLIENT, "string date is: %.*s", (int) string_date.length, string_date.data); UA_String_deleteMembers(&string_date); } UA_sleep_ms(1000); }; /* Clean up */ UA_Variant_deleteMembers(&value); UA_Client_delete(client); /* Disconnects the client internally */ return UA_STATUSCODE_GOOD; }
static UA_StatusCode EndpointsHandshake(UA_Client *client) { UA_GetEndpointsRequest request; UA_GetEndpointsRequest_init(&request); request.requestHeader.authenticationToken = client->authenticationToken; request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; request.endpointUrl = client->endpointUrl; UA_String profileUri = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary"); request.profileUris = &profileUri; request.profileUrisSize = 1; UA_GetEndpointsResponse response; UA_GetEndpointsResponse_init(&response); __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST], &response, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]); UA_Boolean endpointFound = UA_FALSE; UA_Boolean tokenFound = UA_FALSE; UA_String securityNone = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None"); for(UA_Int32 i = 0; i < response.endpointsSize; i++) { UA_EndpointDescription* endpoint = &response.endpoints[i]; /* look out for an endpoint without security */ if(!UA_String_equal(&endpoint->securityPolicyUri, &securityNone)) continue; endpointFound = UA_TRUE; /* endpoint with no security found */ /* look for a user token policy with an anonymous token */ for(UA_Int32 j=0; j<endpoint->userIdentityTokensSize; ++j) { UA_UserTokenPolicy* userToken = &endpoint->userIdentityTokens[j]; if(userToken->tokenType != UA_USERTOKENTYPE_ANONYMOUS) continue; tokenFound = UA_TRUE; UA_UserTokenPolicy_copy(userToken, &client->token); break; } } UA_GetEndpointsResponse_deleteMembers(&response); if(!endpointFound) { UA_LOG_ERROR(client->logger, UA_LOGCATEGORY_CLIENT, "No suitable endpoint found"); return UA_STATUSCODE_BADINTERNALERROR; } if(!tokenFound) { UA_LOG_ERROR(client->logger, UA_LOGCATEGORY_CLIENT, "No anonymous token found"); return UA_STATUSCODE_BADINTERNALERROR; } return response.responseHeader.serviceResult; }
UA_StatusCode UA_Client_connect_iterate(UA_Client *client) { UA_LOG_TRACE(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Client connect iterate"); if (client->connection.state == UA_CONNECTION_ESTABLISHED){ if(client->state < UA_CLIENTSTATE_WAITING_FOR_ACK) { client->connectStatus = sendHELMessage(client); if(client->connectStatus == UA_STATUSCODE_GOOD) { setClientState(client, UA_CLIENTSTATE_WAITING_FOR_ACK); } else { client->connection.close(&client->connection); client->connection.free(&client->connection); } return client->connectStatus; } } /* If server is not connected */ if(client->connection.state == UA_CONNECTION_CLOSED) { client->connectStatus = UA_STATUSCODE_BADCONNECTIONCLOSED; UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_NETWORK, "No connection to server."); } if(client->connectStatus != UA_STATUSCODE_GOOD) { client->connection.close(&client->connection); client->connection.free(&client->connection); } return client->connectStatus; }
/* call only from the single networking thread */ static UA_StatusCode ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_Int32 newsockfd) { UA_Connection *c = malloc(sizeof(UA_Connection)); if(!c) return UA_STATUSCODE_BADINTERNALERROR; struct sockaddr_in addr; socklen_t addrlen = sizeof(struct sockaddr_in); getsockname(newsockfd, (struct sockaddr*)&addr, &addrlen); UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "New Connection %i over TCP from %s:%d", newsockfd, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); UA_Connection_init(c); c->sockfd = newsockfd; c->handle = layer; c->localConf = layer->conf; c->send = socket_write; c->close = ServerNetworkLayerTCP_closeConnection; c->getSendBuffer = ServerNetworkLayerGetSendBuffer; c->releaseSendBuffer = ServerNetworkLayerReleaseSendBuffer; c->releaseRecvBuffer = ServerNetworkLayerReleaseRecvBuffer; c->state = UA_CONNECTION_OPENING; struct ConnectionMapping *nm; nm = realloc(layer->mappings, sizeof(struct ConnectionMapping)*(layer->mappingsSize+1)); if(!nm) { UA_LOG_ERROR(layer->logger, UA_LOGCATEGORY_NETWORK, "No memory for a new Connection"); free(c); return UA_STATUSCODE_BADINTERNALERROR; } layer->mappings = nm; layer->mappings[layer->mappingsSize] = (struct ConnectionMapping){c, newsockfd}; layer->mappingsSize++; return UA_STATUSCODE_GOOD; }
/* * Get the endpoint from the server, where we can call RegisterServer2 (or RegisterServer). * This is normally the endpoint with highest supported encryption mode. * * @param discoveryServerUrl The discovery url from the remote server * @return The endpoint description (which needs to be freed) or NULL */ static UA_EndpointDescription *getRegisterEndpointFromServer(const char *discoveryServerUrl) { UA_Client *client = UA_Client_new(UA_ClientConfig_default); UA_EndpointDescription *endpointArray = NULL; size_t endpointArraySize = 0; UA_StatusCode retval = UA_Client_getEndpoints(client, discoveryServerUrl, &endpointArraySize, &endpointArray); if (retval != UA_STATUSCODE_GOOD) { UA_Array_delete(endpointArray, endpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, "GetEndpoints failed with %s", UA_StatusCode_name(retval)); UA_Client_delete(client); return NULL; } UA_LOG_DEBUG(logger, UA_LOGCATEGORY_SERVER, "Server has %ld endpoints", endpointArraySize); UA_EndpointDescription *foundEndpoint = NULL; for (size_t i = 0; i < endpointArraySize; i++) { UA_LOG_DEBUG(logger, UA_LOGCATEGORY_SERVER, "\tURL = %.*s, SecurityMode = %s", (int) endpointArray[i].endpointUrl.length, endpointArray[i].endpointUrl.data, endpointArray[i].securityMode == UA_MESSAGESECURITYMODE_NONE ? "None" : endpointArray[i].securityMode == UA_MESSAGESECURITYMODE_SIGN ? "Sign" : endpointArray[i].securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT ? "SignAndEncrypt" : "Invalid" ); // find the endpoint with highest supported security mode if ((UA_String_equal(&endpointArray[i].securityPolicyUri, &UA_SECURITY_POLICY_NONE_URI) || UA_String_equal(&endpointArray[i].securityPolicyUri, &UA_SECURITY_POLICY_BASIC128_URI)) && ( foundEndpoint == NULL || foundEndpoint->securityMode < endpointArray[i].securityMode)) foundEndpoint = &endpointArray[i]; } UA_EndpointDescription *returnEndpoint = NULL; if (foundEndpoint != NULL) { returnEndpoint = UA_EndpointDescription_new(); UA_EndpointDescription_copy(foundEndpoint, returnEndpoint); } UA_Array_delete(endpointArray, endpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); return returnEndpoint; }
/** * We need to decode the given binary message to get the name of the called service. * This method is used if the connection has no channel yet. */ static UA_StatusCode UA_debug_dump_setName_withoutChannel(UA_Server *server, UA_Connection *connection, UA_ByteString *message, struct UA_dump_filename* dump_filename) { size_t offset = 0; UA_TcpMessageHeader tcpMessageHeader; UA_StatusCode retval = UA_TcpMessageHeader_decodeBinary(message, &offset, &tcpMessageHeader); if(retval != UA_STATUSCODE_GOOD) return retval; dump_filename->messageType = UA_debug_dumpGetMessageTypePrefix(tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff); if ((tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff) == UA_MESSAGETYPE_MSG) { // this should not happen in normal operation UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Got MSG package without channel."); return UA_STATUSCODE_BADUNEXPECTEDERROR; } return UA_STATUSCODE_GOOD; }
static void responseActivateSession(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response) { UA_ActivateSessionResponse *activateResponse = (UA_ActivateSessionResponse *) response; if(activateResponse->responseHeader.serviceResult) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "ActivateSession failed with error code %s", UA_StatusCode_name(activateResponse->responseHeader.serviceResult)); } client->connection.state = UA_CONNECTION_ESTABLISHED; setClientState(client, UA_CLIENTSTATE_SESSION); #ifdef UA_ENABLE_SUBSCRIPTIONS /* A new session has been created. We need to clean up the subscriptions */ UA_Client_Subscriptions_clean(client); #endif /* Call onConnect (client_async.c) callback */ if(client->asyncConnectCall.callback) client->asyncConnectCall.callback(client, client->asyncConnectCall.userdata, requestId + 1, &activateResponse->responseHeader.serviceResult); }
/* OPN messges to renew the channel are sent asynchronous */ static UA_StatusCode openSecureChannelAsync(UA_Client *client/*, UA_Boolean renew*/) { /* Check if sc is still valid */ /*if(renew && client->nextChannelRenewal - UA_DateTime_nowMonotonic () > 0) return UA_STATUSCODE_GOOD;*/ UA_Connection *conn = &client->connection; if(conn->state != UA_CONNECTION_ESTABLISHED) return UA_STATUSCODE_BADSERVERNOTCONNECTED; /* Prepare the OpenSecureChannelRequest */ UA_OpenSecureChannelRequest opnSecRq; UA_OpenSecureChannelRequest_init(&opnSecRq); opnSecRq.requestHeader.timestamp = UA_DateTime_now(); opnSecRq.requestHeader.authenticationToken = client->authenticationToken; /*if(renew) { opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW; UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "Requesting to renew the SecureChannel"); } else {*/ opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE; UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "Requesting to open a SecureChannel"); //} opnSecRq.securityMode = client->channel.securityMode; opnSecRq.clientNonce = client->channel.localNonce; opnSecRq.requestedLifetime = client->config.secureChannelLifeTime; /* Prepare the entry for the linked list */ UA_UInt32 requestId = ++client->requestId; /*AsyncServiceCall *ac = NULL; if(renew) { ac = (AsyncServiceCall*)UA_malloc(sizeof(AsyncServiceCall)); if (!ac) return UA_STATUSCODE_BADOUTOFMEMORY; ac->callback = (UA_ClientAsyncServiceCallback) processDecodedOPNResponseAsync; ac->responseType = &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE]; ac->requestId = requestId; ac->userdata = NULL; }*/ /* Send the OPN message */ UA_StatusCode retval = UA_SecureChannel_sendAsymmetricOPNMessage ( &client->channel, requestId, &opnSecRq, &UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST]); client->connectStatus = retval; if(retval != UA_STATUSCODE_GOOD) { client->connectStatus = retval; UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "Sending OPN message failed with error %s", UA_StatusCode_name(retval)); UA_Client_disconnect(client); //if(renew) // UA_free(ac); return retval; } UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "OPN message sent"); /* Store the entry for async processing and return */ /*if(renew) { LIST_INSERT_HEAD(&client->asyncServiceCalls, ac, pointers); return retval; }*/ return retval; }
int main(int argc, char **argv) { signal(SIGINT, stopHandler); /* catches ctrl-c */ signal(SIGTERM, stopHandler); UA_ServerConfig *config = UA_ServerConfig_new_minimal(16600, NULL); // To enable mDNS discovery, set application type to discovery server. config->applicationDescription.applicationType = UA_APPLICATIONTYPE_DISCOVERYSERVER; UA_String_deleteMembers(&config->applicationDescription.applicationUri); config->applicationDescription.applicationUri = UA_String_fromChars("urn:open62541.example.server_multicast"); config->mdnsServerName = UA_String_fromChars("Sample Multicast Server"); // See http://www.opcfoundation.org/UA/schemas/1.03/ServerCapabilities.csv //config.serverCapabilitiesSize = 1; //UA_String caps = UA_String_fromChars("LDS"); //config.serverCapabilities = ∩︀ UA_Server *server = UA_Server_new(config); self_discovery_url = &config->networkLayers[0].discoveryUrl; /* add a variable node to the address space */ UA_Int32 myInteger = 42; UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer"); UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer"); UA_DataSource dateDataSource; dateDataSource.read = readInteger; dateDataSource.write = writeInteger; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.description = UA_LOCALIZEDTEXT("en-US", "the answer"); attr.displayName = UA_LOCALIZEDTEXT("en-US", "the answer"); UA_Server_addDataSourceVariableNode(server, myIntegerNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), myIntegerName, UA_NODEID_NULL, attr, dateDataSource, &myInteger, NULL); // callback which is called when a new server is detected through mDNS UA_Server_setServerOnNetworkCallback(server, serverOnNetworkCallback, NULL); // Start the server and call iterate to wait for the multicast discovery of the LDS UA_StatusCode retval = UA_Server_run_startup(server); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, "Could not start the server. StatusCode %s", UA_StatusCode_name(retval)); UA_Server_delete(server); UA_ServerConfig_delete(config); UA_free(discovery_url); return 1; } UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Server started. Waiting for announce of LDS Server."); while (running && discovery_url == NULL) UA_Server_run_iterate(server, true); if(!running) { UA_Server_delete(server); UA_ServerConfig_delete(config); UA_free(discovery_url); return 1; } UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "LDS-ME server found on %s", discovery_url); /* Check if the server supports sign and encrypt. OPC Foundation LDS requires an encrypted session for * RegisterServer call, our server currently uses encrpytion optionally */ UA_EndpointDescription *endpointRegister = getRegisterEndpointFromServer(discovery_url); UA_free(discovery_url); if (endpointRegister == NULL || endpointRegister->securityMode == UA_MESSAGESECURITYMODE_INVALID) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, "Could not find any suitable endpoints on discovery server"); UA_Server_delete(server); UA_ServerConfig_delete(config); return 1; } UA_Client *clientRegister = getRegisterClient(endpointRegister, argc, argv); if (!clientRegister) { UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Could not create the client for remote registering"); UA_Server_delete(server); UA_ServerConfig_delete(config); return 1; } /* Connect the client */ char *endpointUrl = (char*)UA_malloc(endpointRegister->endpointUrl.length + 1); memcpy(endpointUrl, endpointRegister->endpointUrl.data, endpointRegister->endpointUrl.length); endpointUrl[endpointRegister->endpointUrl.length] = 0; retval = UA_Server_addPeriodicServerRegisterCallback(server, clientRegister, endpointUrl, 10 * 60 * 1000, 500, NULL); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, "Could not create periodic job for server register. StatusCode %s", UA_StatusCode_name(retval)); UA_free(endpointUrl); UA_Client_disconnect(clientRegister); UA_Client_delete(clientRegister); UA_Server_delete(server); UA_ServerConfig_delete(config); return 1; } while (running) UA_Server_run_iterate(server, true); UA_Server_run_shutdown(server); // UNregister the server from the discovery server. retval = UA_Server_unregister_discovery(server, clientRegister); if (retval != UA_STATUSCODE_GOOD) UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, "Could not unregister server from discovery server. " "StatusCode %s", UA_StatusCode_name(retval)); UA_free(endpointUrl); UA_Client_disconnect(clientRegister); UA_Client_delete(clientRegister); UA_Server_delete(server); UA_ServerConfig_delete(config); return (int)retval; }
static UA_StatusCode subscriberListen(UA_PubSubChannel *psc) { UA_ByteString buffer; UA_StatusCode retval = UA_ByteString_allocBuffer(&buffer, 512); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Message buffer allocation failed!"); return retval; } /* Receive the message. Blocks for 100ms */ retval = psc->receive(psc, &buffer, NULL, 100); if(retval != UA_STATUSCODE_GOOD || buffer.length == 0) { /* Workaround!! Reset buffer length. Receive can set the length to zero. * Then the buffer is not deleted because no memory allocation is * assumed. * TODO: Return an error code in 'receive' instead of setting the buf * length to zero. */ buffer.length = 512; UA_ByteString_clear(&buffer); return UA_STATUSCODE_GOOD; } /* Decode the message */ UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Message length: %lu", (unsigned long) buffer.length); UA_NetworkMessage networkMessage; memset(&networkMessage, 0, sizeof(UA_NetworkMessage)); size_t currentPosition = 0; UA_NetworkMessage_decodeBinary(&buffer, ¤tPosition, &networkMessage); UA_ByteString_clear(&buffer); /* Is this the correct message type? */ if(networkMessage.networkMessageType != UA_NETWORKMESSAGE_DATASET) goto cleanup; /* At least one DataSetMessage in the NetworkMessage? */ if(networkMessage.payloadHeaderEnabled && networkMessage.payloadHeader.dataSetPayloadHeader.count < 1) goto cleanup; /* Is this a KeyFrame-DataSetMessage? */ for(size_t j = 0; j < networkMessage.payloadHeader.dataSetPayloadHeader.count; j++) { UA_DataSetMessage *dsm = &networkMessage.payload.dataSetPayload.dataSetMessages[j]; if(dsm->header.dataSetMessageType != UA_DATASETMESSAGE_DATAKEYFRAME) continue; /* Loop over the fields and print well-known content types */ for(int i = 0; i < dsm->data.keyFrameData.fieldCount; i++) { const UA_DataType *currentType = dsm->data.keyFrameData.dataSetFields[i].value.type; if(currentType == &UA_TYPES[UA_TYPES_BYTE]) { UA_Byte value = *(UA_Byte *)dsm->data.keyFrameData.dataSetFields[i].value.data; UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Message content: [Byte] \tReceived data: %i", value); } else if (currentType == &UA_TYPES[UA_TYPES_DATETIME]) { UA_DateTime value = *(UA_DateTime *)dsm->data.keyFrameData.dataSetFields[i].value.data; UA_DateTimeStruct receivedTime = UA_DateTime_toStruct(value); UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Message content: [DateTime] \t" "Received date: %02i-%02i-%02i Received time: %02i:%02i:%02i", receivedTime.year, receivedTime.month, receivedTime.day, receivedTime.hour, receivedTime.min, receivedTime.sec); } } } cleanup: UA_NetworkMessage_clear(&networkMessage); return retval; }
void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *requestType, void *response, const UA_DataType *responseType) { /* Requests always begin witih a RequestHeader, therefore we can cast. */ UA_RequestHeader *request = (void*)(uintptr_t)r; UA_StatusCode retval = UA_STATUSCODE_GOOD; UA_init(response, responseType); UA_ResponseHeader *respHeader = (UA_ResponseHeader*)response; /* make sure we have a valid session */ retval = UA_Client_manuallyRenewSecureChannel(client); if(retval != UA_STATUSCODE_GOOD) { respHeader->serviceResult = retval; client->state = UA_CLIENTSTATE_ERRORED; return; } /* handling request parameters */ UA_NodeId_copy(&client->authenticationToken, &request->authenticationToken); request->timestamp = UA_DateTime_now(); request->requestHandle = ++client->requestHandle; /* Send the request */ UA_UInt32 requestId = ++client->requestId; UA_LOG_DEBUG(client->logger, UA_LOGCATEGORY_CLIENT, "Sending a request of type %i", requestType->typeId.identifier.numeric); retval = UA_SecureChannel_sendBinaryMessage(&client->channel, requestId, request, requestType); if(retval) { if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED) respHeader->serviceResult = UA_STATUSCODE_BADREQUESTTOOLARGE; else respHeader->serviceResult = retval; client->state = UA_CLIENTSTATE_ERRORED; return; } /* Retrieve the response */ // Todo: push this into the generic securechannel implementation for client and server UA_ByteString reply; UA_ByteString_init(&reply); do { retval = client->connection.recv(&client->connection, &reply, client->config.timeout); if(retval != UA_STATUSCODE_GOOD) { respHeader->serviceResult = retval; client->state = UA_CLIENTSTATE_ERRORED; return; } } while(!reply.data); size_t offset = 0; UA_SecureConversationMessageHeader msgHeader; retval |= UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &msgHeader); UA_SymmetricAlgorithmSecurityHeader symHeader; retval |= UA_SymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &symHeader); UA_SequenceHeader seqHeader; retval |= UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader); UA_NodeId responseId; retval |= UA_NodeId_decodeBinary(&reply, &offset, &responseId); UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, responseType->typeId.identifier.numeric + UA_ENCODINGOFFSET_BINARY); if(retval != UA_STATUSCODE_GOOD) { goto finish; } /* Todo: we need to demux responses since a publish responses may come at any time */ if(!UA_NodeId_equal(&responseId, &expectedNodeId) || seqHeader.requestId != requestId) { if(responseId.identifier.numeric != UA_NS0ID_SERVICEFAULT + UA_ENCODINGOFFSET_BINARY) { UA_LOG_ERROR(client->logger, UA_LOGCATEGORY_CLIENT, "Reply answers the wrong request. Expected ns=%i,i=%i. But retrieved ns=%i,i=%i", expectedNodeId.namespaceIndex, expectedNodeId.identifier.numeric, responseId.namespaceIndex, responseId.identifier.numeric); respHeader->serviceResult = UA_STATUSCODE_BADINTERNALERROR; } else retval = UA_decodeBinary(&reply, &offset, respHeader, &UA_TYPES[UA_TYPES_SERVICEFAULT]); goto finish; } retval = UA_decodeBinary(&reply, &offset, response, responseType); if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED) retval = UA_STATUSCODE_BADRESPONSETOOLARGE; finish: UA_SymmetricAlgorithmSecurityHeader_deleteMembers(&symHeader); UA_ByteString_deleteMembers(&reply); if(retval != UA_STATUSCODE_GOOD){ UA_LOG_INFO(client->logger, UA_LOGCATEGORY_CLIENT, "Error receiving the response"); client->state = UA_CLIENTSTATE_ERRORED; respHeader->serviceResult = retval; } UA_LOG_DEBUG(client->logger, UA_LOGCATEGORY_CLIENT, "Received a response of type %i", responseId.identifier.numeric); }
int main(int argc, char **argv) { signal(SIGINT, stopHandler); /* catches ctrl-c */ signal(SIGTERM, stopHandler); UA_ServerConfig *config = UA_ServerConfig_new_default(); UA_String_deleteMembers(&config->applicationDescription.applicationUri); config->applicationDescription.applicationUri = UA_String_fromChars("urn:open62541.example.server_register"); config->mdnsServerName = UA_String_fromChars("Sample Server"); // See http://www.opcfoundation.org/UA/schemas/1.03/ServerCapabilities.csv //config.serverCapabilitiesSize = 1; //UA_String caps = UA_String_fromChars("LDS"); //config.serverCapabilities = ∩︀ UA_Server *server = UA_Server_new(config); /* add a variable node to the address space */ UA_Int32 myInteger = 42; UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer"); UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer"); UA_DataSource dateDataSource; dateDataSource.read = readInteger; dateDataSource.write = writeInteger; UA_VariableAttributes attr = UA_VariableAttributes_default; attr.description = UA_LOCALIZEDTEXT("en-US", "the answer"); attr.displayName = UA_LOCALIZEDTEXT("en-US", "the answer"); UA_Server_addDataSourceVariableNode(server, myIntegerNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), myIntegerName, UA_NODEID_NULL, attr, dateDataSource, &myInteger, NULL); // periodic server register after 10 Minutes, delay first register for 500ms UA_StatusCode retval = UA_Server_addPeriodicServerRegisterCallback(server, DISCOVERY_SERVER_ENDPOINT, 10 * 60 * 1000, 500, NULL); // UA_StatusCode retval = UA_Server_addPeriodicServerRegisterJob(server, // "opc.tcp://localhost:4840", 10*60*1000, 500, NULL); if (retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, "Could not create periodic job for server register. StatusCode %s", UA_StatusCode_name(retval)); UA_Server_delete(server); UA_ServerConfig_delete(config); return (int)retval; } retval = UA_Server_run(server, &running); if (retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, "Could not start the server. StatusCode %s", UA_StatusCode_name(retval)); UA_Server_delete(server); UA_ServerConfig_delete(config); return (int)retval; } // UNregister the server from the discovery server. retval = UA_Server_unregister_discovery(server, DISCOVERY_SERVER_ENDPOINT); //retval = UA_Server_unregister_discovery(server, "opc.tcp://localhost:4840" ); if(retval != UA_STATUSCODE_GOOD) UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, "Could not unregister server from discovery server. StatusCode %s", UA_StatusCode_name(retval)); UA_Server_delete(server); UA_ServerConfig_delete(config); return (int)retval; }
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; }
/* Combination of UA_Client_getEndpointsInternal and getEndpoints */ static void responseGetEndpoints(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response) { UA_EndpointDescription* endpointArray = NULL; size_t endpointArraySize = 0; UA_GetEndpointsResponse* resp; resp = (UA_GetEndpointsResponse*)response; if (resp->responseHeader.serviceResult != UA_STATUSCODE_GOOD) { client->connectStatus = resp->responseHeader.serviceResult; UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "GetEndpointRequest failed with error code %s", UA_StatusCode_name (client->connectStatus)); UA_GetEndpointsResponse_deleteMembers(resp); return; } endpointArray = resp->endpoints; endpointArraySize = resp->endpointsSize; resp->endpoints = NULL; resp->endpointsSize = 0; UA_Boolean endpointFound = false; UA_Boolean tokenFound = false; UA_String securityNone = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None"); UA_String binaryTransport = UA_STRING("http://opcfoundation.org/UA-Profile/" "Transport/uatcp-uasc-uabinary"); // TODO: compare endpoint information with client->endpointUri for(size_t i = 0; i < endpointArraySize; ++i) { UA_EndpointDescription* endpoint = &endpointArray[i]; /* look out for binary transport endpoints */ /* Note: Siemens returns empty ProfileUrl, we will accept it as binary */ if(endpoint->transportProfileUri.length != 0 && !UA_String_equal (&endpoint->transportProfileUri, &binaryTransport)) continue; /* Look for an endpoint corresponding to the client security policy */ if(!UA_String_equal(&endpoint->securityPolicyUri, &client->channel.securityPolicy->policyUri)) continue; endpointFound = true; /* Look for a user token policy with an anonymous token */ for(size_t j = 0; j < endpoint->userIdentityTokensSize; ++j) { UA_UserTokenPolicy* userToken = &endpoint->userIdentityTokens[j]; /* Usertokens also have a security policy... */ if(userToken->securityPolicyUri.length > 0 && !UA_String_equal(&userToken->securityPolicyUri, &securityNone)) continue; /* Does the token type match the client configuration? */ if((userToken->tokenType == UA_USERTOKENTYPE_ANONYMOUS && client->config.userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN] && client->config.userIdentityToken.content.decoded.type != NULL) || (userToken->tokenType == UA_USERTOKENTYPE_USERNAME && client->config.userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) || (userToken->tokenType == UA_USERTOKENTYPE_CERTIFICATE && client->config.userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]) || (userToken->tokenType == UA_USERTOKENTYPE_ISSUEDTOKEN && client->config.userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_ISSUEDIDENTITYTOKEN])) continue; /* Endpoint with matching usertokenpolicy found */ tokenFound = true; UA_EndpointDescription_deleteMembers(&client->config.endpoint); UA_EndpointDescription_copy(endpoint, &client->config.endpoint); UA_UserTokenPolicy_deleteMembers(&client->config.userTokenPolicy); UA_UserTokenPolicy_copy(userToken, &client->config.userTokenPolicy); break; } } UA_Array_delete(endpointArray, endpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); if(!endpointFound) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "No suitable endpoint found"); client->connectStatus = UA_STATUSCODE_BADINTERNALERROR; } else if(!tokenFound) { UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT, "No suitable UserTokenPolicy found for the possible endpoints"); client->connectStatus = UA_STATUSCODE_BADINTERNALERROR; } requestSession(client, &requestId); }
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; }
int main(void) { /* * Example for calling FindServersOnNetwork */ { UA_ServerOnNetwork *serverOnNetwork = NULL; size_t serverOnNetworkSize = 0; UA_Client *client = UA_Client_new(); UA_ClientConfig_setDefault(UA_Client_getConfig(client)); UA_StatusCode retval = UA_Client_findServersOnNetwork(client, DISCOVERY_SERVER_ENDPOINT, 0, 0, 0, NULL, &serverOnNetworkSize, &serverOnNetwork); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Could not call FindServersOnNetwork service. " "Is the discovery server started? StatusCode %s", UA_StatusCode_name(retval)); UA_Client_delete(client); return EXIT_FAILURE; } // output all the returned/registered servers for(size_t i = 0; i < serverOnNetworkSize; i++) { UA_ServerOnNetwork *server = &serverOnNetwork[i]; printf("Server[%lu]: %.*s", (unsigned long) i, (int) server->serverName.length, server->serverName.data); printf("\n\tRecordID: %d", server->recordId); printf("\n\tDiscovery URL: %.*s", (int) server->discoveryUrl.length, server->discoveryUrl.data); printf("\n\tCapabilities: "); for(size_t j = 0; j < server->serverCapabilitiesSize; j++) { printf("%.*s,", (int) server->serverCapabilities[j].length, server->serverCapabilities[j].data); } printf("\n\n"); } UA_Array_delete(serverOnNetwork, serverOnNetworkSize, &UA_TYPES[UA_TYPES_SERVERONNETWORK]); } /* Example for calling FindServers */ UA_ApplicationDescription *applicationDescriptionArray = NULL; size_t applicationDescriptionArraySize = 0; UA_StatusCode retval; { UA_Client *client = UA_Client_new(); UA_ClientConfig_setDefault(UA_Client_getConfig(client)); retval = UA_Client_findServers(client, DISCOVERY_SERVER_ENDPOINT, 0, NULL, 0, NULL, &applicationDescriptionArraySize, &applicationDescriptionArray); UA_Client_delete(client); } if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Could not call FindServers service. " "Is the discovery server started? StatusCode %s", UA_StatusCode_name(retval)); return EXIT_FAILURE; } // output all the returned/registered servers for(size_t i = 0; i < applicationDescriptionArraySize; i++) { UA_ApplicationDescription *description = &applicationDescriptionArray[i]; printf("Server[%lu]: %.*s", (unsigned long) i, (int) description->applicationUri.length, description->applicationUri.data); printf("\n\tName: %.*s", (int) description->applicationName.text.length, description->applicationName.text.data); printf("\n\tApplication URI: %.*s", (int) description->applicationUri.length, description->applicationUri.data); printf("\n\tProduct URI: %.*s", (int) description->productUri.length, description->productUri.data); printf("\n\tType: "); switch(description->applicationType) { case UA_APPLICATIONTYPE_SERVER: printf("Server"); break; case UA_APPLICATIONTYPE_CLIENT: printf("Client"); break; case UA_APPLICATIONTYPE_CLIENTANDSERVER: printf("Client and Server"); break; case UA_APPLICATIONTYPE_DISCOVERYSERVER: printf("Discovery Server"); break; default: printf("Unknown"); } printf("\n\tDiscovery URLs:"); for(size_t j = 0; j < description->discoveryUrlsSize; j++) { printf("\n\t\t[%lu]: %.*s", (unsigned long) j, (int) description->discoveryUrls[j].length, description->discoveryUrls[j].data); } printf("\n\n"); } /* * Now that we have the list of available servers, call get endpoints on all of them */ printf("-------- Server Endpoints --------\n"); for(size_t i = 0; i < applicationDescriptionArraySize; i++) { UA_ApplicationDescription *description = &applicationDescriptionArray[i]; if(description->discoveryUrlsSize == 0) { UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "[GetEndpoints] Server %.*s did not provide any discovery urls. Skipping.", (int)description->applicationUri.length, description->applicationUri.data); continue; } printf("\nEndpoints for Server[%lu]: %.*s\n", (unsigned long) i, (int) description->applicationUri.length, description->applicationUri.data); UA_Client *client = UA_Client_new(); UA_ClientConfig_setDefault(UA_Client_getConfig(client)); char *discoveryUrl = (char *) UA_malloc(sizeof(char) * description->discoveryUrls[0].length + 1); memcpy(discoveryUrl, description->discoveryUrls[0].data, description->discoveryUrls[0].length); discoveryUrl[description->discoveryUrls[0].length] = '\0'; UA_EndpointDescription *endpointArray = NULL; size_t endpointArraySize = 0; //TODO: adapt to the new async getEndpoint retval = UA_Client_getEndpoints(client, discoveryUrl, &endpointArraySize, &endpointArray); UA_free(discoveryUrl); if(retval != UA_STATUSCODE_GOOD) { UA_Client_disconnect(client); UA_Client_delete(client); break; } for(size_t j = 0; j < endpointArraySize; j++) { UA_EndpointDescription *endpoint = &endpointArray[j]; printf("\n\tEndpoint[%lu]:", (unsigned long) j); printf("\n\t\tEndpoint URL: %.*s", (int) endpoint->endpointUrl.length, endpoint->endpointUrl.data); printf("\n\t\tTransport profile URI: %.*s", (int) endpoint->transportProfileUri.length, endpoint->transportProfileUri.data); printf("\n\t\tSecurity Mode: "); switch(endpoint->securityMode) { case UA_MESSAGESECURITYMODE_INVALID: printf("Invalid"); break; case UA_MESSAGESECURITYMODE_NONE: printf("None"); break; case UA_MESSAGESECURITYMODE_SIGN: printf("Sign"); break; case UA_MESSAGESECURITYMODE_SIGNANDENCRYPT: printf("Sign and Encrypt"); break; default: printf("No valid security mode"); break; } printf("\n\t\tSecurity profile URI: %.*s", (int) endpoint->securityPolicyUri.length, endpoint->securityPolicyUri.data); printf("\n\t\tSecurity Level: %d", endpoint->securityLevel); } UA_Array_delete(endpointArray, endpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); UA_Client_delete(client); } printf("\n"); UA_Array_delete(applicationDescriptionArray, applicationDescriptionArraySize, &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]); return EXIT_SUCCESS; }