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 /***********************************************************************
7 **
8 ** Name: tmocon.c
9 **
10 ** Description: test client socket connection.
11 **
12 ** Modification History:
13 ** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
14 ** The debug mode will print all of the printfs associated with this test.
15 ** The regress mode will be the default mode. Since the regress tool limits
16 ** the output to a one line status:PASS or FAIL,all of the printf statements
17 ** have been handled with an if (debug_mode) statement.
18 ***********************************************************************/
19
20 /***********************************************************************
21 ** Includes
22 ***********************************************************************/
23 /* Used to get the command line option */
24 #include "plgetopt.h"
25
26 #include "nspr.h"
27 #include "pprio.h"
28
29 #include "plerror.h"
30 #include "plgetopt.h"
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 /* for getcwd */
37 #if defined(XP_UNIX) || defined (XP_OS2)
38 #include <unistd.h>
39 #elif defined(XP_PC)
40 #include <direct.h>
41 #endif
42
43 #ifdef WINCE
44 #include <windows.h>
getcwd(char * buf,size_t size)45 char *getcwd(char *buf, size_t size)
46 {
47 wchar_t wpath[MAX_PATH];
48 _wgetcwd(wpath, MAX_PATH);
49 WideCharToMultiByte(CP_ACP, 0, wpath, -1, buf, size, 0, 0);
50 }
51 #endif
52
53 #ifdef DEBUG
54 #define PORT_INC_DO +100
55 #else
56 #define PORT_INC_DO
57 #endif
58 #ifdef IS_64
59 #define PORT_INC_3264 +200
60 #else
61 #define PORT_INC_3264
62 #endif
63
64 #define BASE_PORT 9867 PORT_INC_DO PORT_INC_3264
65
66 #define DEFAULT_DALLY 1
67 #define DEFAULT_THREADS 1
68 #define DEFAULT_TIMEOUT 10
69 #define DEFAULT_MESSAGES 100
70 #define DEFAULT_MESSAGESIZE 100
71
72 static PRFileDesc *debug_out = NULL;
73
74 typedef struct Shared
75 {
76 PRBool random;
77 PRBool failed;
78 PRBool intermittant;
79 PRIntn debug;
80 PRInt32 messages;
81 PRIntervalTime dally;
82 PRIntervalTime timeout;
83 PRInt32 message_length;
84 PRNetAddr serverAddress;
85 } Shared;
86
Timeout(const Shared * shared)87 static PRIntervalTime Timeout(const Shared *shared)
88 {
89 PRIntervalTime timeout = shared->timeout;
90 if (shared->random)
91 {
92 PRIntervalTime quarter = timeout >> 2; /* one quarter of the interval */
93 PRUint32 random = rand() % quarter; /* something in[0..timeout / 4) */
94 timeout = (((3 * quarter) + random) >> 2) + quarter; /* [75..125)% */
95 }
96 return timeout;
97 } /* Timeout */
98
CauseTimeout(const Shared * shared)99 static void CauseTimeout(const Shared *shared)
100 {
101 if (shared->intermittant) {
102 PR_Sleep(Timeout(shared));
103 }
104 } /* CauseTimeout */
105
MakeReceiver(Shared * shared)106 static PRStatus MakeReceiver(Shared *shared)
107 {
108 PRStatus rv = PR_FAILURE;
109 if (PR_IsNetAddrType(&shared->serverAddress, PR_IpAddrLoopback))
110 {
111 char *argv[3];
112 char path[1024 + sizeof("/tmoacc")];
113
114 getcwd(path, sizeof(path));
115
116 (void)strcat(path, "/tmoacc");
117 #ifdef XP_PC
118 (void)strcat(path, ".exe");
119 #endif
120 argv[0] = path;
121 if (shared->debug > 0)
122 {
123 argv[1] = "-d";
124 argv[2] = NULL;
125 }
126 else {
127 argv[1] = NULL;
128 }
129 if (shared->debug > 1) {
130 PR_fprintf(debug_out, " creating accept process %s ...", path);
131 }
132 fflush(stdout);
133 rv = PR_CreateProcessDetached(path, argv, NULL, NULL);
134 if (PR_SUCCESS == rv)
135 {
136 if (shared->debug > 1) {
137 PR_fprintf(debug_out, " wait 5 seconds");
138 }
139 if (shared->debug > 1) {
140 PR_fprintf(debug_out, " before connecting to accept process ...");
141 }
142 fflush(stdout);
143 PR_Sleep(PR_SecondsToInterval(5));
144 return rv;
145 }
146 shared->failed = PR_TRUE;
147 if (shared->debug > 0) {
148 PL_FPrintError(debug_out, "PR_CreateProcessDetached failed");
149 }
150 }
151 return rv;
152 } /* MakeReceiver */
153
Connect(void * arg)154 static void Connect(void *arg)
155 {
156 PRStatus rv;
157 char *buffer = NULL;
158 PRFileDesc *clientSock;
159 Shared *shared = (Shared*)arg;
160 PRInt32 loop, bytes, flags = 0;
161 struct Descriptor {
162 PRInt32 length;
163 PRUint32 checksum;
164 } descriptor;
165 debug_out = (0 == shared->debug) ? NULL : PR_GetSpecialFD(PR_StandardError);
166
167 buffer = (char*)PR_MALLOC(shared->message_length);
168
169 for (bytes = 0; bytes < shared->message_length; ++bytes) {
170 buffer[bytes] = (char)bytes;
171 }
172
173 descriptor.checksum = 0;
174 for (bytes = 0; bytes < shared->message_length; ++bytes)
175 {
176 PRUint32 overflow = descriptor.checksum & 0x80000000;
177 descriptor.checksum = (descriptor.checksum << 1);
178 if (0x00000000 != overflow) {
179 descriptor.checksum += 1;
180 }
181 descriptor.checksum += buffer[bytes];
182 }
183 descriptor.checksum = PR_htonl(descriptor.checksum);
184
185 for (loop = 0; loop < shared->messages; ++loop)
186 {
187 if (shared->debug > 1) {
188 PR_fprintf(debug_out, "[%d]socket ... ", loop);
189 }
190 clientSock = PR_NewTCPSocket();
191 if (clientSock)
192 {
193 /*
194 * We need to slow down the rate of generating connect requests,
195 * otherwise the listen backlog queue on the accept side may
196 * become full and we will get connection refused or timeout
197 * error.
198 */
199
200 PR_Sleep(shared->dally);
201 if (shared->debug > 1)
202 {
203 char buf[128];
204 PR_NetAddrToString(&shared->serverAddress, buf, sizeof(buf));
205 PR_fprintf(debug_out, "connecting to %s ... ", buf);
206 }
207 rv = PR_Connect(
208 clientSock, &shared->serverAddress, Timeout(shared));
209 if (PR_SUCCESS == rv)
210 {
211 PRInt32 descriptor_length = (loop < (shared->messages - 1)) ?
212 shared->message_length : 0;
213 descriptor.length = PR_htonl(descriptor_length);
214 if (shared->debug > 1)
215 PR_fprintf(
216 debug_out, "sending %d bytes ... ", descriptor_length);
217 CauseTimeout(shared); /* might cause server to timeout */
218 bytes = PR_Send(
219 clientSock, &descriptor, sizeof(descriptor),
220 flags, Timeout(shared));
221 if (bytes != sizeof(descriptor))
222 {
223 shared->failed = PR_TRUE;
224 if (shared->debug > 0) {
225 PL_FPrintError(debug_out, "PR_Send failed");
226 }
227 }
228 if (0 != descriptor_length)
229 {
230 CauseTimeout(shared);
231 bytes = PR_Send(
232 clientSock, buffer, descriptor_length,
233 flags, Timeout(shared));
234 if (bytes != descriptor_length)
235 {
236 shared->failed = PR_TRUE;
237 if (shared->debug > 0) {
238 PL_FPrintError(debug_out, "PR_Send failed");
239 }
240 }
241 }
242 if (shared->debug > 1) {
243 PR_fprintf(debug_out, "closing ... ");
244 }
245 rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH);
246 rv = PR_Close(clientSock);
247 if (shared->debug > 1)
248 {
249 if (PR_SUCCESS == rv) {
250 PR_fprintf(debug_out, "\n");
251 }
252 else {
253 PL_FPrintError(debug_out, "shutdown failed");
254 }
255 }
256 }
257 else
258 {
259 if (shared->debug > 1) {
260 PL_FPrintError(debug_out, "connect failed");
261 }
262 PR_Close(clientSock);
263 if ((loop == 0) && (PR_GetError() == PR_CONNECT_REFUSED_ERROR))
264 {
265 if (MakeReceiver(shared) == PR_FAILURE) {
266 break;
267 }
268 }
269 else
270 {
271 if (shared->debug > 1) {
272 PR_fprintf(debug_out, " exiting\n");
273 }
274 break;
275 }
276 }
277 }
278 else
279 {
280 shared->failed = PR_TRUE;
281 if (shared->debug > 0) {
282 PL_FPrintError(debug_out, "create socket");
283 }
284 break;
285 }
286 }
287
288 PR_DELETE(buffer);
289 } /* Connect */
290
Tmocon(int argc,char ** argv)291 int Tmocon(int argc, char **argv)
292 {
293 /*
294 * USAGE
295 * -d turn on debugging output (default = off)
296 * -v turn on verbose output (default = off)
297 * -h <n> dns name of host serving the connection (default = self)
298 * -i dally intermittantly to cause timeouts (default = off)
299 * -m <n> number of messages to send (default = 100)
300 * -s <n> size of each message (default = 100)
301 * -t <n> number of threads sending (default = 1)
302 * -G use global threads (default = local)
303 * -T <n> timeout on I/O operations (seconds) (default = 10)
304 * -D <n> dally between connect requests (seconds)(default = 0)
305 * -R randomize the dally types around 'T' (default = no)
306 */
307
308 PRStatus rv;
309 int exitStatus;
310 PLOptStatus os;
311 Shared *shared = NULL;
312 PRThread **thread = NULL;
313 PRIntn index, threads = DEFAULT_THREADS;
314 PRThreadScope thread_scope = PR_LOCAL_THREAD;
315 PRInt32 dally = DEFAULT_DALLY, timeout = DEFAULT_TIMEOUT;
316 PLOptState *opt = PL_CreateOptState(argc, argv, "divGRh:m:s:t:T:D:");
317
318 shared = PR_NEWZAP(Shared);
319
320 shared->debug = 0;
321 shared->failed = PR_FALSE;
322 shared->random = PR_FALSE;
323 shared->messages = DEFAULT_MESSAGES;
324 shared->message_length = DEFAULT_MESSAGESIZE;
325
326 PR_STDIO_INIT();
327 memset(&shared->serverAddress, 0, sizeof(shared->serverAddress));
328 rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &shared->serverAddress);
329 PR_ASSERT(PR_SUCCESS == rv);
330
331 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
332 {
333 if (PL_OPT_BAD == os) {
334 continue;
335 }
336 switch (opt->option)
337 {
338 case 'd':
339 if (0 == shared->debug) {
340 shared->debug = 1;
341 }
342 break;
343 case 'v':
344 if (0 == shared->debug) {
345 shared->debug = 2;
346 }
347 break;
348 case 'i':
349 shared->intermittant = PR_TRUE;
350 break;
351 case 'R':
352 shared->random = PR_TRUE;
353 break;
354 case 'G':
355 thread_scope = PR_GLOBAL_THREAD;
356 break;
357 case 'h': /* the value for backlock */
358 {
359 PRIntn es = 0;
360 PRHostEnt host;
361 char buffer[1024];
362 (void)PR_GetHostByName(
363 opt->value, buffer, sizeof(buffer), &host);
364 es = PR_EnumerateHostEnt(
365 es, &host, BASE_PORT, &shared->serverAddress);
366 PR_ASSERT(es > 0);
367 }
368 break;
369 case 'm': /* number of messages to send */
370 shared->messages = atoi(opt->value);
371 break;
372 case 't': /* number of threads sending */
373 threads = atoi(opt->value);
374 break;
375 case 'D': /* dally time between transmissions */
376 dally = atoi(opt->value);
377 break;
378 case 'T': /* timeout on I/O operations */
379 timeout = atoi(opt->value);
380 break;
381 case 's': /* total size of each message */
382 shared->message_length = atoi(opt->value);
383 break;
384 default:
385 break;
386 }
387 }
388 PL_DestroyOptState(opt);
389
390 if (0 == timeout) {
391 timeout = DEFAULT_TIMEOUT;
392 }
393 if (0 == threads) {
394 threads = DEFAULT_THREADS;
395 }
396 if (0 == shared->messages) {
397 shared->messages = DEFAULT_MESSAGES;
398 }
399 if (0 == shared->message_length) {
400 shared->message_length = DEFAULT_MESSAGESIZE;
401 }
402
403 shared->dally = PR_SecondsToInterval(dally);
404 shared->timeout = PR_SecondsToInterval(timeout);
405
406 thread = (PRThread**)PR_CALLOC(threads * sizeof(PRThread*));
407
408 for (index = 0; index < threads; ++index)
409 thread[index] = PR_CreateThread(
410 PR_USER_THREAD, Connect, shared,
411 PR_PRIORITY_NORMAL, thread_scope,
412 PR_JOINABLE_THREAD, 0);
413 for (index = 0; index < threads; ++index) {
414 rv = PR_JoinThread(thread[index]);
415 }
416
417 PR_DELETE(thread);
418
419 PR_fprintf(
420 PR_GetSpecialFD(PR_StandardError), "%s\n",
421 ((shared->failed) ? "FAILED" : "PASSED"));
422 exitStatus = (shared->failed) ? 1 : 0;
423 PR_DELETE(shared);
424 return exitStatus;
425 }
426
main(int argc,char ** argv)427 int main(int argc, char **argv)
428 {
429 return (PR_VersionCheck(PR_VERSION)) ?
430 PR_Initialize(Tmocon, argc, argv, 4) : -1;
431 } /* main */
432
433 /* tmocon.c */
434
435
436