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