1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "nspr.h"
7 
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #include "plerror.h"
12 #include "plgetopt.h"
13 
14 #ifdef DEBUG
15 #define PORT_INC_DO +100
16 #else
17 #define PORT_INC_DO
18 #endif
19 #ifdef IS_64
20 #define PORT_INC_3264 +200
21 #else
22 #define PORT_INC_3264
23 #endif
24 
25 #define BASE_PORT 9867 PORT_INC_DO PORT_INC_3264
26 #define DEFAULT_THREADS 1
27 #define DEFAULT_BACKLOG 10
28 #define DEFAULT_TIMEOUT 10
29 #define RANDOM_RANGE 100  /* should be significantly smaller than RAND_MAX */
30 
31 typedef enum {running, stopped} Status;
32 
33 typedef struct Shared
34 {
35     PRLock *ml;
36     PRCondVar *cv;
37     PRBool passed;
38     PRBool random;
39     PRFileDesc *debug;
40     PRIntervalTime timeout;
41     PRFileDesc *listenSock;
42     Status status;
43 } Shared;
44 
Timeout(const Shared * shared)45 static PRIntervalTime Timeout(const Shared *shared)
46 {
47     PRIntervalTime timeout = shared->timeout;
48     if (shared->random)
49     {
50         PRIntervalTime half = timeout >> 1;  /* one half of the interval */
51         PRIntervalTime quarter = half >> 1;  /* one quarter of the interval */
52         /* something in [0..timeout / 2) */
53         PRUint32 random = (rand() % RANDOM_RANGE) * half / RANDOM_RANGE;
54         timeout = (3 * quarter) + random;  /* [75..125)% */
55     }
56     return timeout;
57 }  /* Timeout */
58 
Accept(void * arg)59 static void Accept(void *arg)
60 {
61     PRStatus rv;
62     char *buffer = NULL;
63     PRNetAddr clientAddr;
64     Shared *shared = (Shared*)arg;
65     PRInt32 recv_length = 0, flags = 0;
66     PRFileDesc *clientSock;
67     PRIntn toread, byte, bytes, loop = 0;
68     struct Descriptor {
69         PRInt32 length;
70         PRUint32 checksum;
71     } descriptor;
72 
73     do
74     {
75         PRUint32 checksum = 0;
76         if (NULL != shared->debug) {
77             PR_fprintf(shared->debug, "[%d]accepting ... ", loop++);
78         }
79         clientSock = PR_Accept(
80                          shared->listenSock, &clientAddr, Timeout(shared));
81         if (clientSock != NULL)
82         {
83             if (NULL != shared->debug) {
84                 PR_fprintf(shared->debug, "reading length ... ");
85             }
86             bytes = PR_Recv(
87                         clientSock, &descriptor, sizeof(descriptor),
88                         flags, Timeout(shared));
89             if (sizeof(descriptor) == bytes)
90             {
91                 /* and, before doing something stupid ... */
92                 descriptor.length = PR_ntohl(descriptor.length);
93                 descriptor.checksum = PR_ntohl(descriptor.checksum);
94                 if (NULL != shared->debug) {
95                     PR_fprintf(shared->debug, "%d bytes ... ", descriptor.length);
96                 }
97                 toread = descriptor.length;
98                 if (recv_length < descriptor.length)
99                 {
100                     if (NULL != buffer) {
101                         PR_DELETE(buffer);
102                     }
103                     buffer = (char*)PR_MALLOC(descriptor.length);
104                     recv_length = descriptor.length;
105                 }
106                 for (toread = descriptor.length; toread > 0; toread -= bytes)
107                 {
108                     bytes = PR_Recv(
109                                 clientSock, &buffer[descriptor.length - toread],
110                                 toread, flags, Timeout(shared));
111                     if (-1 == bytes)
112                     {
113                         if (NULL != shared->debug) {
114                             PR_fprintf(shared->debug, "read data failed...");
115                         }
116                         bytes = 0;
117                     }
118                 }
119             }
120             else if (NULL != shared->debug)
121             {
122                 PR_fprintf(shared->debug, "read desciptor failed...");
123                 descriptor.length = -1;
124             }
125             if (NULL != shared->debug) {
126                 PR_fprintf(shared->debug, "closing");
127             }
128             rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH);
129             if ((PR_FAILURE == rv) && (NULL != shared->debug))
130             {
131                 PR_fprintf(shared->debug, " failed");
132                 shared->passed = PR_FALSE;
133             }
134             rv = PR_Close(clientSock);
135             if (PR_FAILURE == rv) if (NULL != shared->debug)
136                 {
137                     PR_fprintf(shared->debug, " failed");
138                     shared->passed = PR_FALSE;
139                 }
140             if (descriptor.length > 0)
141             {
142                 for (byte = 0; byte < descriptor.length; ++byte)
143                 {
144                     PRUint32 overflow = checksum & 0x80000000;
145                     checksum = (checksum << 1);
146                     if (0x00000000 != overflow) {
147                         checksum += 1;
148                     }
149                     checksum += buffer[byte];
150                 }
151                 if ((descriptor.checksum != checksum) && (NULL != shared->debug))
152                 {
153                     PR_fprintf(shared->debug, " ... data mismatch");
154                     shared->passed = PR_FALSE;
155                 }
156             }
157             else if (0 == descriptor.length)
158             {
159                 PR_Lock(shared->ml);
160                 shared->status = stopped;
161                 PR_NotifyCondVar(shared->cv);
162                 PR_Unlock(shared->ml);
163             }
164             if (NULL != shared->debug) {
165                 PR_fprintf(shared->debug, "\n");
166             }
167         }
168         else
169         {
170             if (PR_PENDING_INTERRUPT_ERROR != PR_GetError())
171             {
172                 if (NULL != shared->debug) {
173                     PL_PrintError("Accept");
174                 }
175                 shared->passed = PR_FALSE;
176             }
177         }
178     } while (running == shared->status);
179     if (NULL != buffer) {
180         PR_DELETE(buffer);
181     }
182 }  /* Accept */
183 
Tmoacc(PRIntn argc,char ** argv)184 PRIntn Tmoacc(PRIntn argc, char **argv)
185 {
186     PRStatus rv;
187     PRIntn exitStatus;
188     PRIntn index;
189     Shared *shared;
190     PLOptStatus os;
191     PRThread **thread;
192     PRNetAddr listenAddr;
193     PRSocketOptionData sockOpt;
194     PRIntn timeout = DEFAULT_TIMEOUT;
195     PRIntn threads = DEFAULT_THREADS;
196     PRIntn backlog = DEFAULT_BACKLOG;
197     PRThreadScope thread_scope = PR_LOCAL_THREAD;
198 
199     PLOptState *opt = PL_CreateOptState(argc, argv, "dGb:t:T:R");
200 
201     shared = PR_NEWZAP(Shared);
202 
203     shared->debug = NULL;
204     shared->passed = PR_TRUE;
205     shared->random = PR_TRUE;
206     shared->status = running;
207     shared->ml = PR_NewLock();
208     shared->cv = PR_NewCondVar(shared->ml);
209 
210     while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
211     {
212         if (PL_OPT_BAD == os) {
213             continue;
214         }
215         switch (opt->option)
216         {
217             case 'd':  /* debug mode */
218                 shared->debug = PR_GetSpecialFD(PR_StandardError);
219                 break;
220             case 'G':  /* use global threads */
221                 thread_scope = PR_GLOBAL_THREAD;
222                 break;
223             case 'b':  /* size of listen backlog */
224                 backlog = atoi(opt->value);
225                 break;
226             case 't':  /* number of threads doing accept */
227                 threads = atoi(opt->value);
228                 break;
229             case 'T':  /* timeout used for network operations */
230                 timeout = atoi(opt->value);
231                 break;
232             case 'R':  /* randomize the timeout values */
233                 shared->random = PR_TRUE;
234                 break;
235             default:
236                 break;
237         }
238     }
239     PL_DestroyOptState(opt);
240     if (0 == threads) {
241         threads = DEFAULT_THREADS;
242     }
243     if (0 == backlog) {
244         backlog = DEFAULT_BACKLOG;
245     }
246     if (0 == timeout) {
247         timeout = DEFAULT_TIMEOUT;
248     }
249 
250     PR_STDIO_INIT();
251     memset(&listenAddr, 0, sizeof(listenAddr));
252     rv = PR_InitializeNetAddr(PR_IpAddrAny, BASE_PORT, &listenAddr);
253     PR_ASSERT(PR_SUCCESS == rv);
254 
255     shared->timeout = PR_SecondsToInterval(timeout);
256 
257     /* First bind to the socket */
258     shared->listenSock = PR_NewTCPSocket();
259     if (shared->listenSock)
260     {
261         sockOpt.option = PR_SockOpt_Reuseaddr;
262         sockOpt.value.reuse_addr = PR_TRUE;
263         rv = PR_SetSocketOption(shared->listenSock, &sockOpt);
264         PR_ASSERT(PR_SUCCESS == rv);
265         rv = PR_Bind(shared->listenSock, &listenAddr);
266         if (rv != PR_FAILURE)
267         {
268             rv = PR_Listen(shared->listenSock, threads + backlog);
269             if (PR_SUCCESS == rv)
270             {
271                 thread = (PRThread**)PR_CALLOC(threads * sizeof(PRThread*));
272                 for (index = 0; index < threads; ++index)
273                 {
274                     thread[index] = PR_CreateThread(
275                                         PR_USER_THREAD, Accept, shared,
276                                         PR_PRIORITY_NORMAL, thread_scope,
277                                         PR_JOINABLE_THREAD, 0);
278                     PR_ASSERT(NULL != thread[index]);
279                 }
280 
281                 PR_Lock(shared->ml);
282                 while (shared->status == running) {
283                     PR_WaitCondVar(shared->cv, PR_INTERVAL_NO_TIMEOUT);
284                 }
285                 PR_Unlock(shared->ml);
286                 for (index = 0; index < threads; ++index)
287                 {
288                     rv = PR_Interrupt(thread[index]);
289                     PR_ASSERT(PR_SUCCESS== rv);
290                     rv = PR_JoinThread(thread[index]);
291                     PR_ASSERT(PR_SUCCESS== rv);
292                 }
293                 PR_DELETE(thread);
294             }
295             else
296             {
297                 if (shared->debug) {
298                     PL_PrintError("Listen");
299                 }
300                 shared->passed = PR_FALSE;
301             }
302         }
303         else
304         {
305             if (shared->debug) {
306                 PL_PrintError("Bind");
307             }
308             shared->passed = PR_FALSE;
309         }
310 
311         PR_Close(shared->listenSock);
312     }
313     else
314     {
315         if (shared->debug) {
316             PL_PrintError("Create");
317         }
318         shared->passed = PR_FALSE;
319     }
320 
321     PR_DestroyCondVar(shared->cv);
322     PR_DestroyLock(shared->ml);
323 
324     PR_fprintf(
325         PR_GetSpecialFD(PR_StandardError), "%s\n",
326         ((shared->passed) ? "PASSED" : "FAILED"));
327 
328     exitStatus = (shared->passed) ? 0 : 1;
329     PR_DELETE(shared);
330     return exitStatus;
331 }
332 
main(int argc,char ** argv)333 int main(int argc, char **argv)
334 {
335     return (PR_VersionCheck(PR_VERSION)) ?
336            PR_Initialize(Tmoacc, argc, argv, 4) : -1;
337 }  /* main */
338 
339 /* tmoacc */
340