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