Merge branch 'master' into new_tcp

Conflicts:
	examples/networklayer_tcp.c
	src/client/ua_client.c
	src/ua_session.c
This commit is contained in:
Julius Pfrommer 2015-04-20 12:07:22 +02:00
commit ba36eba3bc
26 changed files with 490 additions and 385 deletions

View File

@ -130,6 +130,8 @@ if(EXTENSION_UDP)
add_definitions(-DEXTENSION_STATELESS) add_definitions(-DEXTENSION_STATELESS)
message(STATUS "Extensions: enabling udp") message(STATUS "Extensions: enabling udp")
add_definitions(-DEXTENSION_UDP) add_definitions(-DEXTENSION_UDP)
add_executable(exampleServerUDP $<TARGET_OBJECTS:open62541-object> examples/networklayer_udp.c examples/server_udp.c)
target_link_libraries(exampleServerUDP rt)
endif() endif()
option(EXTENSION_STATELESS "Enable stateless extension" OFF) option(EXTENSION_STATELESS "Enable stateless extension" OFF)

View File

@ -46,9 +46,9 @@ int main(int argc, char** argv) {
/* init the server */ /* init the server */
UA_Server *server = UA_Server_new(); UA_Server *server = UA_Server_new();
UA_Server_setLogger(server, Logger_Stdout_new());
UA_Server_addNetworkLayer(server, UA_Server_addNetworkLayer(server,
ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, PORT)); ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, PORT));
UA_Server_setLogger(server, Logger_Stdout_new());
UA_UInt16 nsIndex = UA_Server_addNamespace(server, "myApplicationNamespace"); UA_UInt16 nsIndex = UA_Server_addNamespace(server, "myApplicationNamespace");
/* add a variable node */ /* add a variable node */
@ -76,8 +76,7 @@ int main(int argc, char** argv) {
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
UA_Client *client = UA_Client_new(UA_ClientConfig_standard); UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
UA_ClientNetworkLayer nl = ClientNetworkLayerTCP_new(UA_ConnectionConfig_standard); UA_StatusCode retval = UA_Client_connect(client, ClientNetworkLayerTCP_connect,
UA_StatusCode retval = UA_Client_connect(client, UA_ConnectionConfig_standard, nl,
"opc.tcp://localhost:16664"); "opc.tcp://localhost:16664");
if(retval != UA_STATUSCODE_GOOD) { if(retval != UA_STATUSCODE_GOOD) {
UA_Client_delete(client); UA_Client_delete(client);

View File

@ -3,6 +3,7 @@
#include "ua_client.h" #include "ua_client.h"
#include "ua_nodeids.h" #include "ua_nodeids.h"
#include "networklayer_tcp.h" #include "networklayer_tcp.h"
#include "logger_stdout.h"
#else #else
#include "open62541.h" #include "open62541.h"
#endif #endif
@ -11,7 +12,7 @@
#include "networklayer_tcp.h" #include "networklayer_tcp.h"
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
UA_Client *client = UA_Client_new(UA_ClientConfig_standard); UA_Client *client = UA_Client_new(UA_ClientConfig_standard, Logger_Stdout_new());
UA_StatusCode retval = UA_Client_connect(client, ClientNetworkLayerTCP_connect, UA_StatusCode retval = UA_Client_connect(client, ClientNetworkLayerTCP_connect,
"opc.tcp://localhost:16664"); "opc.tcp://localhost:16664");
if(retval != UA_STATUSCODE_GOOD) { if(retval != UA_STATUSCODE_GOOD) {

View File

@ -119,6 +119,20 @@ static UA_StatusCode socket_set_nonblocking(UA_Int32 sockfd) {
return UA_STATUSCODE_GOOD; return UA_STATUSCODE_GOOD;
} }
/*****************************/
/* Generic Buffer Management */
/*****************************/
static UA_StatusCode GetMallocedBuffer(UA_Connection *connection, UA_ByteString *buf, size_t minSize) {
if(minSize > connection->remoteConf.recvBufferSize)
return UA_STATUSCODE_BADINTERNALERROR;
return UA_ByteString_newMembers(buf, minSize);
}
static void ReleaseMallocedBuffer(UA_Connection *connection, UA_ByteString *buf) {
UA_ByteString_deleteMembers(buf);
}
/***************************/ /***************************/
/* Server NetworkLayer TCP */ /* Server NetworkLayer TCP */
/***************************/ /***************************/
@ -157,7 +171,7 @@ static UA_StatusCode socket_set_nonblocking(UA_Int32 sockfd) {
typedef struct { typedef struct {
/* config */ /* config */
UA_Server *server; UA_Logger *logger;
UA_UInt32 port; UA_UInt32 port;
UA_String discoveryUrl; UA_String discoveryUrl;
UA_ConnectionConfig conf; /* todo: rename to localconf. */ UA_ConnectionConfig conf; /* todo: rename to localconf. */
@ -209,9 +223,7 @@ static void ServerNetworkLayerTCP_closeConnection(UA_Connection *connection) {
return; return;
connection->state = UA_CONNECTION_CLOSED; connection->state = UA_CONNECTION_CLOSED;
#endif #endif
socket_close(connection); socket_close(connection);
ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP*)connection->handle; ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP*)connection->handle;
struct DeleteList *d = malloc(sizeof(struct DeleteList)); struct DeleteList *d = malloc(sizeof(struct DeleteList));
d->connection = connection; d->connection = connection;
@ -232,65 +244,60 @@ static UA_StatusCode ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_
UA_Connection *c = malloc(sizeof(UA_Connection)); UA_Connection *c = malloc(sizeof(UA_Connection));
if(!c) if(!c)
return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_BADINTERNALERROR;
UA_Connection_init(c); UA_Connection_init(c);
c->sockfd = newsockfd; c->sockfd = newsockfd;
c->handle = layer; c->handle = layer;
c->localConf = layer->conf; c->localConf = layer->conf;
c->write = socket_write; c->write = socket_write;
c->close = ServerNetworkLayerTCP_closeConnection; c->close = ServerNetworkLayerTCP_closeConnection;
c->getBuffer = GetMallocedBuffer;
struct ConnectionMapping *nm = realloc(layer->mappings, c->releaseBuffer = ReleaseMallocedBuffer;
sizeof(struct ConnectionMapping)*(layer->mappingsSize+1)); struct ConnectionMapping *nm =
realloc(layer->mappings, sizeof(struct ConnectionMapping)*(layer->mappingsSize+1));
if(!nm) { if(!nm) {
free(c); free(c);
return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_BADINTERNALERROR;
} }
layer->mappings = nm; layer->mappings = nm;
layer->mappings[layer->mappingsSize] = (struct ConnectionMapping){c, newsockfd}; layer->mappings[layer->mappingsSize] = (struct ConnectionMapping){c, newsockfd};
layer->mappingsSize++; layer->mappingsSize++;
return UA_STATUSCODE_GOOD; return UA_STATUSCODE_GOOD;
} }
static UA_StatusCode ServerNetworkLayerTCP_start(ServerNetworkLayerTCP *layer) { static UA_StatusCode ServerNetworkLayerTCP_start(ServerNetworkLayerTCP *layer, UA_Logger *logger) {
UA_Logger *logger = UA_Server_getLogger(layer->server); layer->logger = logger;
#ifdef _WIN32 #ifdef _WIN32
if((layer->serversockfd = socket(PF_INET, SOCK_STREAM,0)) == (UA_Int32)INVALID_SOCKET) { if((layer->serversockfd = socket(PF_INET, SOCK_STREAM,0)) == (UA_Int32)INVALID_SOCKET) {
UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "Error opening socket, code: %d", UA_LOG_WARNING((*layer->logger), UA_LOGGERCATEGORY_COMMUNICATION, "Error opening socket, code: %d",
WSAGetLastError()); WSAGetLastError());
return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_BADINTERNALERROR;
} }
#else #else
if((layer->serversockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { if((layer->serversockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "Error opening socket"); UA_LOG_WARNING((*layer->logger), UA_LOGGERCATEGORY_COMMUNICATION, "Error opening socket");
return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_BADINTERNALERROR;
} }
#endif #endif
const struct sockaddr_in serv_addr =
const struct sockaddr_in serv_addr = { {.sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY,
.sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY, .sin_port = htons(layer->port), .sin_zero = {0}};
.sin_port = htons(layer->port), .sin_zero = {0}};
int optval = 1; int optval = 1;
if(setsockopt(layer->serversockfd, SOL_SOCKET, if(setsockopt(layer->serversockfd, SOL_SOCKET,
SO_REUSEADDR, (const char *)&optval, SO_REUSEADDR, (const char *)&optval,
sizeof(optval)) == -1) { sizeof(optval)) == -1) {
UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "Error during setting of socket options"); UA_LOG_WARNING((*layer->logger), UA_LOGGERCATEGORY_COMMUNICATION, "Error during setting of socket options");
CLOSESOCKET(layer->serversockfd); CLOSESOCKET(layer->serversockfd);
return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_BADINTERNALERROR;
} }
if(bind(layer->serversockfd, (const struct sockaddr *)&serv_addr, if(bind(layer->serversockfd, (const struct sockaddr *)&serv_addr,
sizeof(serv_addr)) < 0) { sizeof(serv_addr)) < 0) {
UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "Error during socket binding"); UA_LOG_WARNING((*layer->logger), UA_LOGGERCATEGORY_COMMUNICATION, "Error during socket binding");
CLOSESOCKET(layer->serversockfd); CLOSESOCKET(layer->serversockfd);
return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_BADINTERNALERROR;
} }
socket_set_nonblocking(layer->serversockfd); socket_set_nonblocking(layer->serversockfd);
listen(layer->serversockfd, MAXBACKLOG); listen(layer->serversockfd, MAXBACKLOG);
UA_LOG_INFO((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "Listening on %.*s", UA_LOG_INFO((*layer->logger), UA_LOGGERCATEGORY_COMMUNICATION, "Listening on %.*s",
layer->discoveryUrl.length, layer->discoveryUrl.data); layer->discoveryUrl.length, layer->discoveryUrl.data);
return UA_STATUSCODE_GOOD; return UA_STATUSCODE_GOOD;
} }
@ -335,7 +342,6 @@ static UA_Int32 ServerNetworkLayerTCP_getWork(ServerNetworkLayerTCP *layer, UA_W
setFDSet(layer); setFDSet(layer);
struct timeval tmptv = {0, timeout * 1000}; struct timeval tmptv = {0, timeout * 1000};
UA_Int32 resultsize = select(layer->highestfd+1, &layer->fdset, NULL, NULL, &tmptv); UA_Int32 resultsize = select(layer->highestfd+1, &layer->fdset, NULL, NULL, &tmptv);
UA_WorkItem *items; UA_WorkItem *items;
if(resultsize < 0 || !(items = malloc(sizeof(UA_WorkItem)*(resultsize+1)))) { if(resultsize < 0 || !(items = malloc(sizeof(UA_WorkItem)*(resultsize+1)))) {
/* abort .. reattach the deletes so that they get deleted eventually.. */ /* abort .. reattach the deletes so that they get deleted eventually.. */
@ -355,7 +361,7 @@ static UA_Int32 ServerNetworkLayerTCP_getWork(ServerNetworkLayerTCP *layer, UA_W
return 0; return 0;
} }
// accept new connections (can only be a single one) /* accept new connections (can only be a single one) */
if(FD_ISSET(layer->serversockfd, &layer->fdset)) { if(FD_ISSET(layer->serversockfd, &layer->fdset)) {
resultsize--; resultsize--;
struct sockaddr_in cli_addr; struct sockaddr_in cli_addr;
@ -375,14 +381,12 @@ static UA_Int32 ServerNetworkLayerTCP_getWork(ServerNetworkLayerTCP *layer, UA_W
for(size_t i = 0; i < layer->mappingsSize && j < resultsize; i++) { for(size_t i = 0; i < layer->mappingsSize && j < resultsize; i++) {
if(!(FD_ISSET(layer->mappings[i].sockfd, &layer->fdset))) if(!(FD_ISSET(layer->mappings[i].sockfd, &layer->fdset)))
continue; continue;
if(!buf.data) { if(!buf.data) {
buf.data = malloc(sizeof(UA_Byte) * layer->conf.recvBufferSize); buf.data = malloc(sizeof(UA_Byte) * layer->conf.recvBufferSize);
buf.length = layer->conf.recvBufferSize; buf.length = layer->conf.recvBufferSize;
if(!buf.data) if(!buf.data)
break; break;
} }
if(socket_recv(layer->mappings[i].connection, &buf, 0) == UA_STATUSCODE_GOOD) { if(socket_recv(layer->mappings[i].connection, &buf, 0) == UA_STATUSCODE_GOOD) {
items[j].type = UA_WORKITEMTYPE_BINARYNETWORKMESSAGE; items[j].type = UA_WORKITEMTYPE_BINARYNETWORKMESSAGE;
items[j].work.binaryNetworkMessage.message = buf; items[j].work.binaryNetworkMessage.message = buf;
@ -403,6 +407,7 @@ static UA_Int32 ServerNetworkLayerTCP_getWork(ServerNetworkLayerTCP *layer, UA_W
j++; j++;
} }
/* free the array if there is no work */
if(j == 0) { if(j == 0) {
free(items); free(items);
*workItems = NULL; *workItems = NULL;
@ -423,7 +428,6 @@ static UA_Int32 ServerNetworkLayerTCP_stop(ServerNetworkLayerTCP *layer, UA_Work
UA_WorkItem *items = malloc(sizeof(UA_WorkItem) * layer->mappingsSize); UA_WorkItem *items = malloc(sizeof(UA_WorkItem) * layer->mappingsSize);
if(!items) if(!items)
return 0; return 0;
for(size_t i = 0; i < layer->mappingsSize; i++) { for(size_t i = 0; i < layer->mappingsSize; i++) {
items[i].type = UA_WORKITEMTYPE_CLOSECONNECTION; items[i].type = UA_WORKITEMTYPE_CLOSECONNECTION;
items[i].work.closeConnection = layer->mappings[i].connection; items[i].work.closeConnection = layer->mappings[i].connection;
@ -445,7 +449,7 @@ static void ServerNetworkLayerTCP_delete(ServerNetworkLayerTCP *layer) {
free(layer); free(layer);
} }
UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_Server *server, UA_ConnectionConfig conf, UA_UInt32 port) { UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UInt32 port) {
#ifdef _WIN32 #ifdef _WIN32
WORD wVersionRequested; WORD wVersionRequested;
WSADATA wsaData; WSADATA wsaData;
@ -453,7 +457,6 @@ UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_Server *server, UA_Connection
WSAStartup(wVersionRequested, &wsaData); WSAStartup(wVersionRequested, &wsaData);
#endif #endif
ServerNetworkLayerTCP *layer = malloc(sizeof(ServerNetworkLayerTCP)); ServerNetworkLayerTCP *layer = malloc(sizeof(ServerNetworkLayerTCP));
layer->server = server;
layer->conf = conf; layer->conf = conf;
layer->mappingsSize = 0; layer->mappingsSize = 0;
layer->mappings = NULL; layer->mappings = NULL;
@ -477,16 +480,20 @@ UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_Server *server, UA_Connection
/* Client NetworkLayer TCP */ /* Client NetworkLayer TCP */
/***************************/ /***************************/
UA_StatusCode ClientNetworkLayerTCP_connect(UA_Client *client, UA_Connection *conn, char *endpointUrl) { UA_Connection ClientNetworkLayerTCP_connect(char *endpointUrl, UA_Logger *logger) {
UA_Connection connection;
UA_Connection_init(&connection);
size_t urlLength = strlen(endpointUrl); size_t urlLength = strlen(endpointUrl);
if(urlLength < 11 || urlLength >= 512) { if(urlLength < 11 || urlLength >= 512) {
printf("server url size invalid\n"); UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "Server url size invalid");
return UA_STATUSCODE_BADINTERNALERROR; return connection;
} }
if(strncmp(endpointUrl, "opc.tcp://", 10) != 0) { if(strncmp(endpointUrl, "opc.tcp://", 10) != 0) {
printf("server url does not begin with opc.tcp://\n"); UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "Server url does not begin with opc.tcp://");
return UA_STATUSCODE_BADINTERNALERROR; return connection;
} }
UA_UInt16 portpos = 9; UA_UInt16 portpos = 9;
UA_UInt16 port = 0; UA_UInt16 port = 0;
for(;portpos < urlLength-1; portpos++) { for(;portpos < urlLength-1; portpos++) {
@ -496,8 +503,8 @@ UA_StatusCode ClientNetworkLayerTCP_connect(UA_Client *client, UA_Connection *co
} }
} }
if(port == 0) { if(port == 0) {
printf("port invalid"); UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "Port invalid");
return UA_STATUSCODE_BADINTERNALERROR; return connection;
} }
char hostname[512]; char hostname[512];
@ -509,30 +516,33 @@ UA_StatusCode ClientNetworkLayerTCP_connect(UA_Client *client, UA_Connection *co
WSADATA wsaData; WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2); wVersionRequested = MAKEWORD(2, 2);
WSAStartup(wVersionRequested, &wsaData); WSAStartup(wVersionRequested, &wsaData);
if((conn->sockfd = socket(PF_INET, SOCK_STREAM,0)) == (UA_Int32)INVALID_SOCKET) { if((connection.sockfd = socket(PF_INET, SOCK_STREAM,0)) == (UA_Int32)INVALID_SOCKET) {
#else #else
if((conn->sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { if((connection.sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
#endif #endif
printf("Could not create socket\n"); UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "Could not create socket");
return UA_STATUSCODE_BADINTERNALERROR; return connection;
} }
struct hostent *server = gethostbyname(hostname); struct hostent *server = gethostbyname(hostname);
if (server == NULL) { if(server == NULL) {
printf("DNS lookup of %s failed\n", hostname); UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "DNS lookup of %s failed", hostname);
return UA_STATUSCODE_BADINTERNALERROR; return connection;
} }
struct sockaddr_in server_addr; struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(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); 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_family = AF_INET;
server_addr.sin_port = htons(port); server_addr.sin_port = htons(port);
if(connect(conn->sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { if(connect(connection.sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
printf("Connect failed.\n"); UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "Connection failed");
return UA_STATUSCODE_BADINTERNALERROR; return connection;
} }
socket_set_nonblocking(conn->sockfd); connection.state = UA_CONNECTION_OPENING;
conn->write = socket_write; socket_set_nonblocking(connection.sockfd);
conn->recv = socket_recv; connection.write = socket_write;
conn->close = socket_close; connection.recv = socket_recv;
return UA_STATUSCODE_GOOD; connection.close = socket_close;
connection.getBuffer = GetMallocedBuffer;
connection.releaseBuffer = ReleaseMallocedBuffer;
return connection;
} }

View File

@ -18,8 +18,8 @@ extern "C" {
#endif #endif
/** @brief Create the TCP networklayer and listen to the specified port */ /** @brief Create the TCP networklayer and listen to the specified port */
UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_Server *server, UA_ConnectionConfig conf, UA_UInt32 port); UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UInt32 port);
UA_StatusCode ClientNetworkLayerTCP_connect(UA_Client *client, UA_Connection *conn, char *endpointUrl); UA_Connection ClientNetworkLayerTCP_connect(char *endpointUrl, UA_Logger *logger);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"

View File

@ -16,15 +16,6 @@
/* with a space so amalgamation does not remove the includes */ /* with a space so amalgamation does not remove the includes */
# include <errno.h> // errno, EINTR # include <errno.h> // errno, EINTR
# include <fcntl.h> // fcntl # include <fcntl.h> // fcntl
#ifdef _WIN32
# include <malloc.h>
# include <winsock2.h>
# include <sys/types.h>
# include <windows.h>
# include <ws2tcpip.h>
# define CLOSESOCKET(S) closesocket(S)
#else
# include <strings.h> //bzero # include <strings.h> //bzero
# include <sys/select.h> # include <sys/select.h>
# include <netinet/in.h> # include <netinet/in.h>
@ -34,11 +25,30 @@
# include <unistd.h> // read, write, close # include <unistd.h> // read, write, close
# include <arpa/inet.h> # include <arpa/inet.h>
# define CLOSESOCKET(S) close(S) # define CLOSESOCKET(S) close(S)
#endif
#define MAXBACKLOG 100 #define MAXBACKLOG 100
struct ServerNetworklayerUDP; #ifdef _WIN32
# error fixme: udp not yet implemented for windows
#endif
/*****************************/
/* Generic Buffer Management */
/*****************************/
static UA_StatusCode GetMallocedBuffer(UA_Connection *connection, UA_ByteString *buf, size_t minSize) {
if(minSize > connection->remoteConf.recvBufferSize)
return UA_STATUSCODE_BADINTERNALERROR;
return UA_ByteString_newMembers(buf, minSize);
}
static void ReleaseMallocedBuffer(UA_Connection *connection, UA_ByteString *buf) {
UA_ByteString_deleteMembers(buf);
}
/*********************/
/* UDP Network Layer */
/*********************/
/* Forwarded to the server as a (UA_Connection) and used for callbacks back into /* Forwarded to the server as a (UA_Connection) and used for callbacks back into
the networklayer */ the networklayer */
@ -46,84 +56,28 @@ typedef struct {
UA_Connection connection; UA_Connection connection;
struct sockaddr from; struct sockaddr from;
socklen_t fromlen; socklen_t fromlen;
struct ServerNetworkLayerUDP *layer;
} UDPConnection; } UDPConnection;
typedef struct ServerNetworkLayerUDP { typedef struct {
UA_Server *server;
UA_ConnectionConfig conf; UA_ConnectionConfig conf;
fd_set fdset; fd_set fdset;
#ifdef _WIN32
UA_UInt32 serversockfd;
#else
UA_Int32 serversockfd; UA_Int32 serversockfd;
#endif
UA_UInt32 port; UA_UInt32 port;
UA_Logger *logger;
} ServerNetworkLayerUDP; } ServerNetworkLayerUDP;
static UA_StatusCode setNonBlocking(int sockid) {
#ifdef _WIN32
u_long iMode = 1;
if(ioctlsocket(sockid, FIONBIO, &iMode) != NO_ERROR)
return UA_STATUSCODE_BADINTERNALERROR;
#else
int opts = fcntl(sockid,F_GETFL);
if(opts < 0 || fcntl(sockid,F_SETFL,opts|O_NONBLOCK) < 0)
return UA_STATUSCODE_BADINTERNALERROR;
#endif
return UA_STATUSCODE_GOOD;
}
static void setFDSet(ServerNetworkLayerUDP *layer) {
FD_ZERO(&layer->fdset);
FD_SET(layer->serversockfd, &layer->fdset);
}
// the callbacks are thread-safe if UA_MULTITHREADING is defined
static void closeConnectionUDP(UDPConnection *handle) {
free(handle);
}
void writeCallbackUDP(UDPConnection *handle, UA_ByteStringArray gather_buf);
/** Accesses only the sockfd in the handle. Can be run from parallel threads. */ /** Accesses only the sockfd in the handle. Can be run from parallel threads. */
void writeCallbackUDP(UDPConnection *handle, UA_ByteStringArray gather_buf) { static void writeCallbackUDP(UDPConnection *handle, UA_ByteStringArray gather_buf) {
UA_UInt32 total_len = 0, nWritten = 0; UA_UInt32 total_len = 0, nWritten = 0;
#ifdef _WIN32
/*
LPWSABUF buf = _alloca(gather_buf.stringsSize * sizeof(WSABUF));
int result = 0;
for(UA_UInt32 i = 0; i<gather_buf.stringsSize; i++) {
buf[i].buf = (char*)gather_buf.strings[i].data;
buf[i].len = gather_buf.strings[i].length;
total_len += gather_buf.strings[i].length;
}
while(nWritten < total_len) {
UA_UInt32 n = 0;
do {
result = WSASendto(handle->layer->serversockfd, buf, gather_buf.stringsSize ,
(LPDWORD)&n, 0,
handle->from, handle->fromlen,
NULL, NULL);
//FIXME:
if(result != 0)
printf("Error WSASend, code: %d \n", WSAGetLastError());
} while(errno == EINTR);
nWritten += n;
}
*/
#error fixme: udp not yet implemented for windows
#else
struct iovec iov[gather_buf.stringsSize]; struct iovec iov[gather_buf.stringsSize];
for(UA_UInt32 i=0;i<gather_buf.stringsSize;i++) { for(UA_UInt32 i=0;i<gather_buf.stringsSize;i++) {
iov[i] = (struct iovec) {.iov_base = gather_buf.strings[i].data, iov[i] = (struct iovec) {.iov_base = gather_buf.strings[i].data,
.iov_len = gather_buf.strings[i].length}; .iov_len = gather_buf.strings[i].length};
total_len += gather_buf.strings[i].length; total_len += gather_buf.strings[i].length;
} }
struct sockaddr_in *sin = NULL; struct sockaddr_in *sin = NULL;
if (handle->from.sa_family == AF_INET) { if (handle->from.sa_family == AF_INET) {
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align" #pragma GCC diagnostic ignored "-Wcast-align"
@ -132,49 +86,50 @@ void writeCallbackUDP(UDPConnection *handle, UA_ByteStringArray gather_buf) {
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
} else { } else {
//FIXME: //FIXME:
return; return;
} }
struct msghdr message = {.msg_name = sin, .msg_namelen = handle->fromlen, .msg_iov = iov, struct msghdr message = {.msg_name = sin, .msg_namelen = handle->fromlen, .msg_iov = iov,
.msg_iovlen = gather_buf.stringsSize, .msg_control = NULL, .msg_iovlen = gather_buf.stringsSize, .msg_control = NULL,
.msg_controllen = 0, .msg_flags = 0}; .msg_controllen = 0, .msg_flags = 0};
while (nWritten < total_len) { while (nWritten < total_len) {
UA_Int32 n = 0; UA_Int32 n = 0;
do { do {
n = sendmsg(handle->layer->serversockfd, &message, 0); n = sendmsg(((ServerNetworkLayerUDP*)handle->connection.handle)->serversockfd, &message, 0);
if(n==-1L){ if(n == -1L) {
printf("ERROR:%i\n", errno); printf("ERROR:%i\n", errno);
} }
} while (n == -1L && errno == EINTR); } while (n == -1L && errno == EINTR);
nWritten += n; nWritten += n;
} }
#endif }
static UA_StatusCode socket_set_nonblocking(UA_Int32 sockfd) {
int opts = fcntl(sockfd, F_GETFL);
if(opts < 0 || fcntl(sockfd, F_SETFL, opts|O_NONBLOCK) < 0)
return UA_STATUSCODE_BADINTERNALERROR;
return UA_STATUSCODE_GOOD;
}
static void setFDSet(ServerNetworkLayerUDP *layer) {
FD_ZERO(&layer->fdset);
FD_SET(layer->serversockfd, &layer->fdset);
}
static void closeConnectionUDP(UDPConnection *handle) {
free(handle);
} }
static UA_StatusCode ServerNetworkLayerUDP_start(ServerNetworkLayerUDP *layer, UA_Logger *logger) { static UA_StatusCode ServerNetworkLayerUDP_start(ServerNetworkLayerUDP *layer, UA_Logger *logger) {
#ifdef _WIN32 layer->logger = logger;
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
WSAStartup(wVersionRequested, &wsaData);
if((layer->serversockfd = socket(PF_INET, SOCK_DGRAM,0)) == INVALID_SOCKET) {
printf("ERROR opening socket, code: %d\n", WSAGetLastError());
return UA_STATUSCODE_BADINTERNALERROR;
}
#else
if((layer->serversockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { if((layer->serversockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
perror("ERROR opening socket"); perror("ERROR opening socket");
return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_BADINTERNALERROR;
} }
#endif const struct sockaddr_in serv_addr =
{.sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY,
const struct sockaddr_in serv_addr = { .sin_port = htons(layer->port), .sin_zero = {0}};
.sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY,
.sin_port = htons(layer->port), .sin_zero = {0}};
int optval = 1; int optval = 1;
if(setsockopt(layer->serversockfd, SOL_SOCKET, if(setsockopt(layer->serversockfd, SOL_SOCKET,
SO_REUSEADDR, (const char *)&optval, SO_REUSEADDR, (const char *)&optval,
@ -183,84 +138,64 @@ static UA_StatusCode ServerNetworkLayerUDP_start(ServerNetworkLayerUDP *layer, U
CLOSESOCKET(layer->serversockfd); CLOSESOCKET(layer->serversockfd);
return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_BADINTERNALERROR;
} }
if(bind(layer->serversockfd, (const struct sockaddr *)&serv_addr, if(bind(layer->serversockfd, (const struct sockaddr *)&serv_addr,
sizeof(serv_addr)) < 0) { sizeof(serv_addr)) < 0) {
perror("binding"); perror("binding");
CLOSESOCKET(layer->serversockfd); CLOSESOCKET(layer->serversockfd);
return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_BADINTERNALERROR;
} }
socket_set_nonblocking(layer->serversockfd);
setNonBlocking(layer->serversockfd); printf("Listening for UDP connections on %s:%d", inet_ntoa(serv_addr.sin_addr),
UA_LOG_INFO((*logger), UA_LOGGERCATEGORY_SERVER, "Listening for UDP connections on %s:%d", ntohs(serv_addr.sin_port));
inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
return UA_STATUSCODE_GOOD; return UA_STATUSCODE_GOOD;
} }
static UA_Int32 ServerNetworkLayerUDP_getWork(ServerNetworkLayerUDP *layer, UA_WorkItem **workItems, static UA_Int32 ServerNetworkLayerUDP_getWork(ServerNetworkLayerUDP *layer, UA_WorkItem **workItems,
UA_UInt16 timeout) { UA_UInt16 timeout) {
UA_WorkItem *items = NULL; UA_WorkItem *items = NULL;
setFDSet(layer); setFDSet(layer);
struct timeval tmptv = {0, timeout}; struct timeval tmptv = {0, timeout};
UA_Int32 resultsize = select(layer->serversockfd+1, &layer->fdset, NULL, NULL, &tmptv); UA_Int32 resultsize = select(layer->serversockfd+1, &layer->fdset, NULL, NULL, &tmptv);
if(resultsize <= 0 || !FD_ISSET(layer->serversockfd, &layer->fdset)) { if(resultsize <= 0 || !FD_ISSET(layer->serversockfd, &layer->fdset)) {
*workItems = items; *workItems = items;
return 0; return 0;
} }
items = malloc(sizeof(UA_WorkItem)*(resultsize)); items = malloc(sizeof(UA_WorkItem)*(resultsize));
// read from established sockets // read from established sockets
UA_Int32 j = 0; UA_Int32 j = 0;
UA_ByteString buf = { -1, NULL}; UA_ByteString buf = {-1, NULL};
if(!buf.data) { if(!buf.data) {
buf.data = malloc(sizeof(UA_Byte) * layer->conf.recvBufferSize); buf.data = malloc(sizeof(UA_Byte) * layer->conf.recvBufferSize);
if(!buf.data){ if(!buf.data)
//TODO:
printf("malloc failed"); printf("malloc failed");
}
} }
struct sockaddr sender; struct sockaddr sender;
socklen_t sendsize = sizeof(sender); socklen_t sendsize = sizeof(sender);
bzero(&sender, sizeof(sender)); bzero(&sender, sizeof(sender));
#ifdef _WIN32
buf.length = recvfrom(layer->conLinks[i].sockfd, (char *)buf.data,
layer->conf.recvBufferSize, 0);
//todo: fixme
#else
buf.length = recvfrom(layer->serversockfd, buf.data, layer->conf.recvBufferSize, 0, &sender, &sendsize); buf.length = recvfrom(layer->serversockfd, buf.data, layer->conf.recvBufferSize, 0, &sender, &sendsize);
#endif
if (buf.length <= 0) { if (buf.length <= 0) {
} else { } else {
UDPConnection *c = malloc(sizeof(UDPConnection)); UDPConnection *c = malloc(sizeof(UDPConnection));
if(!c) if(!c)
return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_BADINTERNALERROR;
c->layer = layer;
c->from = sender; c->from = sender;
c->fromlen = sendsize; c->fromlen = sendsize;
c->connection.state = UA_CONNECTION_OPENING; c->connection.state = UA_CONNECTION_OPENING;
c->connection.localConf = layer->conf; c->connection.localConf = layer->conf;
c->connection.channel = NULL; c->connection.channel = NULL;
c->connection.close = (void (*)(void*))closeConnectionUDP; c->connection.close = (void (*)(UA_Connection*))closeConnectionUDP;
c->connection.write = (void (*)(void*, UA_ByteStringArray))writeCallbackUDP; c->connection.write = (UA_StatusCode (*)(UA_Connection*, UA_ByteStringArray))writeCallbackUDP;
c->connection.getBuffer = GetMallocedBuffer;
c->connection.releaseBuffer = ReleaseMallocedBuffer;
c->connection.handle = layer;
items[j].type = UA_WORKITEMTYPE_BINARYNETWORKMESSAGE; items[j].type = UA_WORKITEMTYPE_BINARYNETWORKMESSAGE;
items[j].work.binaryNetworkMessage.message = buf; items[j].work.binaryNetworkMessage.message = buf;
items[j].work.binaryNetworkMessage.connection = (UA_Connection*)c; items[j].work.binaryNetworkMessage.connection = (UA_Connection*)c;
buf.data = NULL; buf.data = NULL;
j++; j++;
} }
if(buf.data) if(buf.data)
free(buf.data); free(buf.data);
if(j == 0) { if(j == 0) {
free(items); free(items);
*workItems = NULL; *workItems = NULL;
@ -278,14 +213,14 @@ static void ServerNetworkLayerUDP_delete(ServerNetworkLayerUDP *layer) {
free(layer); free(layer);
} }
UA_ServerNetworkLayer ServerNetworkLayerUDP_new(UA_ConnectionConfig conf, UA_UInt32 port){ UA_ServerNetworkLayer ServerNetworkLayerUDP_new(UA_ConnectionConfig conf, UA_UInt32 port) {
ServerNetworkLayerUDP *udplayer = malloc(sizeof(ServerNetworkLayerUDP)); ServerNetworkLayerUDP *udplayer = malloc(sizeof(ServerNetworkLayerUDP));
udplayer->conf = conf; udplayer->conf = conf;
udplayer->port = port; udplayer->port = port;
UA_ServerNetworkLayer nl; UA_ServerNetworkLayer nl;
nl.nlHandle = udplayer; nl.nlHandle = udplayer;
nl.start = (UA_StatusCode (*)(void*))ServerNetworkLayerUDP_start; nl.start = (UA_StatusCode (*)(void*, UA_Logger *logger))ServerNetworkLayerUDP_start;
nl.getWork = (UA_Int32 (*)(void*, UA_WorkItem**, UA_UInt16)) ServerNetworkLayerUDP_getWork; nl.getWork = (UA_Int32 (*)(void*, UA_WorkItem**, UA_UInt16)) ServerNetworkLayerUDP_getWork;
nl.stop = (UA_Int32 (*)(void*, UA_WorkItem**)) ServerNetworkLayerUDP_stop; nl.stop = (UA_Int32 (*)(void*, UA_WorkItem**)) ServerNetworkLayerUDP_stop;
nl.free = (void (*)(void*))ServerNetworkLayerUDP_delete; nl.free = (void (*)(void*))ServerNetworkLayerUDP_delete;

View File

@ -196,7 +196,7 @@ int main(int argc, char** argv) {
UA_ByteString certificate = loadCertificate(); UA_ByteString certificate = loadCertificate();
UA_Server_setServerCertificate(server, certificate); UA_Server_setServerCertificate(server, certificate);
UA_ByteString_deleteMembers(&certificate); UA_ByteString_deleteMembers(&certificate);
UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(server, UA_ConnectionConfig_standard, 16664)); UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
UA_UInt16 nsIndex = UA_Server_addNamespace(server, "myApplicationNamespace"); UA_UInt16 nsIndex = UA_Server_addNamespace(server, "myApplicationNamespace");
// print the status every 2 sec // print the status every 2 sec

View File

@ -65,7 +65,7 @@ int main(int argc, char** argv) {
UA_ByteString certificate = loadCertificate(); UA_ByteString certificate = loadCertificate();
UA_Server_setServerCertificate(server, certificate); UA_Server_setServerCertificate(server, certificate);
UA_ByteString_deleteMembers(&certificate); UA_ByteString_deleteMembers(&certificate);
UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(server, UA_ConnectionConfig_standard, 16664)); UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
UA_Server_addNamespace(server, "myApplicationNamespace"); UA_Server_addNamespace(server, "myApplicationNamespace");
UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL, UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL,

View File

@ -34,13 +34,14 @@ int main(int argc, char** argv) {
UA_Variant *myIntegerVariant = UA_Variant_new(); UA_Variant *myIntegerVariant = UA_Variant_new();
UA_Int32 myInteger = 42; UA_Int32 myInteger = 42;
UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]); UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer"); const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
UA_NodeId myIntegerNodeId = UA_NODEID_NULL; /* assign a random free nodeid */ const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
UA_NodeId parentNodeId = UA_NODEID_STATIC(0, UA_NS0ID_OBJECTSFOLDER); UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_STATIC(0, UA_NS0ID_ORGANIZES); UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_Server_addVariableNode(server, myIntegerVariant, myIntegerName, UA_Server_addVariableNode(server, myIntegerVariant, myIntegerName,
myIntegerNodeId, parentNodeId, parentReferenceNodeId); myIntegerNodeId, parentNodeId, parentReferenceNodeId);
UA_StatusCode retval = UA_Server_run(server, 1, &running); UA_StatusCode retval = UA_Server_run(server, 1, &running);
UA_Server_delete(server); UA_Server_delete(server);

View File

@ -8,6 +8,7 @@ extern "C" {
#include "ua_util.h" #include "ua_util.h"
#include "ua_types.h" #include "ua_types.h"
#include "ua_connection.h" #include "ua_connection.h"
#include "ua_log.h"
#include "ua_types_generated.h" #include "ua_types_generated.h"
struct UA_Client; struct UA_Client;
@ -17,7 +18,7 @@ typedef struct UA_Client UA_Client;
* The client networklayer is defined by a single function that fills a UA_Connection struct after * The client networklayer is defined by a single function that fills a UA_Connection struct after
* successfully connecting. * successfully connecting.
*/ */
typedef UA_StatusCode (*UA_ConnectClientConnection)(UA_Client *client, UA_Connection *conn, char *endpointUrl); typedef UA_Connection (*UA_ConnectClientConnection)(char *endpointUrl, UA_Logger *logger);
typedef struct UA_ClientConfig { typedef struct UA_ClientConfig {
UA_Int32 timeout; //sync response timeout UA_Int32 timeout; //sync response timeout
@ -25,7 +26,7 @@ typedef struct UA_ClientConfig {
} UA_ClientConfig; } UA_ClientConfig;
extern const UA_ClientConfig UA_ClientConfig_standard; extern const UA_ClientConfig UA_ClientConfig_standard;
UA_Client UA_EXPORT * UA_Client_new(UA_ClientConfig config); UA_Client UA_EXPORT * UA_Client_new(UA_ClientConfig config, UA_Logger logger);
void UA_Client_delete(UA_Client* client); void UA_Client_delete(UA_Client* client);
@ -38,6 +39,7 @@ UA_WriteResponse UA_EXPORT UA_Client_write(UA_Client *client, UA_WriteRequest *r
/* View Service Set */ /* View Service Set */
UA_BrowseResponse UA_EXPORT UA_Client_browse(UA_Client *client, UA_BrowseRequest *request); UA_BrowseResponse UA_EXPORT UA_Client_browse(UA_Client *client, UA_BrowseRequest *request);
UA_BrowseNextResponse UA_EXPORT UA_Client_browseNext(UA_Client *client, UA_BrowseNextRequest *request);
UA_TranslateBrowsePathsToNodeIdsResponse UA_EXPORT UA_TranslateBrowsePathsToNodeIdsResponse UA_EXPORT
UA_Client_translateTranslateBrowsePathsToNodeIds(UA_Client *client, UA_Client_translateTranslateBrowsePathsToNodeIds(UA_Client *client,
UA_TranslateBrowsePathsToNodeIdsRequest *request); UA_TranslateBrowsePathsToNodeIdsRequest *request);

View File

@ -71,7 +71,7 @@ struct UA_Connection {
UA_Int32 sockfd; ///> Most connectivity solutions run on sockets. Having the socket id here simplifies the design. UA_Int32 sockfd; ///> Most connectivity solutions run on sockets. Having the socket id here simplifies the design.
void *handle; ///> A pointer to the networklayer void *handle; ///> A pointer to the networklayer
UA_ByteString incompleteMessage; ///> Half-received messages (tcp is a streaming protocol) get stored here UA_ByteString incompleteMessage; ///> Half-received messages (tcp is a streaming protocol) get stored here
UA_StatusCode (*getBuffer)(UA_Connection *connection, UA_ByteString *buf); ///> Attach a buffer according to localConf UA_StatusCode (*getBuffer)(UA_Connection *connection, UA_ByteString *buf, size_t minSize); ///> Attach the data array to the buffer. Fails if minSize is larger than remoteConf allows
void (*releaseBuffer)(UA_Connection *connection, UA_ByteString *buf); ///> Release the buffer void (*releaseBuffer)(UA_Connection *connection, UA_ByteString *buf); ///> Release the buffer
UA_StatusCode (*write)(UA_Connection *connection, UA_ByteStringArray buf); ///> The bytestrings cannot be reused after sending! UA_StatusCode (*write)(UA_Connection *connection, UA_ByteStringArray buf); ///> The bytestrings cannot be reused after sending!
UA_StatusCode (*recv)(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeout); // timeout in milliseconds UA_StatusCode (*recv)(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeout); // timeout in milliseconds

View File

@ -35,10 +35,11 @@ extern "C" {
typedef enum UA_LoggerCategory { typedef enum UA_LoggerCategory {
UA_LOGGERCATEGORY_COMMUNICATION, UA_LOGGERCATEGORY_COMMUNICATION,
UA_LOGGERCATEGORY_SERVER, UA_LOGGERCATEGORY_SERVER,
UA_LOGGERCATEGORY_CLIENT,
UA_LOGGERCATEGORY_USERLAND UA_LOGGERCATEGORY_USERLAND
} UA_LoggerCategory; } UA_LoggerCategory;
extern UA_EXPORT const char *UA_LoggerCategoryNames[3]; extern UA_EXPORT const char *UA_LoggerCategoryNames[4];
typedef struct UA_Logger { typedef struct UA_Logger {
void (*log_trace)(UA_LoggerCategory category, const char *msg, ...); void (*log_trace)(UA_LoggerCategory category, const char *msg, ...);
@ -50,45 +51,45 @@ typedef struct UA_Logger {
} UA_Logger; } UA_Logger;
#if UA_LOGLEVEL <= 100 #if UA_LOGLEVEL <= 100
#define UA_LOG_TRACE(LOGGER, CATEGORY, MSG...) do { \ #define UA_LOG_TRACE(LOGGER, CATEGORY, ...) do { \
if(LOGGER.log_trace) LOGGER.log_trace(CATEGORY, MSG); } while(0) if(LOGGER.log_trace) LOGGER.log_trace(CATEGORY, __VA_ARGS__); } while(0)
#else #else
#define UA_LOG_TRACE(LOGGER, CATEGORY, MSG...) do {} while(0) #define UA_LOG_TRACE(LOGGER, CATEGORY, ...) do {} while(0)
#endif #endif
#if UA_LOGLEVEL <= 200 #if UA_LOGLEVEL <= 200
#define UA_LOG_DEBUG(LOGGER, CATEGORY, MSG...) do { \ #define UA_LOG_DEBUG(LOGGER, CATEGORY, ...) do { \
if(LOGGER.log_debug) LOGGER.log_debug(CATEGORY, MSG); } while(0) if(LOGGER.log_debug) LOGGER.log_debug(CATEGORY, __VA_ARGS__); } while(0)
#else #else
#define UA_LOG_DEBUG(LOGGER, CATEGORY, MSG...) do {} while(0) #define UA_LOG_DEBUG(LOGGER, CATEGORY, ...) do {} while(0)
#endif #endif
#if UA_LOGLEVEL <= 300 #if UA_LOGLEVEL <= 300
#define UA_LOG_INFO(LOGGER, CATEGORY, MSG...) do { \ #define UA_LOG_INFO(LOGGER, CATEGORY, ...) do { \
if(LOGGER.log_info) LOGGER.log_info(CATEGORY, MSG); } while(0) if(LOGGER.log_info) LOGGER.log_info(CATEGORY, __VA_ARGS__); } while(0)
#else #else
#define UA_LOG_INFO(LOGGER, CATEGORY, MSG...) do {} while(0) #define UA_LOG_INFO(LOGGER, CATEGORY, ...) do {} while(0)
#endif #endif
#if UA_LOGLEVEL <= 400 #if UA_LOGLEVEL <= 400
#define UA_LOG_WARNING(LOGGER, CATEGORY, MSG...) do { \ #define UA_LOG_WARNING(LOGGER, CATEGORY, ...) do { \
if(LOGGER.log_warning) LOGGER.log_warning(CATEGORY, MSG); } while(0) if(LOGGER.log_warning) LOGGER.log_warning(CATEGORY, __VA_ARGS__); } while(0)
#else #else
#define UA_LOG_WARNING(LOGGER, CATEGORY, MSG...) do {} while(0) #define UA_LOG_WARNING(LOGGER, CATEGORY, ...) do {} while(0)
#endif #endif
#if UA_LOGLEVEL <= 500 #if UA_LOGLEVEL <= 500
#define UA_LOG_ERROR(LOGGER, CATEGORY, MSG...) do { \ #define UA_LOG_ERROR(LOGGER, CATEGORY, ...) do { \
if(LOGGER.log_error) LOGGER.log_error(CATEGORY, MSG); } while(0) if(LOGGER.log_error) LOGGER.log_error(CATEGORY, __VA_ARGS__); } while(0)
#else #else
#define UA_LOG_ERROR(LOGGER, CATEGORY, MSG...) do {} while(0) #define UA_LOG_ERROR(LOGGER, CATEGORY, ...) do {} while(0)
#endif #endif
#if UA_LOGLEVEL <= 600 #if UA_LOGLEVEL <= 600
#define UA_LOG_FATAL(LOGGER, CATEGORY, MSG...) do { \ #define UA_LOG_FATAL(LOGGER, CATEGORY, ...) do { \
if(LOGGER.log_fatal) LOGGER.log_fatal(CATEGORY, MSG); } while(0) if(LOGGER.log_fatal) LOGGER.log_fatal(CATEGORY, __VA_ARGS__); } while(0)
#else #else
#define UA_LOG_FATAL(LOGGER, CATEGORY, MSG...) do {} while(0) #define UA_LOG_FATAL(LOGGER, CATEGORY, ...) do {} while(0)
#endif #endif
/** @} */ /** @} */

View File

@ -272,6 +272,10 @@ typedef UA_Int32 (*UA_ExternalNodeStore_browseNodes)
UA_UInt32 *indices, UA_UInt32 indicesSize, UA_UInt32 requestedMaxReferencesPerNode, UA_UInt32 *indices, UA_UInt32 indicesSize, UA_UInt32 requestedMaxReferencesPerNode,
UA_BrowseResult *browseResults, UA_DiagnosticInfo *diagnosticInfos); UA_BrowseResult *browseResults, UA_DiagnosticInfo *diagnosticInfos);
typedef UA_Int32 (*UA_ExternalNodeStore_translateBrowsePathsToNodeIds)
(void *ensHandle, const UA_RequestHeader *requestHeader, UA_BrowsePath *browsePath,
UA_UInt32 *indices, UA_UInt32 indicesSize, UA_BrowsePathResult *browsePathResults, UA_DiagnosticInfo *diagnosticInfos);
typedef UA_Int32 (*UA_ExternalNodeStore_delete)(void *ensHandle); typedef UA_Int32 (*UA_ExternalNodeStore_delete)(void *ensHandle);
typedef struct UA_ExternalNodeStore { typedef struct UA_ExternalNodeStore {
@ -281,6 +285,7 @@ typedef struct UA_ExternalNodeStore {
UA_ExternalNodeStore_writeNodes writeNodes; UA_ExternalNodeStore_writeNodes writeNodes;
UA_ExternalNodeStore_readNodes readNodes; UA_ExternalNodeStore_readNodes readNodes;
UA_ExternalNodeStore_browseNodes browseNodes; UA_ExternalNodeStore_browseNodes browseNodes;
UA_ExternalNodeStore_translateBrowsePathsToNodeIds translateBrowsePathsToNodeIds;
UA_ExternalNodeStore_addReferences addReferences; UA_ExternalNodeStore_addReferences addReferences;
UA_ExternalNodeStore_deleteReferences deleteReferences; UA_ExternalNodeStore_deleteReferences deleteReferences;
UA_ExternalNodeStore_delete destroy; UA_ExternalNodeStore_delete destroy;

View File

@ -24,6 +24,7 @@ struct UA_Client {
UA_NodeId authenticationToken; UA_NodeId authenticationToken;
/* Config */ /* Config */
UA_Logger logger;
UA_ClientConfig config; UA_ClientConfig config;
}; };
@ -31,13 +32,13 @@ const UA_ClientConfig UA_ClientConfig_standard =
{ 5 /* ms receive timout */, {.protocolVersion = 0, .sendBufferSize = 65536, .recvBufferSize = 65536, { 5 /* ms receive timout */, {.protocolVersion = 0, .sendBufferSize = 65536, .recvBufferSize = 65536,
.maxMessageSize = 65536, .maxChunkCount = 1}}; .maxMessageSize = 65536, .maxChunkCount = 1}};
UA_Client * UA_Client_new(UA_ClientConfig config) { UA_Client * UA_Client_new(UA_ClientConfig config, UA_Logger logger) {
UA_Client *client = UA_malloc(sizeof(UA_Client)); UA_Client *client = UA_malloc(sizeof(UA_Client));
if(!client) if(!client)
return UA_NULL; return UA_NULL;
client->config = config; client->config = config;
client->logger = logger;
UA_String_init(&client->endpointUrl); UA_String_init(&client->endpointUrl);
client->connection.state = UA_CONNECTION_OPENING;
UA_Connection_init(&client->connection); UA_Connection_init(&client->connection);
client->sequenceNumber = 0; client->sequenceNumber = 0;
@ -411,19 +412,17 @@ static UA_StatusCode CloseSecureChannel(UA_Client *client) {
/* User-Facing Functions */ /* User-Facing Functions */
/*************************/ /*************************/
UA_StatusCode UA_Client_connect(UA_Client *client, UA_ConnectClientConnection connectFunc, char *endpointUrl) UA_StatusCode UA_Client_connect(UA_Client *client, UA_ConnectClientConnection connectFunc, char *endpointUrl) {
{ client->connection = connectFunc(endpointUrl, &client->logger);
UA_StatusCode retval = connectFunc(client, &client->connection, endpointUrl); if(client->connection.state != UA_CONNECTION_OPENING)
if(retval != UA_STATUSCODE_GOOD) { return UA_STATUSCODE_BADCONNECTIONCLOSED;
return retval;
}
client->endpointUrl = UA_STRING_ALLOC(endpointUrl); client->endpointUrl = UA_STRING_ALLOC(endpointUrl);
if(client->endpointUrl.length < 0) if(client->endpointUrl.length < 0)
return UA_STATUSCODE_BADOUTOFMEMORY; return UA_STATUSCODE_BADOUTOFMEMORY;
client->connection.localConf = client->config.localConnectionConfig; client->connection.localConf = client->config.localConnectionConfig;
retval = HelAckHandshake(client); UA_StatusCode retval = HelAckHandshake(client);
if(retval == UA_STATUSCODE_GOOD) if(retval == UA_STATUSCODE_GOOD)
retval = SecureChannelHandshake(client); retval = SecureChannelHandshake(client);
if(retval == UA_STATUSCODE_GOOD) if(retval == UA_STATUSCODE_GOOD)
@ -433,7 +432,7 @@ UA_StatusCode UA_Client_connect(UA_Client *client, UA_ConnectClientConnection co
return retval; return retval;
} }
UA_StatusCode UA_EXPORT UA_Client_disconnect(UA_Client *client) { UA_StatusCode UA_Client_disconnect(UA_Client *client) {
UA_StatusCode retval; UA_StatusCode retval;
retval = CloseSession(client); retval = CloseSession(client);
if(retval == UA_STATUSCODE_GOOD) if(retval == UA_STATUSCODE_GOOD)
@ -462,6 +461,13 @@ UA_BrowseResponse UA_Client_browse(UA_Client *client, UA_BrowseRequest *request)
return response; return response;
} }
UA_BrowseNextResponse UA_Client_browseNext(UA_Client *client, UA_BrowseNextRequest *request) {
UA_BrowseNextResponse response;
synchronousRequest(request, &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST], &response,
&UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE], client);
return response;
}
UA_TranslateBrowsePathsToNodeIdsResponse UA_TranslateBrowsePathsToNodeIdsResponse
UA_Client_translateTranslateBrowsePathsToNodeIds(UA_Client *client, UA_Client_translateTranslateBrowsePathsToNodeIds(UA_Client *client,
UA_TranslateBrowsePathsToNodeIdsRequest *request) { UA_TranslateBrowsePathsToNodeIdsRequest *request) {
@ -492,8 +498,7 @@ UA_DeleteNodesResponse UA_Client_deleteNodes(UA_Client *client, UA_DeleteNodesRe
return response; return response;
} }
UA_DeleteReferencesResponse UA_EXPORT UA_DeleteReferencesResponse UA_Client_deleteReferences(UA_Client *client, UA_DeleteReferencesRequest *request) {
UA_Client_deleteReferences(UA_Client *client, UA_DeleteReferencesRequest *request) {
UA_DeleteReferencesResponse response; UA_DeleteReferencesResponse response;
synchronousRequest(request, &UA_TYPES[UA_TYPES_BROWSEREQUEST], &response, synchronousRequest(request, &UA_TYPES[UA_TYPES_BROWSEREQUEST], &response,
&UA_TYPES[UA_TYPES_BROWSERESPONSE], client); &UA_TYPES[UA_TYPES_BROWSERESPONSE], client);

View File

@ -6,8 +6,6 @@
#include "ua_services.h" #include "ua_services.h"
#include "ua_nodeids.h" #include "ua_nodeids.h"
const char *UA_LoggerCategoryNames[3] = {"communication", "server", "userland"};
/**********************/ /**********************/
/* Namespace Handling */ /* Namespace Handling */
/**********************/ /**********************/

View File

@ -57,7 +57,31 @@ addOneWayReferenceWithSession(UA_Server *server, UA_Session *session, const UA_A
const UA_Node *node = UA_NodeStore_get(server->nodestore, &item->sourceNodeId); const UA_Node *node = UA_NodeStore_get(server->nodestore, &item->sourceNodeId);
if(!node) if(!node)
return UA_STATUSCODE_BADINTERNALERROR; return UA_STATUSCODE_BADINTERNALERROR;
UA_StatusCode retval = UA_STATUSCODE_GOOD;
#ifndef UA_MULTITHREADING
size_t i = node->referencesSize;
if(node->referencesSize < 0)
i = 0;
UA_ReferenceNode *new_refs = UA_realloc(node->references, sizeof(UA_ReferenceNode) * (i + 1));
if(!new_refs)
retval = UA_STATUSCODE_BADOUTOFMEMORY;
else {
UA_ReferenceNode_init(&new_refs[i]);
retval = UA_NodeId_copy(&item->referenceTypeId, &new_refs[i].referenceTypeId);
new_refs[i].isInverse = !item->isForward;
retval |= UA_ExpandedNodeId_copy(&item->targetNodeId, &new_refs[i].targetId);
/* hack. be careful! possible only in the single-threaded case. */
UA_Node *mutable_node = (UA_Node*)(uintptr_t)node;
mutable_node->references = new_refs;
if(retval != UA_STATUSCODE_GOOD) {
UA_NodeId_deleteMembers(&new_refs[node->referencesSize].referenceTypeId);
UA_ExpandedNodeId_deleteMembers(&new_refs[node->referencesSize].targetId);
} else
mutable_node->referencesSize = i+1;
}
UA_NodeStore_release(node);
return retval;
#else
UA_Node *newNode = UA_NULL; UA_Node *newNode = UA_NULL;
void (*deleteNode)(UA_Node*) = UA_NULL; void (*deleteNode)(UA_Node*) = UA_NULL;
switch(node->nodeClass) { switch(node->nodeClass) {
@ -119,7 +143,7 @@ addOneWayReferenceWithSession(UA_Server *server, UA_Session *session, const UA_A
// insert the new reference // insert the new reference
UA_memcpy(new_refs, old_refs, sizeof(UA_ReferenceNode)*count); UA_memcpy(new_refs, old_refs, sizeof(UA_ReferenceNode)*count);
UA_ReferenceNode_init(&new_refs[count]); UA_ReferenceNode_init(&new_refs[count]);
UA_StatusCode retval = UA_NodeId_copy(&item->referenceTypeId, &new_refs[count].referenceTypeId); retval = UA_NodeId_copy(&item->referenceTypeId, &new_refs[count].referenceTypeId);
new_refs[count].isInverse = !item->isForward; new_refs[count].isInverse = !item->isForward;
retval |= UA_ExpandedNodeId_copy(&item->targetNodeId, &new_refs[count].targetId); retval |= UA_ExpandedNodeId_copy(&item->targetNodeId, &new_refs[count].targetId);
if(retval != UA_STATUSCODE_GOOD) { if(retval != UA_STATUSCODE_GOOD) {
@ -135,13 +159,14 @@ addOneWayReferenceWithSession(UA_Server *server, UA_Session *session, const UA_A
newNode->references = new_refs; newNode->references = new_refs;
newNode->referencesSize = ++count; newNode->referencesSize = ++count;
retval = UA_NodeStore_replace(server->nodestore, node, newNode, UA_NULL); retval = UA_NodeStore_replace(server->nodestore, node, newNode, UA_NULL);
UA_NodeStore_release(node); UA_NodeStore_release(node);
if(retval != UA_STATUSCODE_BADINTERNALERROR) if (retval == UA_STATUSCODE_BADINTERNALERROR) {
return retval; /* presumably because the node was replaced and an old version was updated at the same time. just try again */
deleteNode(newNode);
// error presumably because the node was replaced and an old version was updated just try again return addOneWayReferenceWithSession(server, session, item);
deleteNode(newNode); }
return addOneWayReferenceWithSession(server, session, item); return retval;
#endif
} }
/* userland version of addReferenceWithSession */ /* userland version of addReferenceWithSession */

View File

@ -127,31 +127,26 @@ static void processOPN(UA_Connection *connection, UA_Server *server, const UA_By
static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r) { static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r) {
r->requestHandle = p->requestHandle; r->requestHandle = p->requestHandle;
r->serviceResult = UA_STATUSCODE_GOOD;
r->stringTableSize = 0; r->stringTableSize = 0;
r->timestamp = UA_DateTime_now(); r->timestamp = UA_DateTime_now();
} }
// if the message is small enough, we allocate it on the stack and save a malloc
#define ALLOC_MESSAGE(MESSAGE, SIZE) do { \
UA_UInt32 messageSize = SIZE; \
if(messageSize <= MAX_STACK_MESSAGE) { \
messageOnStack = UA_TRUE; \
*MESSAGE = (UA_ByteString){.length = messageSize, \
.data = UA_alloca(messageSize)}; \
} else \
UA_ByteString_newMembers(MESSAGE, messageSize); \
} while(0)
#define INVOKE_SERVICE(TYPE) do { \ #define INVOKE_SERVICE(TYPE) do { \
UA_##TYPE##Request p; \ UA_##TYPE##Request p; \
UA_##TYPE##Response r; \ UA_##TYPE##Response r; \
if(UA_##TYPE##Request_decodeBinary(msg, pos, &p)) \ if(UA_##TYPE##Request_decodeBinary(msg, pos, &p)) \
return; \ return; \
if(clientChannel->session && \
UA_NodeId_equal(&clientChannel->session->authenticationToken, \
&p.requestHeader.authenticationToken)) \
clientSession = clientChannel->session; \
UA_##TYPE##Response_init(&r); \ UA_##TYPE##Response_init(&r); \
init_response_header(&p.requestHeader, &r.responseHeader); \ init_response_header(&p.requestHeader, &r.responseHeader); \
Service_##TYPE(server, clientSession, &p, &r); \ if(!clientSession) \
ALLOC_MESSAGE(message, UA_##TYPE##Response_calcSizeBinary(&r)); \ r.responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID; \
else \
Service_##TYPE(server, clientSession, &p, &r); \
connection->getBuffer(connection, message, UA_##TYPE##Response_calcSizeBinary(&r)); \
UA_##TYPE##Response_encodeBinary(&r, message, &sendOffset); \ UA_##TYPE##Response_encodeBinary(&r, message, &sendOffset); \
UA_##TYPE##Request_deleteMembers(&p); \ UA_##TYPE##Request_deleteMembers(&p); \
UA_##TYPE##Response_deleteMembers(&r); \ UA_##TYPE##Response_deleteMembers(&r); \
@ -167,15 +162,11 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
UA_SecureChannel *clientChannel = connection->channel; UA_SecureChannel *clientChannel = connection->channel;
UA_SecureChannel anonymousChannel; UA_SecureChannel anonymousChannel;
#ifdef EXTENSION_STATELESS
if(!clientChannel) { if(!clientChannel) {
UA_SecureChannel_init(&anonymousChannel); UA_SecureChannel_init(&anonymousChannel);
clientChannel = &anonymousChannel; clientChannel = &anonymousChannel;
} }
UA_Session *clientSession = clientChannel->session;
#ifdef EXTENSION_STATELESS
if(secureChannelId == 0)
clientSession = &anonymousSession;
#endif #endif
// 2) Read the security header // 2) Read the security header
@ -191,6 +182,12 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
clientChannel->sequenceNumber = sequenceHeader.sequenceNumber; clientChannel->sequenceNumber = sequenceHeader.sequenceNumber;
clientChannel->requestId = sequenceHeader.requestId; clientChannel->requestId = sequenceHeader.requestId;
UA_Session *clientSession = UA_NULL;
#ifdef EXTENSION_STATELESS
if(clientChannel == &anonymousChannel)
clientSession = &anonymousSession;
#endif
// 3) Read the nodeid of the request // 3) Read the nodeid of the request
UA_NodeId requestType; UA_NodeId requestType;
if(UA_NodeId_decodeBinary(msg, pos, &requestType)) if(UA_NodeId_decodeBinary(msg, pos, &requestType))
@ -206,21 +203,8 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
UA_UInt32 responseType; UA_UInt32 responseType;
UA_ByteString *header = &responseBufs[0]; UA_ByteString *header = &responseBufs[0];
UA_ByteString *message = &responseBufs[1]; UA_ByteString *message = &responseBufs[1];
UA_Boolean messageOnStack = UA_FALSE;
size_t sendOffset = 0; size_t sendOffset = 0;
#ifdef EXTENSION_STATELESS
switch(requestType.identifier.numeric - UA_ENCODINGOFFSET_BINARY) {
case UA_NS0ID_READREQUEST:
case UA_NS0ID_WRITEREQUEST:
case UA_NS0ID_BROWSEREQUEST:
break;
default:
if(clientSession != &anonymousSession)
retval = UA_STATUSCODE_BADNOTCONNECTED;
}
#endif
//subtract UA_ENCODINGOFFSET_BINARY for binary encoding, if retval is set, this forces the default path //subtract UA_ENCODINGOFFSET_BINARY for binary encoding, if retval is set, this forces the default path
switch(requestType.identifier.numeric - UA_ENCODINGOFFSET_BINARY + retval) { switch(requestType.identifier.numeric - UA_ENCODINGOFFSET_BINARY + retval) {
case UA_NS0ID_GETENDPOINTSREQUEST: { case UA_NS0ID_GETENDPOINTSREQUEST: {
@ -231,7 +215,7 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
UA_GetEndpointsResponse_init(&r); UA_GetEndpointsResponse_init(&r);
init_response_header(&p.requestHeader, &r.responseHeader); init_response_header(&p.requestHeader, &r.responseHeader);
Service_GetEndpoints(server, &p, &r); Service_GetEndpoints(server, &p, &r);
ALLOC_MESSAGE(message, UA_GetEndpointsResponse_calcSizeBinary(&r)); connection->getBuffer(connection, message, UA_GetEndpointsResponse_calcSizeBinary(&r));
UA_GetEndpointsResponse_encodeBinary(&r, message, &sendOffset); UA_GetEndpointsResponse_encodeBinary(&r, message, &sendOffset);
UA_GetEndpointsRequest_deleteMembers(&p); UA_GetEndpointsRequest_deleteMembers(&p);
UA_GetEndpointsResponse_deleteMembers(&r); UA_GetEndpointsResponse_deleteMembers(&r);
@ -247,7 +231,7 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
UA_FindServersResponse_init(&r); UA_FindServersResponse_init(&r);
init_response_header(&p.requestHeader, &r.responseHeader); init_response_header(&p.requestHeader, &r.responseHeader);
Service_FindServers(server, &p, &r); Service_FindServers(server, &p, &r);
ALLOC_MESSAGE(message, UA_FindServersResponse_calcSizeBinary(&r)); connection->getBuffer(connection, message, UA_FindServersResponse_calcSizeBinary(&r));
UA_FindServersResponse_encodeBinary(&r, message, &sendOffset); UA_FindServersResponse_encodeBinary(&r, message, &sendOffset);
UA_FindServersRequest_deleteMembers(&p); UA_FindServersRequest_deleteMembers(&p);
UA_FindServersResponse_deleteMembers(&r); UA_FindServersResponse_deleteMembers(&r);
@ -263,7 +247,7 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
UA_CreateSessionResponse_init(&r); UA_CreateSessionResponse_init(&r);
init_response_header(&p.requestHeader, &r.responseHeader); init_response_header(&p.requestHeader, &r.responseHeader);
Service_CreateSession(server, clientChannel, &p, &r); Service_CreateSession(server, clientChannel, &p, &r);
ALLOC_MESSAGE(message, UA_CreateSessionResponse_calcSizeBinary(&r)); connection->getBuffer(connection, message, UA_CreateSessionResponse_calcSizeBinary(&r));
UA_CreateSessionResponse_encodeBinary(&r, message, &sendOffset); UA_CreateSessionResponse_encodeBinary(&r, message, &sendOffset);
UA_CreateSessionRequest_deleteMembers(&p); UA_CreateSessionRequest_deleteMembers(&p);
UA_CreateSessionResponse_deleteMembers(&r); UA_CreateSessionResponse_deleteMembers(&r);
@ -279,7 +263,7 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
UA_ActivateSessionResponse_init(&r); UA_ActivateSessionResponse_init(&r);
init_response_header(&p.requestHeader, &r.responseHeader); init_response_header(&p.requestHeader, &r.responseHeader);
Service_ActivateSession(server, clientChannel, &p, &r); Service_ActivateSession(server, clientChannel, &p, &r);
ALLOC_MESSAGE(message, UA_ActivateSessionResponse_calcSizeBinary(&r)); connection->getBuffer(connection, message, UA_ActivateSessionResponse_calcSizeBinary(&r));
UA_ActivateSessionResponse_encodeBinary(&r, message, &sendOffset); UA_ActivateSessionResponse_encodeBinary(&r, message, &sendOffset);
UA_ActivateSessionRequest_deleteMembers(&p); UA_ActivateSessionRequest_deleteMembers(&p);
UA_ActivateSessionResponse_deleteMembers(&r); UA_ActivateSessionResponse_deleteMembers(&r);
@ -287,57 +271,39 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
break; break;
} }
case UA_NS0ID_CLOSESESSIONREQUEST: { case UA_NS0ID_CLOSESESSIONREQUEST:
UA_CloseSessionRequest p; INVOKE_SERVICE(CloseSession);
UA_CloseSessionResponse r;
if(UA_CloseSessionRequest_decodeBinary(msg, pos, &p))
return;
UA_CloseSessionResponse_init(&r);
init_response_header(&p.requestHeader, &r.responseHeader);
Service_CloseSession(server, &p, &r);
ALLOC_MESSAGE(message, UA_CloseSessionResponse_calcSizeBinary(&r));
UA_CloseSessionResponse_encodeBinary(&r, message, &sendOffset);
UA_CloseSessionRequest_deleteMembers(&p);
UA_CloseSessionResponse_deleteMembers(&r);
responseType = requestType.identifier.numeric + 3;
break; break;
}
case UA_NS0ID_READREQUEST: case UA_NS0ID_READREQUEST:
INVOKE_SERVICE(Read); INVOKE_SERVICE(Read);
break; break;
case UA_NS0ID_WRITEREQUEST: case UA_NS0ID_WRITEREQUEST:
INVOKE_SERVICE(Write); INVOKE_SERVICE(Write);
break; break;
case UA_NS0ID_BROWSEREQUEST: case UA_NS0ID_BROWSEREQUEST:
INVOKE_SERVICE(Browse); INVOKE_SERVICE(Browse);
break; break;
case UA_NS0ID_BROWSENEXTREQUEST:
INVOKE_SERVICE(BrowseNext);
break;
case UA_NS0ID_ADDREFERENCESREQUEST: case UA_NS0ID_ADDREFERENCESREQUEST:
INVOKE_SERVICE(AddReferences); INVOKE_SERVICE(AddReferences);
break; break;
case UA_NS0ID_REGISTERNODESREQUEST: case UA_NS0ID_REGISTERNODESREQUEST:
INVOKE_SERVICE(RegisterNodes); INVOKE_SERVICE(RegisterNodes);
break; break;
case UA_NS0ID_UNREGISTERNODESREQUEST: case UA_NS0ID_UNREGISTERNODESREQUEST:
INVOKE_SERVICE(UnregisterNodes); INVOKE_SERVICE(UnregisterNodes);
break; break;
case UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST: case UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST:
INVOKE_SERVICE(TranslateBrowsePathsToNodeIds); INVOKE_SERVICE(TranslateBrowsePathsToNodeIds);
break; break;
default: { default: {
UA_LOG_INFO(server->logger, UA_LOGGERCATEGORY_COMMUNICATION, "Unknown request: NodeId(ns=%d, i=%d)", UA_LOG_INFO(server->logger, UA_LOGGERCATEGORY_COMMUNICATION, "Unknown request: NodeId(ns=%d, i=%d)",
requestType.namespaceIndex, requestType.identifier.numeric); requestType.namespaceIndex, requestType.identifier.numeric);
UA_RequestHeader p; UA_RequestHeader p;
UA_ResponseHeader r; UA_ResponseHeader r;
if(UA_RequestHeader_decodeBinary(msg, pos, &p)) if(UA_RequestHeader_decodeBinary(msg, pos, &p) != UA_STATUSCODE_GOOD)
return; return;
UA_ResponseHeader_init(&r); UA_ResponseHeader_init(&r);
init_response_header(&p, &r); init_response_header(&p, &r);
@ -346,7 +312,7 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
if(retval != UA_STATUSCODE_GOOD) if(retval != UA_STATUSCODE_GOOD)
r.serviceResult = retval; r.serviceResult = retval;
#endif #endif
ALLOC_MESSAGE(message, UA_ResponseHeader_calcSizeBinary(&r)); connection->getBuffer(connection, message, UA_ResponseHeader_calcSizeBinary(&r));
UA_ResponseHeader_encodeBinary(&r, message, &sendOffset); UA_ResponseHeader_encodeBinary(&r, message, &sendOffset);
UA_RequestHeader_deleteMembers(&p); UA_RequestHeader_deleteMembers(&p);
UA_ResponseHeader_deleteMembers(&r); UA_ResponseHeader_deleteMembers(&r);
@ -376,11 +342,10 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
+ UA_SymmetricAlgorithmSecurityHeader_calcSizeBinary(&symSecHeader) + UA_SymmetricAlgorithmSecurityHeader_calcSizeBinary(&symSecHeader)
+ UA_SequenceHeader_calcSizeBinary(&seqHeader) + UA_SequenceHeader_calcSizeBinary(&seqHeader)
+ UA_NodeId_calcSizeBinary(&response_nodeid); + UA_NodeId_calcSizeBinary(&response_nodeid);
respHeader.messageHeader.messageSize = headerSize + message->length;
*header = (UA_ByteString){ .length = headerSize, .data = UA_alloca(headerSize) };
respHeader.messageHeader.messageSize = header->length + message->length;
size_t rpos = 0; size_t rpos = 0;
connection->getBuffer(connection, header, headerSize);
UA_SecureConversationMessageHeader_encodeBinary(&respHeader, header, &rpos); UA_SecureConversationMessageHeader_encodeBinary(&respHeader, header, &rpos);
UA_SymmetricAlgorithmSecurityHeader_encodeBinary(&symSecHeader, header, &rpos); UA_SymmetricAlgorithmSecurityHeader_encodeBinary(&symSecHeader, header, &rpos);
UA_SequenceHeader_encodeBinary(&seqHeader, header, &rpos); UA_SequenceHeader_encodeBinary(&seqHeader, header, &rpos);
@ -393,27 +358,22 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
responseBufArray.strings = responseBufs; responseBufArray.strings = responseBufs;
responseBufArray.stringsSize = 2; responseBufArray.stringsSize = 2;
connection->write(connection, responseBufArray); connection->write(connection, responseBufArray);
connection->releaseBuffer(connection, header);
if(!messageOnStack) connection->releaseBuffer(connection, message);
UA_free(message->data);
} }
static void processCLO(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, static void processCLO(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, size_t *pos) {
size_t *pos) {
UA_UInt32 secureChannelId; UA_UInt32 secureChannelId;
UA_StatusCode retval = UA_UInt32_decodeBinary(msg, pos, &secureChannelId); UA_StatusCode retval = UA_UInt32_decodeBinary(msg, pos, &secureChannelId);
if(retval != UA_STATUSCODE_GOOD || !connection->channel || if(retval != UA_STATUSCODE_GOOD || !connection->channel ||
connection->channel->securityToken.channelId != secureChannelId) connection->channel->securityToken.channelId != secureChannelId)
return; return;
Service_CloseSecureChannel(server, secureChannelId); Service_CloseSecureChannel(server, secureChannelId);
} }
void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, UA_ByteString *msg) { void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, UA_ByteString *msg) {
if(msg->length <= 0) if(msg->length <= 0)
return; return;
size_t pos = 0; size_t pos = 0;
UA_TcpMessageHeader tcpMessageHeader; UA_TcpMessageHeader tcpMessageHeader;
do { do {
@ -423,9 +383,6 @@ void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection
break; break;
} }
if(tcpMessageHeader.messageSize < 32)
break; // there is no usefull message of that size
size_t targetpos = pos - 8 + tcpMessageHeader.messageSize; size_t targetpos = pos - 8 + tcpMessageHeader.messageSize;
switch(tcpMessageHeader.messageTypeAndFinal & 0xffffff) { switch(tcpMessageHeader.messageTypeAndFinal & 0xffffff) {

View File

@ -89,7 +89,7 @@ void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
const UA_ActivateSessionRequest *request, UA_ActivateSessionResponse *response); const UA_ActivateSessionRequest *request, UA_ActivateSessionResponse *response);
/** Used to terminate a Session. */ /** Used to terminate a Session. */
void Service_CloseSession(UA_Server *server, const UA_CloseSessionRequest *request, void Service_CloseSession(UA_Server *server, UA_Session *session, const UA_CloseSessionRequest *request,
UA_CloseSessionResponse *response); UA_CloseSessionResponse *response);
// Service_Cancel // Service_Cancel
/** @} */ /** @} */
@ -136,14 +136,23 @@ void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_D
* further limited by the use of a View. This Browse Service also supports a * further limited by the use of a View. This Browse Service also supports a
* primitive filtering capability. * primitive filtering capability.
*/ */
void Service_Browse(UA_Server *server, UA_Session *session, void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseRequest *request,
const UA_BrowseRequest *request, UA_BrowseResponse *response); UA_BrowseResponse *response);
/**
* Used to request the next set of Browse or BrowseNext response information
* that is too large to be sent in a single response. Too large in this
* context means that the Server is not able to return a larger response or that
* the number of results to return exceeds the maximum number of results to
* return that was specified by the Client in the original Browse request.
*/
void Service_BrowseNext(UA_Server *server, UA_Session *session, const UA_BrowseNextRequest *request,
UA_BrowseNextResponse *response);
/** Used to translate textual node paths to their respective ids. */ /** Used to translate textual node paths to their respective ids. */
void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session, void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
const UA_TranslateBrowsePathsToNodeIdsRequest *request, const UA_TranslateBrowsePathsToNodeIdsRequest *request,
UA_TranslateBrowsePathsToNodeIdsResponse *response); UA_TranslateBrowsePathsToNodeIdsResponse *response);
// Service_BrowseNext
// Service_RegisterNodes // Service_RegisterNodes
void Service_RegisterNodes(UA_Server *server, UA_Session *session, const UA_RegisterNodesRequest *request, void Service_RegisterNodes(UA_Server *server, UA_Session *session, const UA_RegisterNodesRequest *request,

View File

@ -20,7 +20,7 @@ void Service_FindServers(UA_Server *server, const UA_FindServersRequest *request
void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *request, UA_GetEndpointsResponse *response) { void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *request, UA_GetEndpointsResponse *response) {
/* test if the supported binary profile shall be returned */ /* test if the supported binary profile shall be returned */
UA_Boolean relevant_endpoints[server->endpointDescriptionsSize]; UA_Boolean *relevant_endpoints = UA_alloca(sizeof(UA_Boolean)*server->endpointDescriptionsSize);
size_t relevant_count = 0; size_t relevant_count = 0;
for(UA_Int32 j = 0; j < server->endpointDescriptionsSize; j++) { for(UA_Int32 j = 0; j < server->endpointDescriptionsSize; j++) {
relevant_endpoints[j] = UA_FALSE; relevant_endpoints[j] = UA_FALSE;

View File

@ -53,8 +53,8 @@ void Service_ActivateSession(UA_Server *server,UA_SecureChannel *channel,
channel->session = foundSession; channel->session = foundSession;
} }
void Service_CloseSession(UA_Server *server, const UA_CloseSessionRequest *request, void Service_CloseSession(UA_Server *server, UA_Session *session, const UA_CloseSessionRequest *request,
UA_CloseSessionResponse *response) { UA_CloseSessionResponse *response) {
UA_Session *foundSession; UA_Session *foundSession;
UA_SessionManager_getSessionByToken(&server->sessionManager, UA_SessionManager_getSessionByToken(&server->sessionManager,
(const UA_NodeId*)&request->requestHeader.authenticationToken, &foundSession); (const UA_NodeId*)&request->requestHeader.authenticationToken, &foundSession);
@ -64,8 +64,6 @@ void Service_CloseSession(UA_Server *server, const UA_CloseSessionRequest *reque
return; return;
} }
if(UA_SessionManager_removeSession(&server->sessionManager, &foundSession->sessionId) == UA_STATUSCODE_GOOD) response->responseHeader.serviceResult =
response->responseHeader.serviceResult = UA_STATUSCODE_GOOD; UA_SessionManager_removeSession(&server->sessionManager, &session->sessionId);
else
response->responseHeader.serviceResult = UA_STATUSCODE_BADSECURECHANNELIDINVALID;
} }

View File

@ -4,9 +4,8 @@
#include "ua_nodestore.h" #include "ua_nodestore.h"
#include "ua_util.h" #include "ua_util.h"
static UA_StatusCode static UA_StatusCode fillrefdescr(UA_NodeStore *ns, const UA_Node *curr, UA_ReferenceNode *ref,
fillrefdescr(UA_NodeStore *ns, const UA_Node *curr, UA_ReferenceNode *ref, UA_UInt32 mask, UA_UInt32 mask, UA_ReferenceDescription *descr)
UA_ReferenceDescription *descr)
{ {
UA_StatusCode retval = UA_STATUSCODE_GOOD; UA_StatusCode retval = UA_STATUSCODE_GOOD;
UA_ReferenceDescription_init(descr); UA_ReferenceDescription_init(descr);
@ -25,13 +24,14 @@ fillrefdescr(UA_NodeStore *ns, const UA_Node *curr, UA_ReferenceNode *ref, UA_UI
retval |= UA_QualifiedName_copy(&curr->browseName, &descr->browseName); retval |= UA_QualifiedName_copy(&curr->browseName, &descr->browseName);
if(mask & UA_BROWSERESULTMASK_DISPLAYNAME) if(mask & UA_BROWSERESULTMASK_DISPLAYNAME)
retval |= UA_LocalizedText_copy(&curr->displayName, &descr->displayName); retval |= UA_LocalizedText_copy(&curr->displayName, &descr->displayName);
if(mask & UA_BROWSERESULTMASK_TYPEDEFINITION ) { if(mask & UA_BROWSERESULTMASK_TYPEDEFINITION &&
for(UA_Int32 i = 0;i < curr->referencesSize;i++) { (curr->nodeClass == UA_NODECLASS_OBJECT || curr->nodeClass == UA_NODECLASS_VARIABLE)) {
for(UA_Int32 i = 0; i < curr->referencesSize; i++) {
UA_ReferenceNode *refnode = &curr->references[i]; UA_ReferenceNode *refnode = &curr->references[i];
if(refnode->referenceTypeId.identifier.numeric == UA_NS0ID_HASTYPEDEFINITION) { if(refnode->referenceTypeId.identifier.numeric != UA_NS0ID_HASTYPEDEFINITION)
retval |= UA_ExpandedNodeId_copy(&refnode->targetId, &descr->typeDefinition); continue;
break; retval |= UA_ExpandedNodeId_copy(&refnode->targetId, &descr->typeDefinition);
} break;
} }
} }
@ -42,9 +42,9 @@ fillrefdescr(UA_NodeStore *ns, const UA_Node *curr, UA_ReferenceNode *ref, UA_UI
/* Tests if the node is relevant to the browse request and shall be returned. If /* Tests if the node is relevant to the browse request and shall be returned. If
so, it is retrieved from the Nodestore. If not, null is returned. */ so, it is retrieved from the Nodestore. If not, null is returned. */
static const UA_Node static const UA_Node *relevant_node(UA_NodeStore *ns, const UA_BrowseDescription *descr,
*relevant_node(UA_NodeStore *ns, const UA_BrowseDescription *descr, UA_Boolean return_all, UA_Boolean return_all, UA_ReferenceNode *reference,
UA_ReferenceNode *reference, UA_NodeId *relevant, size_t relevant_count) UA_NodeId *relevant, size_t relevant_count)
{ {
if(reference->isInverse == UA_TRUE && descr->browseDirection == UA_BROWSEDIRECTION_FORWARD) if(reference->isInverse == UA_TRUE && descr->browseDirection == UA_BROWSEDIRECTION_FORWARD)
return UA_NULL; return UA_NULL;
@ -73,14 +73,21 @@ is_relevant: ;
* the array and increase the size. Since the hierarchy is not cyclic, we can safely progress in the * the array and increase the size. Since the hierarchy is not cyclic, we can safely progress in the
* array to process the newly found referencetype nodeids (emulated recursion). * array to process the newly found referencetype nodeids (emulated recursion).
*/ */
static UA_StatusCode static UA_StatusCode findsubtypes(UA_NodeStore *ns, const UA_NodeId *root, UA_NodeId **reftypes,
findsubtypes(UA_NodeStore *ns, const UA_NodeId *root, UA_NodeId **reftypes, size_t *reftypes_count) size_t *reftypes_count)
{ {
size_t results_size = 20; // probably too big, but saves mallocs size_t results_size = 20; // probably too big, but saves mallocs
UA_NodeId *results = UA_malloc(sizeof(UA_NodeId) * results_size); UA_NodeId *results = UA_malloc(sizeof(UA_NodeId) * results_size);
if(!results) if(!results)
return UA_STATUSCODE_BADOUTOFMEMORY; return UA_STATUSCODE_BADOUTOFMEMORY;
const UA_Node *node = UA_NodeStore_get(ns, root);
if(!node || node->nodeClass != UA_NODECLASS_REFERENCETYPE) {
UA_NodeStore_release(node);
return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
}
UA_NodeStore_release(node);
UA_StatusCode retval = UA_NodeId_copy(root, &results[0]); UA_StatusCode retval = UA_NodeId_copy(root, &results[0]);
if(retval != UA_STATUSCODE_GOOD) { if(retval != UA_STATUSCODE_GOOD) {
UA_free(results); UA_free(results);
@ -90,7 +97,7 @@ findsubtypes(UA_NodeStore *ns, const UA_NodeId *root, UA_NodeId **reftypes, size
size_t index = 0; // where are we currently in the array? size_t index = 0; // where are we currently in the array?
size_t last = 0; // where is the last element in the array? size_t last = 0; // where is the last element in the array?
do { do {
const UA_Node *node = UA_NodeStore_get(ns, &results[index]); node = UA_NodeStore_get(ns, &results[index]);
if(!node || node->nodeClass != UA_NODECLASS_REFERENCETYPE) if(!node || node->nodeClass != UA_NODECLASS_REFERENCETYPE)
continue; continue;
for(UA_Int32 i = 0; i < node->referencesSize; i++) { for(UA_Int32 i = 0; i < node->referencesSize; i++) {
@ -127,19 +134,36 @@ findsubtypes(UA_NodeStore *ns, const UA_NodeId *root, UA_NodeId **reftypes, size
return UA_STATUSCODE_GOOD; return UA_STATUSCODE_GOOD;
} }
/* Results for a single browsedescription. */ /* Results for a single browsedescription. Call this either with an existing continuationpoint from
static void which we take the entire context for the search. Or, call with a browsedescription and maxrefs
browse(UA_NodeStore *ns, const UA_BrowseDescription *descr, UA_UInt32 maxrefs, UA_BrowseResult *result) value. If we need to create a new continuationpoint, it will be alloced and the new pointer
stored in *cp.
*/
static void browse(UA_NodeStore *ns, struct ContinuationPointEntry **cp, const UA_BrowseDescription *descr,
UA_UInt32 maxrefs, UA_BrowseResult *result)
{ {
UA_UInt32 continuationIndex = 0;
if(*cp) {
descr = &(*cp)->browseDescription;
maxrefs = (*cp)->maxReferences;
continuationIndex = (*cp)->continuationIndex;
}
if(descr->browseDirection != UA_BROWSEDIRECTION_BOTH &&
descr->browseDirection != UA_BROWSEDIRECTION_FORWARD &&
descr->browseDirection != UA_BROWSEDIRECTION_INVERSE) {
result->statusCode = UA_STATUSCODE_BADBROWSEDIRECTIONINVALID;
return;
}
size_t relevant_refs_size = 0; size_t relevant_refs_size = 0;
UA_NodeId *relevant_refs = UA_NULL; UA_NodeId *relevant_refs = UA_NULL;
// what are the relevant references? // what are the relevant references?
UA_Boolean all_refs = UA_NodeId_isNull(&descr->referenceTypeId); UA_Boolean all_refs = UA_NodeId_isNull(&descr->referenceTypeId);
if(!all_refs) { if(!all_refs) {
if(descr->includeSubtypes) { if(descr->includeSubtypes) {
result->statusCode = findsubtypes(ns, &descr->referenceTypeId, result->statusCode = findsubtypes(ns, &descr->referenceTypeId, &relevant_refs,
&relevant_refs, &relevant_refs_size); &relevant_refs_size);
if(result->statusCode != UA_STATUSCODE_GOOD) if(result->statusCode != UA_STATUSCODE_GOOD)
return; return;
} else { } else {
@ -162,35 +186,64 @@ browse(UA_NodeStore *ns, const UA_BrowseDescription *descr, UA_UInt32 maxrefs, U
goto cleanup; goto cleanup;
} }
// allocate memory for the results UA_UInt32 real_maxrefs = maxrefs;
maxrefs = node->referencesSize; // allocate enough space for all of them if(real_maxrefs == 0)
result->references = UA_malloc(sizeof(UA_ReferenceDescription) * maxrefs); real_maxrefs = node->referencesSize;
result->references = UA_malloc(sizeof(UA_ReferenceDescription) * real_maxrefs);
if(!result->references) { if(!result->references) {
result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY; result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
goto cleanup; goto cleanup;
} }
size_t count = 0; size_t count = 0;
for(UA_Int32 i = 0; i < node->referencesSize && count < maxrefs; i++) { size_t skipped = 0;
const UA_Node *current; for(UA_Int32 i = 0; i < node->referencesSize && count < real_maxrefs; i++) {
current = relevant_node(ns, descr, all_refs, &node->references[i], relevant_refs, relevant_refs_size); const UA_Node *current = relevant_node(ns, descr, all_refs, &node->references[i],
relevant_refs, relevant_refs_size);
if(!current) if(!current)
continue; continue;
if(skipped < continuationIndex) {
UA_StatusCode retval = fillrefdescr(ns, current, &node->references[i], UA_NodeStore_release(current);
descr->resultMask, &result->references[count]); skipped++;
continue;
}
UA_StatusCode retval = fillrefdescr(ns, current, &node->references[i], descr->resultMask,
&result->references[count]);
UA_NodeStore_release(current); UA_NodeStore_release(current);
if(retval != UA_STATUSCODE_GOOD) {
if(retval) {
UA_Array_delete(result->references, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION], count); UA_Array_delete(result->references, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION], count);
count = 0;
result->references = UA_NULL; result->references = UA_NULL;
count = 0;
result->statusCode = UA_STATUSCODE_UNCERTAINNOTALLNODESAVAILABLE; result->statusCode = UA_STATUSCODE_UNCERTAINNOTALLNODESAVAILABLE;
break; goto cleanup;
} }
count++; count++;
} }
if(count != 0)
if(*cp) {
(*cp)->continuationIndex += count;
if((*cp)->continuationIndex == node->referencesSize) {
/* remove a finished continuationPoint */
UA_ByteString_deleteMembers(&(*cp)->identifier);
UA_BrowseDescription_deleteMembers(&(*cp)->browseDescription);
LIST_REMOVE(*cp, pointers);
UA_free(*cp);
*cp = UA_NULL;
}
} else if(maxrefs != 0 && count >= maxrefs) {
/* create a continuationPoint */
*cp = UA_malloc(sizeof(struct ContinuationPointEntry));
UA_BrowseDescription_copy(descr,&(*cp)->browseDescription);
(*cp)->maxReferences = maxrefs;
(*cp)->continuationIndex = count;
UA_Guid *ident = UA_Guid_new();
UA_UInt32 seed = (uintptr_t)*cp;
*ident = UA_Guid_random(&seed);
(*cp)->identifier.data = (UA_Byte*)ident;
(*cp)->identifier.length = sizeof(UA_Guid);
}
if(count > 0)
result->referencesSize = count; result->referencesSize = count;
else { else {
UA_free(result->references); UA_free(result->references);
@ -205,6 +258,11 @@ cleanup:
void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseRequest *request, void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseRequest *request,
UA_BrowseResponse *response) { UA_BrowseResponse *response) {
if(!UA_NodeId_isNull(&request->view.viewId)) {
response->responseHeader.serviceResult = UA_STATUSCODE_BADVIEWIDUNKNOWN;
return;
}
if(request->nodesToBrowseSize <= 0) { if(request->nodesToBrowseSize <= 0) {
response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO; response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
return; return;
@ -240,13 +298,63 @@ void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseReque
/* ### End External Namespaces */ /* ### End External Namespaces */
response->resultsSize = size; response->resultsSize = size;
for(size_t i = 0;i < size;i++){ for(size_t i = 0; i < size; i++) {
if(!isExternal[i]) if(!isExternal[i]) {
browse(server->nodestore, &request->nodesToBrowse[i], struct ContinuationPointEntry *cp = UA_NULL;
browse(server->nodestore, &cp, &request->nodesToBrowse[i],
request->requestedMaxReferencesPerNode, &response->results[i]); request->requestedMaxReferencesPerNode, &response->results[i]);
if(cp) {
LIST_INSERT_HEAD(&session->continuationPoints, cp, pointers);
UA_ByteString_copy(&cp->identifier, &response->results[i].continuationPoint);
}
}
} }
} }
void Service_BrowseNext(UA_Server *server, UA_Session *session, const UA_BrowseNextRequest *request,
UA_BrowseNextResponse *response) {
if(request->continuationPointsSize <= 0) {
response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
return;
}
size_t size = request->continuationPointsSize;
if(!request->releaseContinuationPoints) {
response->results = UA_Array_new(&UA_TYPES[UA_TYPES_BROWSERESULT], size);
if(!response->results) {
response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
return;
}
response->resultsSize = size;
for(size_t i = 0; i < size; i++) {
struct ContinuationPointEntry *cp = UA_NULL;
struct ContinuationPointEntry *search_cp;
LIST_FOREACH(search_cp, &session->continuationPoints, pointers) {
if(UA_ByteString_equal(&search_cp->identifier, &request->continuationPoints[i])) {
cp = search_cp;
break;
}
}
if(!cp)
response->results[i].statusCode = UA_STATUSCODE_BADCONTINUATIONPOINTINVALID;
else
browse(server->nodestore, &cp, UA_NULL, 0, &response->results[i]);
}
} else {
for(size_t i = 0; i < size; i++) {
struct ContinuationPointEntry *cp = UA_NULL;
LIST_FOREACH(cp, &session->continuationPoints, pointers) {
if(UA_ByteString_equal(&cp->identifier, &request->continuationPoints[i])) {
UA_ByteString_deleteMembers(&cp->identifier);
UA_BrowseDescription_deleteMembers(&cp->browseDescription);
LIST_REMOVE(cp, pointers);
UA_free(cp);
break;
}
}
}
}
}
/***********************/ /***********************/
/* TranslateBrowsePath */ /* TranslateBrowsePath */
/***********************/ /***********************/
@ -364,49 +472,67 @@ static void translateBrowsePath(UA_Server *server, UA_Session *session, const UA
void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session, void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
const UA_TranslateBrowsePathsToNodeIdsRequest *request, const UA_TranslateBrowsePathsToNodeIdsRequest *request,
UA_TranslateBrowsePathsToNodeIdsResponse *response) { UA_TranslateBrowsePathsToNodeIdsResponse *response) {
if(request->browsePathsSize <= 0) { size_t size = request->browsePathsSize;
if(size <= 0) {
response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO; response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
return; return;
} }
response->results = UA_Array_new(&UA_TYPES[UA_TYPES_BROWSEPATHRESULT], request->browsePathsSize); response->results = UA_Array_new(&UA_TYPES[UA_TYPES_BROWSEPATHRESULT], size);
if(!response->results) { if(!response->results) {
response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY; response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
return; return;
} }
response->resultsSize = request->browsePathsSize; /* ### Begin External Namespaces */
for(UA_Int32 i = 0;i < response->resultsSize;i++) UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * size);
translateBrowsePath(server, session, &request->browsePaths[i], &response->results[i]); UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * size);
for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
size_t indexSize = 0;
for(size_t i = 0;i < size;i++) {
if(request->browsePaths[i].startingNode.namespaceIndex != server->externalNamespaces[j].index)
continue;
isExternal[i] = UA_TRUE;
indices[indexSize] = i;
indexSize++;
}
if(indexSize == 0)
continue;
UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
ens->translateBrowsePathsToNodeIds(ens->ensHandle, &request->requestHeader, request->browsePaths,
indices, indexSize, response->results, response->diagnosticInfos);
}
/* ### End External Namespaces */
response->resultsSize = size;
for(size_t i = 0; i < size; i++){
if(!isExternal[i])
translateBrowsePath(server, session, &request->browsePaths[i], &response->results[i]);
}
} }
void Service_RegisterNodes(UA_Server *server, UA_Session *session, void Service_RegisterNodes(UA_Server *server, UA_Session *session, const UA_RegisterNodesRequest *request,
const UA_RegisterNodesRequest *request, UA_RegisterNodesResponse *response) {
UA_RegisterNodesResponse *response) {
//TODO: hang the nodeids to the session if really needed //TODO: hang the nodeids to the session if really needed
response->responseHeader.timestamp = UA_DateTime_now(); response->responseHeader.timestamp = UA_DateTime_now();
response->registeredNodeIdsSize = request->nodesToRegisterSize; response->registeredNodeIdsSize = request->nodesToRegisterSize;
response->registeredNodeIds = request->nodesToRegister; response->registeredNodeIds = request->nodesToRegister;
if(request->nodesToRegisterSize==0){ if(request->nodesToRegisterSize==0)
response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO; response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
} if(UA_NodeId_equal(&request->requestHeader.authenticationToken, &UA_NODEID_NULL) ||
if(UA_NodeId_equal(&request->requestHeader.authenticationToken, &UA_NODEID_NULL) || !UA_NodeId_equal(&request->requestHeader.authenticationToken, &session->authenticationToken) ){ !UA_NodeId_equal(&request->requestHeader.authenticationToken, &session->authenticationToken))
response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID; response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
}
} }
void Service_UnregisterNodes(UA_Server *server, UA_Session *session, void Service_UnregisterNodes(UA_Server *server, UA_Session *session, const UA_UnregisterNodesRequest *request,
const UA_UnregisterNodesRequest *request, UA_UnregisterNodesResponse *response) {
UA_UnregisterNodesResponse *response) {
//TODO: remove the nodeids from the session if really needed //TODO: remove the nodeids from the session if really needed
response->responseHeader.timestamp = UA_DateTime_now(); response->responseHeader.timestamp = UA_DateTime_now();
if(request->nodesToUnregisterSize==0){ if(request->nodesToUnregisterSize==0)
response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO; response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
} if(UA_NodeId_equal(&request->requestHeader.authenticationToken, &UA_NODEID_NULL) ||
if(UA_NodeId_equal(&request->requestHeader.authenticationToken, &UA_NODEID_NULL) || !UA_NodeId_equal(&request->requestHeader.authenticationToken, &session->authenticationToken) ){ !UA_NodeId_equal(&request->requestHeader.authenticationToken, &session->authenticationToken))
response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID; response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
}
} }

View File

@ -2,13 +2,15 @@
#include "ua_connection.h" #include "ua_connection.h"
#include "ua_types_encoding_binary.h" #include "ua_types_encoding_binary.h"
const char *UA_LoggerCategoryNames[4] = {"communication", "server", "client", "userland"};
// max message size is 64k // max message size is 64k
const UA_ConnectionConfig UA_ConnectionConfig_standard = const UA_ConnectionConfig UA_ConnectionConfig_standard =
{.protocolVersion = 0, .sendBufferSize = 65536, .recvBufferSize = 65536, {.protocolVersion = 0, .sendBufferSize = 65536, .recvBufferSize = 65536,
.maxMessageSize = 65536, .maxChunkCount = 1}; .maxMessageSize = 65536, .maxChunkCount = 1};
void UA_Connection_init(UA_Connection *connection) { void UA_Connection_init(UA_Connection *connection) {
connection->state = UA_CONNECTION_OPENING; connection->state = UA_CONNECTION_CLOSED;
connection->localConf = (UA_ConnectionConfig){0,0,0,0,0}; connection->localConf = (UA_ConnectionConfig){0,0,0,0,0};
connection->remoteConf = (UA_ConnectionConfig){0,0,0,0,0}; connection->remoteConf = (UA_ConnectionConfig){0,0,0,0,0};
connection->channel = UA_NULL; connection->channel = UA_NULL;

View File

@ -10,10 +10,11 @@ UA_Session anonymousSession = {
.discoveryUrlsSize = -1, .discoveryUrls = UA_NULL}, .discoveryUrlsSize = -1, .discoveryUrls = UA_NULL},
.sessionName = {sizeof("Anonymous Session")-1, (UA_Byte*)"Anonymous Session"}, .sessionName = {sizeof("Anonymous Session")-1, (UA_Byte*)"Anonymous Session"},
.authenticationToken = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .authenticationToken = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
.identifier.numeric = 0}, .identifier.numeric = 0},
.sessionId = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0}, .sessionId = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0},
.maxRequestMessageSize = UA_UINT32_MAX, .maxResponseMessageSize = UA_UINT32_MAX, .maxRequestMessageSize = UA_UINT32_MAX, .maxResponseMessageSize = UA_UINT32_MAX,
.timeout = UA_INT64_MAX, .validTill = UA_INT64_MAX, .channel = UA_NULL}; .timeout = UA_INT64_MAX, .validTill = UA_INT64_MAX, .channel = UA_NULL,
.continuationPoints = {UA_NULL}};
UA_Session adminSession = { UA_Session adminSession = {
.clientDescription = {.applicationUri = {-1, UA_NULL}, .productUri = {-1, UA_NULL}, .clientDescription = {.applicationUri = {-1, UA_NULL}, .productUri = {-1, UA_NULL},
@ -26,7 +27,16 @@ UA_Session adminSession = {
.identifier.numeric = 1}, .identifier.numeric = 1},
.sessionId = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 1}, .sessionId = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 1},
.maxRequestMessageSize = UA_UINT32_MAX, .maxResponseMessageSize = UA_UINT32_MAX, .maxRequestMessageSize = UA_UINT32_MAX, .maxResponseMessageSize = UA_UINT32_MAX,
.timeout = UA_INT64_MAX, .validTill = UA_INT64_MAX, .channel = UA_NULL}; .timeout = UA_INT64_MAX, .validTill = UA_INT64_MAX, .channel = UA_NULL,
.continuationPoints = {UA_NULL}};
/* TODO: Nobody seems to call this function right now */
static UA_StatusCode UA_Session_generateToken(UA_NodeId *newToken, UA_UInt32 *seed) {
newToken->namespaceIndex = 0; // where else?
newToken->identifierType = UA_NODEIDTYPE_GUID;
newToken->identifier.guid = UA_Guid_random(seed);
return UA_STATUSCODE_GOOD;
}
void UA_Session_init(UA_Session *session) { void UA_Session_init(UA_Session *session) {
UA_ApplicationDescription_init(&session->clientDescription); UA_ApplicationDescription_init(&session->clientDescription);
@ -38,6 +48,7 @@ void UA_Session_init(UA_Session *session) {
session->timeout = 0; session->timeout = 0;
UA_DateTime_init(&session->validTill); UA_DateTime_init(&session->validTill);
session->channel = UA_NULL; session->channel = UA_NULL;
session->continuationPoints = (struct ContinuationPointList){UA_NULL};
} }
void UA_Session_deleteMembers(UA_Session *session) { void UA_Session_deleteMembers(UA_Session *session) {
@ -46,6 +57,13 @@ void UA_Session_deleteMembers(UA_Session *session) {
UA_NodeId_deleteMembers(&session->sessionId); UA_NodeId_deleteMembers(&session->sessionId);
UA_String_deleteMembers(&session->sessionName); UA_String_deleteMembers(&session->sessionName);
session->channel = UA_NULL; session->channel = UA_NULL;
struct ContinuationPointEntry *cp;
while((cp = LIST_FIRST(&session->continuationPoints))) {
UA_ByteString_deleteMembers(&cp->identifier);
UA_BrowseDescription_deleteMembers(&cp->browseDescription);
LIST_REMOVE(cp, pointers);
UA_free(cp);
}
} }
UA_StatusCode UA_Session_setExpirationDate(UA_Session *session) { UA_StatusCode UA_Session_setExpirationDate(UA_Session *session) {

View File

@ -3,6 +3,7 @@
#include "ua_types.h" #include "ua_types.h"
#include "ua_securechannel.h" #include "ua_securechannel.h"
#include "queue.h"
/** /**
* @ingroup communication * @ingroup communication
@ -10,6 +11,14 @@
* @{ * @{
*/ */
struct ContinuationPointEntry {
LIST_ENTRY(ContinuationPointEntry) pointers;
UA_ByteString identifier;
UA_BrowseDescription browseDescription;
UA_Int32 continuationIndex;
UA_UInt32 maxReferences;
};
struct UA_Session { struct UA_Session {
UA_ApplicationDescription clientDescription; UA_ApplicationDescription clientDescription;
UA_String sessionName; UA_String sessionName;
@ -20,6 +29,7 @@ struct UA_Session {
UA_Int64 timeout; UA_Int64 timeout;
UA_DateTime validTill; UA_DateTime validTill;
UA_SecureChannel *channel; UA_SecureChannel *channel;
LIST_HEAD(ContinuationPointList, ContinuationPointEntry) continuationPoints;
}; };
extern UA_Session anonymousSession; ///< If anonymous access is allowed, this session is used internally (Session ID: 0) extern UA_Session anonymousSession; ///< If anonymous access is allowed, this session is used internally (Session ID: 0)

View File

@ -2,9 +2,9 @@
#define TESTING_NETWORKLAYERS_H_ #define TESTING_NETWORKLAYERS_H_
#ifdef NOT_AMALGATED #ifdef NOT_AMALGATED
#include "ua_server.h" # include "ua_server.h"
#else #else
#include "open62541.h" # include "open62541.h"
#endif #endif
/** @brief Create the TCP networklayer and listen to the specified port */ /** @brief Create the TCP networklayer and listen to the specified port */

View File

@ -48,6 +48,7 @@ minimal_types = ["InvalidType", "Node", "NodeClass", "ReferenceNode", "Applicati
"TranslateBrowsePathsToNodeIdsResponse", "BrowsePath", "BrowsePathResult", "RelativePath", "TranslateBrowsePathsToNodeIdsResponse", "BrowsePath", "BrowsePathResult", "RelativePath",
"BrowsePathTarget", "RelativePathElement", "CreateSubscriptionRequest", "CreateSubscriptionResponse", "BrowsePathTarget", "RelativePathElement", "CreateSubscriptionRequest", "CreateSubscriptionResponse",
"BrowseResponse", "BrowseResult", "ReferenceDescription", "BrowseRequest", "ViewDescription", "BrowseResponse", "BrowseResult", "ReferenceDescription", "BrowseRequest", "ViewDescription",
"BrowseNextRequest", "BrowseNextResponse",
"BrowseDescription", "BrowseDirection", "CloseSessionRequest", "AddNodesRequest", "AddNodesResponse", "BrowseDescription", "BrowseDirection", "CloseSessionRequest", "AddNodesRequest", "AddNodesResponse",
"AddNodesItem", "AddNodesResult", "DeleteNodesItem","AddReferencesRequest", "AddReferencesResponse", "AddNodesItem", "AddNodesResult", "DeleteNodesItem","AddReferencesRequest", "AddReferencesResponse",
"AddReferencesItem","DeleteReferencesItem", "VariableNode", "MethodNode", "VariableTypeNode", "AddReferencesItem","DeleteReferencesItem", "VariableNode", "MethodNode", "VariableTypeNode",