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