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