1 /* $Id: test_ncbi_dsock.c,v 6.36 2016/08/15 17:29:12 fukanchi Exp $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author: Anton Lavrentiev
27 *
28 * File Description:
29 * Test suite for datagram socket API
30 *
31 */
32
33 #include <connect/ncbi_connutil.h>
34 #include <connect/ncbi_socket.h>
35 #include "../ncbi_ansi_ext.h"
36 #include "../ncbi_priv.h" /* CORE logging facilities */
37 #include <errno.h>
38 #include <stdlib.h>
39 #include <time.h>
40
41 #include "test_assert.h" /* This header must go last */
42
43 /* maximal UDP packet size as allowed by the standard */
44 #define MAX_UDP_DGRAM_SIZE 65535
45
46 #if defined(NCBI_OS_BSD) || defined(NCBI_OS_OSF1) || defined(NCBI_OS_DARWIN)
47 /* FreeBSD has this limit :-/ Source: `sysctl net.inet.udp.maxdgram` */
48 /* For OSF1 (and FreeBSD) see also: /usr/include/netinet/udp_var.h */
49 # define MAX_DGRAM_SIZE (9*1024)
50 #elif defined(NCBI_OS_IRIX)
51 /* This has been found experimentally on IRIX64 6.5 04101931 IP25 */
52 # define MAX_DGRAM_SIZE (60*1024)
53 #elif defined(NCBI_OS_LINUX)
54 /* Larger sizes do not seem to work everywhere */
55 # define MAX_DGRAM_SIZE 59550
56 #elif defined(NCBI_OS_SOLARIS)
57 /* 65508 was reported too large */
58 # define MAX_DGRAM_SIZE 65507
59 #else
60 # define MAX_DGRAM_SIZE MAX_UDP_DGRAM_SIZE
61 #endif
62 /* NOTE: x86_64 (AMD) kernel does not allow dgrams bigger than MTU minus
63 * small overhead; for these we use the script and pass the MTU via argv.
64 */
65
66 #define DEFAULT_PORT 55555
67
68
69 static unsigned short s_MTU = MAX_DGRAM_SIZE;
70
71
s_Usage(const char * prog)72 static int s_Usage(const char* prog)
73 {
74 CORE_LOGF(eLOG_Error, ("Usage:\n%s {client|server} [port [mtu [seed]]]",
75 prog));
76 return 1;
77 }
78
79
s_Server(const char * sport)80 static int s_Server(const char* sport)
81 {
82 int i;
83 char* buf;
84 unsigned int host;
85 unsigned short port;
86 char addr[32];
87 SOCK server;
88 EIO_Status status;
89 STimeout timeout;
90 char minibuf[255];
91 size_t msglen, n, len;
92
93 if ((status = DSOCK_Create(&server)) != eIO_Success) {
94 CORE_LOGF(eLOG_Error, ("[Server] Cannot create DSOCK: %s",
95 IO_StatusStr(status)));
96 return 1;
97 }
98 if (sscanf(sport, "%hu%n", &port, &i) < 1 || sport[i]) {
99 port = 0;
100 i = 0;
101 }
102 if ((status = DSOCK_Bind(server, port)) != eIO_Success) {
103 CORE_LOGF(eLOG_Error, ("[Server] Cannot bind DSOCK to port %hu: %s",
104 port, IO_StatusStr(status)));
105 return 1;
106 }
107 if (!port && sport[i]) {
108 FILE* fp;
109 port = SOCK_GetLocalPort(server, eNH_HostByteOrder);
110 if (port && (fp = fopen(sport, "w")) != 0) {
111 if (fprintf(fp, "%hu", port) < 1 || fflush(fp) != 0)
112 status = eIO_Unknown;
113 fclose(fp);
114 } else
115 status = eIO_Unknown;
116 }
117
118 CORE_LOGF(eLOG_Note, ("[Server] DSOCK on port %hu", port));
119 assert(status == eIO_Success);
120
121 for (;;) {
122 if ((status = DSOCK_WaitMsg(server, 0/*infinite*/)) != eIO_Success) {
123 CORE_LOGF(eLOG_Error, ("[Server] Failed waiting on DSOCK: %s",
124 IO_StatusStr(status)));
125 break;
126 }
127
128 timeout.sec = 0;
129 timeout.usec = 0;
130 if ((status = SOCK_SetTimeout(server, eIO_Read, &timeout))
131 != eIO_Success) {
132 CORE_LOGF(eLOG_Error,("[Server] Cannot set zero read timeout: %s",
133 IO_StatusStr(status)));
134 break;
135 }
136
137 len = (size_t)(((double) rand()/(double) RAND_MAX)*sizeof(minibuf));
138 if ((status = DSOCK_RecvMsg(server, minibuf, len, 0, &msglen,
139 &host, &port)) != eIO_Success) {
140 CORE_LOGF(eLOG_Error, ("[Server] Cannot read from DSOCK: %s",
141 IO_StatusStr(status)));
142 continue;
143 }
144 if (len > msglen)
145 len = msglen;
146
147 if (SOCK_ntoa(host, addr, sizeof(addr)) != 0)
148 strcpy(addr, "<unknown>");
149
150 CORE_LOGF(eLOG_Note, ("[Server] Message received from %s:%hu, "
151 "%lu bytes",
152 addr, port, (unsigned long) msglen));
153
154 if (!(buf = (char*) malloc(msglen < 10 ? 10 : msglen))) {
155 CORE_LOG_ERRNO(eLOG_Error, errno,
156 "[Server] Cannot allocate message buffer");
157 break;
158 }
159 if (len)
160 memcpy(buf, minibuf, len);
161
162 while (len < msglen) {
163 n = (size_t)(((double)rand()/(double)RAND_MAX)*(msglen-len) + 0.5);
164 if ((status = SOCK_Read(server, buf + len, n, &n, eIO_ReadPlain))
165 != eIO_Success) {
166 CORE_LOGF(eLOG_Error,("[Server] Cannot read msg @ byte %lu:"
167 " %s", (unsigned long) len,
168 IO_StatusStr(status)));
169 free(buf);
170 continue;
171 }
172 len += n;
173 }
174 assert(SOCK_Read(server, 0, 1, &n, eIO_ReadPlain) == eIO_Closed);
175 assert(memcmp(buf + msglen - 10, "\0\0\0\0\0\0\0\0\0", 10) == 0);
176
177 CORE_LOG(eLOG_Note, "[Server] Bouncing the message to sender");
178
179 timeout.sec = 1;
180 timeout.usec = 0;
181 if ((status = SOCK_SetTimeout(server, eIO_Write, &timeout))
182 != eIO_Success){
183 CORE_LOGF(eLOG_Error, ("[Server] Cannot set write timeout: %s",
184 IO_StatusStr(status)));
185 break;
186 }
187
188 msglen -= 10;
189 for (len = 0; len < msglen; len += n) {
190 n = (size_t)(((double)rand()/(double)RAND_MAX)*(msglen-len) + 0.5);
191 if ((status = SOCK_Write(server, buf + len, n, &n, eIO_WritePlain))
192 != eIO_Success) {
193 CORE_LOGF(eLOG_Error,("[Server] Cannot write msg @ byte %lu:"
194 " %s", (unsigned long) len,
195 IO_StatusStr(status)));
196 break;
197 }
198 }
199
200 free(buf);
201
202 if ((status = DSOCK_SendMsg(server, addr, port, "--Reply--", 10))
203 != eIO_Success) {
204 CORE_LOGF(eLOG_Error, ("[Server] Cannot send to DSOCK: %s",
205 IO_StatusStr(status)));
206 /*continue*/;
207 }
208 }
209
210 /* On errors control reaches here */
211 if ((status = SOCK_Close(server)) != eIO_Success) {
212 CORE_LOGF(eLOG_Error, ("[Server] Cannot close DSOCK: %s",
213 IO_StatusStr(status)));
214 }
215 return 1;
216 }
217
218
s_Client(int x_port,unsigned int max_try)219 static int s_Client(int x_port, unsigned int max_try)
220 {
221 size_t msglen, n;
222 STimeout timeout;
223 EIO_Status status;
224 SOCK client;
225 unsigned short port;
226 char* buf;
227 unsigned long id;
228 unsigned int m;
229
230 if (x_port <= 0) {
231 CORE_LOGF(eLOG_Error, ("[Client] Port malformed (%d)", x_port));
232 return 1;
233 }
234
235 if ((status = DSOCK_Create(&client)) != eIO_Success) {
236 CORE_LOGF(eLOG_Error, ("[Client] Cannot create DSOCK: %s",
237 IO_StatusStr(status)));
238 return 1;
239 }
240 port = (unsigned short) x_port;
241
242 CORE_LOGF(eLOG_Note, ("[Client] DSOCK on port %hu", port));
243
244 msglen = (size_t)(((double)rand()/(double)RAND_MAX) * s_MTU);
245 if (msglen < sizeof(time_t) + 10)
246 msglen = sizeof(time_t) + 10;
247 if (msglen == MAX_UDP_DGRAM_SIZE)
248 msglen--;
249
250 CORE_LOGF(eLOG_Note, ("[Client] Generating a message %lu bytes long",
251 (unsigned long) msglen));
252
253 if (!(buf = (char*) malloc(2 * msglen))) {
254 CORE_LOG_ERRNO(eLOG_Error, errno,
255 "[Client] Cannot allocate message buffer");
256 SOCK_Close(client);
257 return 1;
258 }
259
260 for (n = sizeof(unsigned long); n < msglen - 10; n++)
261 buf[n] = rand() % 0xFF;
262 memcpy(buf + msglen - 10, "\0\0\0\0\0\0\0\0\0", 10);
263
264 id = (unsigned long) time(0);
265
266 for (m = 1; m <= max_try; m++) {
267 unsigned long tmp;
268 unsigned int k;
269
270 if (m != 1)
271 CORE_LOGF(eLOG_Note, ("[Client] Attempt #%u", (unsigned int) m));
272 id++;
273
274 *((unsigned long*) buf) = SOCK_HostToNetLong((unsigned long) id);
275
276 if ((status = DSOCK_SendMsg(client, "127.0.0.1", port, buf, msglen))
277 != eIO_Success) {
278 CORE_LOGF(eLOG_Error, ("[Client] Cannot send to DSOCK: %s",
279 IO_StatusStr(status)));
280 SOCK_Close(client);
281 return 1;
282 }
283
284 timeout.sec = 1;
285 timeout.usec = 0;
286 if ((status = SOCK_SetTimeout(client, eIO_Read, &timeout))
287 != eIO_Success) {
288 CORE_LOGF(eLOG_Error, ("[Client] Cannot set read timeout: %s",
289 IO_StatusStr(status)));
290 SOCK_Close(client);
291 return 1;
292 }
293
294 k = 0;
295 again:
296 if ((status = DSOCK_RecvMsg(client, buf + msglen, msglen, 0, &n, 0, 0))
297 != eIO_Success) {
298 CORE_LOGF(eLOG_Error, ("[Client] Cannot read from DSOCK: %s",
299 IO_StatusStr(status)));
300 continue;
301 }
302
303 if (n != msglen) {
304 CORE_LOGF(eLOG_Error, ("[Client] Received message of wrong size: "
305 "%lu", (unsigned long) n));
306 SOCK_Close(client);
307 return 1;
308 }
309
310 memcpy(&tmp, buf + msglen, sizeof(tmp));
311 if (SOCK_NetToHostLong(tmp) != id) {
312 k++;
313 CORE_LOGF(k < max_try ? eLOG_Warning : eLOG_Error,
314 ("[Client] Stale message received%s",
315 k < max_try ? ", reattempting to fetch" : ""));
316 if (k < max_try)
317 goto again;
318 break;
319 }
320
321 CORE_LOGF(eLOG_Note, ("[Client] Received the message back, %lu bytes",
322 (unsigned long) n));
323 assert(SOCK_Read(client, 0, 1, &n, eIO_ReadPlain) == eIO_Closed);
324 break;
325 }
326 if (m > max_try) {
327 SOCK_Close(client);
328 return 1;
329 }
330
331 for (n = sizeof(unsigned long); n < msglen - 10; n++) {
332 if (buf[n] != buf[msglen + n])
333 break;
334 }
335
336 if (n < msglen - 10) {
337 CORE_LOGF(eLOG_Error, ("[Client] Bounced message corrupted, off=%lu",
338 (unsigned long) n));
339 SOCK_Close(client);
340 return 1;
341 }
342
343 if (strcmp(buf + msglen*2 - 10, "--Reply--") != 0) {
344 CORE_LOGF(eLOG_Error, ("[Client] No signature in the message: %.9s",
345 buf + msglen*2 - 10));
346 SOCK_Close(client);
347 return 1;
348 }
349
350 free(buf);
351
352 if ((status = SOCK_Close(client)) != eIO_Success) {
353 CORE_LOGF(eLOG_Error, ("[Client] Cannot close DSOCK: %s",
354 IO_StatusStr(status)));
355 return 1;
356 }
357
358 CORE_LOG(eLOG_Note, "TEST completed successfully");
359 CORE_SetLOG(0);
360 return 0;
361 }
362
363
364 #define _STR(x) #x
365 #define STR(x) _STR(x)
366
367
main(int argc,const char * argv[])368 int main(int argc, const char* argv[])
369 {
370 unsigned short max_try;
371 SConnNetInfo* net_info;
372
373 CORE_SetLOGFormatFlags(fLOG_None | fLOG_Level |
374 fLOG_OmitNoteLevel | fLOG_DateTime);
375 CORE_SetLOGFILE(stderr, 0/*false*/);
376
377 if (argc < 2 || argc > 5)
378 return s_Usage(argv[0]);
379
380 if (argc <= 4)
381 g_NCBI_ConnectRandomSeed = (int) time(0) ^ NCBI_CONNECT_SRAND_ADDEND;
382 else
383 g_NCBI_ConnectRandomSeed = atoi(argv[4]);
384 CORE_LOGF(eLOG_Note, ("Random SEED = %u", g_NCBI_ConnectRandomSeed));
385 srand(g_NCBI_ConnectRandomSeed);
386
387 assert((net_info = ConnNetInfo_Create(0)) != 0);
388 if (net_info->debug_printout)
389 SOCK_SetDataLoggingAPI(eOn);
390 max_try = net_info->max_try;
391 ConnNetInfo_Destroy(net_info);
392
393 if (argc > 3) {
394 int mtu = atoi(argv[3]);
395 if (mtu > 32 && mtu < (int) s_MTU)
396 s_MTU = mtu - 32/*small protocol (IP/UDP) overhead*/;
397 }
398
399 if (strcasecmp(argv[1], "client") == 0) {
400 return s_Client(argv[2] ? atoi(argv[2]) : DEFAULT_PORT, max_try);
401 }
402 if (strcasecmp(argv[1], "server") == 0)
403 return s_Server(argv[2] ? argv[2] : STR(DEFAULT_PORT));
404
405 return s_Usage(argv[0]);
406 }
407