forked from open62541/open62541
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathua_network_udp.c
248 lines (221 loc) · 8.35 KB
/
ua_network_udp.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
* See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
#include "ua_network_udp.h"
#include <stdlib.h> // malloc, free
#include <stdio.h>
#include <string.h> // memset
#ifdef UA_ENABLE_MULTITHREADING
# include <urcu/uatomic.h>
#endif
/* with a space so amalgamation does not remove the includes */
# include <errno.h> // errno, EINTR
# include <fcntl.h> // fcntl
# include <strings.h> //bzero
# include <sys/select.h>
# include <netinet/in.h>
# include <netinet/tcp.h>
# include <sys/socketvar.h>
# include <sys/ioctl.h>
# include <unistd.h> // read, write, close
# include <arpa/inet.h>
#ifdef __QNX__
#include <sys/socket.h>
#endif
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
# include <sys/param.h>
# if defined(BSD)
# include<sys/socket.h>
# endif
#endif
# define CLOSESOCKET(S) close(S)
#define MAXBACKLOG 100
#ifdef _WIN32
# error udp not yet implemented for windows
#endif
/*****************************/
/* Generic Buffer Management */
/*****************************/
static UA_StatusCode GetMallocedBuffer(UA_Connection *connection, size_t length, UA_ByteString *buf) {
if(length > connection->remoteConf.recvBufferSize)
return UA_STATUSCODE_BADCOMMUNICATIONERROR;
return UA_ByteString_allocBuffer(buf, connection->remoteConf.recvBufferSize);
}
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
the networklayer */
typedef struct {
UA_Connection connection;
struct sockaddr from;
socklen_t fromlen;
} UDPConnection;
typedef struct {
UA_ConnectionConfig conf;
UA_UInt16 port;
fd_set fdset;
UA_Int32 serversockfd;
UA_Logger logger; // Set during start
} ServerNetworkLayerUDP;
/** Accesses only the sockfd in the handle. Can be run from parallel threads. */
static UA_StatusCode sendUDP(UA_Connection *connection, UA_ByteString *buf) {
UDPConnection *udpc = (UDPConnection*)connection;
ServerNetworkLayerUDP *layer = (ServerNetworkLayerUDP*)connection->handle;
long nWritten = 0;
struct sockaddr_in *sin = NULL;
if (udpc->from.sa_family == AF_INET) {
#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align"
#endif
sin = (struct sockaddr_in *) &udpc->from;
#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__))
#pragma GCC diagnostic pop
#endif
} else {
UA_ByteString_deleteMembers(buf);
return UA_STATUSCODE_BADINTERNALERROR;
}
while (nWritten < (long)buf->length) {
long n = sendto(layer->serversockfd, buf->data, buf->length, 0,
(struct sockaddr*)sin, sizeof(struct sockaddr_in));
if(n == -1L) {
UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "UDP send error %i", errno);
UA_ByteString_deleteMembers(buf);
return UA_STATUSCODE_BADINTERNALERROR;
}
nWritten += n;
}
UA_ByteString_deleteMembers(buf);
return UA_STATUSCODE_GOOD;
}
static UA_StatusCode 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(UA_Connection *handle) {
free(handle);
}
static UA_StatusCode ServerNetworkLayerUDP_start(UA_ServerNetworkLayer *nl, UA_Logger logger) {
ServerNetworkLayerUDP *layer = nl->handle;
layer->logger = logger;
layer->serversockfd = socket(PF_INET, SOCK_DGRAM, 0);
if(layer->serversockfd < 0) {
UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error opening socket");
return UA_STATUSCODE_BADINTERNALERROR;
}
const struct sockaddr_in serv_addr =
{.sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY,
.sin_port = htons(layer->port), .sin_zero = {0}};
int optval = 1;
if(setsockopt(layer->serversockfd, SOL_SOCKET,
SO_REUSEADDR, (const char *)&optval, sizeof(optval)) == -1) {
UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Could not setsockopt");
CLOSESOCKET(layer->serversockfd);
return UA_STATUSCODE_BADINTERNALERROR;
}
if(bind(layer->serversockfd, (const struct sockaddr *)&serv_addr,
sizeof(serv_addr)) < 0) {
UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Could not bind the socket");
CLOSESOCKET(layer->serversockfd);
return UA_STATUSCODE_BADINTERNALERROR;
}
socket_set_nonblocking(layer->serversockfd);
UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Listening for UDP connections on %s:%d",
inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
return UA_STATUSCODE_GOOD;
}
static size_t ServerNetworkLayerUDP_getJobs(UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt16 timeout) {
ServerNetworkLayerUDP *layer = nl->handle;
UA_Job *items = NULL;
setFDSet(layer);
struct timeval tmptv = {0, timeout};
int resultsize = select(layer->serversockfd+1, &layer->fdset, NULL, NULL, &tmptv);
if(resultsize <= 0 || !FD_ISSET(layer->serversockfd, &layer->fdset)) {
*jobs = items;
return 0;
}
items = malloc(sizeof(UA_Job)*(unsigned long)resultsize);
// read from established sockets
size_t j = 0;
UA_ByteString buf = {0, NULL};
if(!buf.data) {
buf.data = malloc(sizeof(UA_Byte) * layer->conf.recvBufferSize);
if(!buf.data)
UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "malloc failed");
}
struct sockaddr sender;
socklen_t sendsize = sizeof(sender);
bzero(&sender, sizeof(sender));
ssize_t rec_result = recvfrom(layer->serversockfd, buf.data, layer->conf.recvBufferSize, 0, &sender, &sendsize);
if (rec_result > 0) {
buf.length = (size_t)rec_result;
UDPConnection *c = malloc(sizeof(UDPConnection));
if(!c){
free(items);
return UA_STATUSCODE_BADINTERNALERROR;
}
UA_Connection_init(&c->connection);
c->from = sender;
c->fromlen = sendsize;
// c->sockfd = newsockfd;
c->connection.getSendBuffer = GetMallocedBuffer;
c->connection.releaseSendBuffer = ReleaseMallocedBuffer;
c->connection.releaseRecvBuffer = ReleaseMallocedBuffer;
c->connection.handle = layer;
c->connection.send = sendUDP;
c->connection.close = closeConnectionUDP;
c->connection.localConf = layer->conf;
c->connection.state = UA_CONNECTION_OPENING;
items[j].type = UA_JOBTYPE_BINARYMESSAGE_NETWORKLAYER;
items[j].job.binaryMessage.message = buf;
items[j].job.binaryMessage.connection = (UA_Connection*)c;
buf.data = NULL;
j++;
*jobs = items;
} else {
free(items);
*jobs = NULL;
}
if(buf.data)
free(buf.data);
return j;
}
static size_t ServerNetworkLayerUDP_stop(UA_ServerNetworkLayer *nl, UA_Job **jobs) {
ServerNetworkLayerUDP *layer = nl->handle;
UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK,
"Shutting down the UDP network layer");
CLOSESOCKET(layer->serversockfd);
return 0;
}
static void ServerNetworkLayerUDP_deleteMembers(UA_ServerNetworkLayer *nl) {
ServerNetworkLayerUDP *layer = nl->handle;
free(layer);
UA_String_deleteMembers(&nl->discoveryUrl);
}
UA_ServerNetworkLayer
UA_ServerNetworkLayerUDP(UA_ConnectionConfig conf, UA_UInt16 port) {
UA_ServerNetworkLayer nl;
memset(&nl, 0, sizeof(UA_ServerNetworkLayer));
ServerNetworkLayerUDP *layer = malloc(sizeof(ServerNetworkLayerUDP));
if(!layer)
return nl;
memset(layer, 0, sizeof(ServerNetworkLayerUDP));
layer->conf = conf;
layer->port = port;
nl.handle = layer;
nl.start = ServerNetworkLayerUDP_start;
nl.getJobs = ServerNetworkLayerUDP_getJobs;
nl.stop = ServerNetworkLayerUDP_stop;
nl.deleteMembers = ServerNetworkLayerUDP_deleteMembers;
return nl;
}