/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /*********************************************************************** ** ** Name: tmocon.c ** ** Description: test client socket connection. ** ** Modification History: ** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. ** The debug mode will print all of the printfs associated with this test. ** The regress mode will be the default mode. Since the regress tool limits ** the output to a one line status:PASS or FAIL,all of the printf statements ** have been handled with an if (debug_mode) statement. ***********************************************************************/ /*********************************************************************** ** Includes ***********************************************************************/ /* Used to get the command line option */ #include "plgetopt.h" #include "nspr.h" #include "pprio.h" #include "plerror.h" #include "plgetopt.h" #include #include #include /* for getcwd */ #if defined(XP_UNIX) || defined (XP_OS2) #include #elif defined(XP_PC) #include #endif #ifdef WINCE #include char *getcwd(char *buf, size_t size) { wchar_t wpath[MAX_PATH]; _wgetcwd(wpath, MAX_PATH); WideCharToMultiByte(CP_ACP, 0, wpath, -1, buf, size, 0, 0); } #endif #ifdef DEBUG #define PORT_INC_DO +100 #else #define PORT_INC_DO #endif #ifdef IS_64 #define PORT_INC_3264 +200 #else #define PORT_INC_3264 #endif #define BASE_PORT 9867 PORT_INC_DO PORT_INC_3264 #define DEFAULT_DALLY 1 #define DEFAULT_THREADS 1 #define DEFAULT_TIMEOUT 10 #define DEFAULT_MESSAGES 100 #define DEFAULT_MESSAGESIZE 100 static PRFileDesc *debug_out = NULL; typedef struct Shared { PRBool random; PRBool failed; PRBool intermittant; PRIntn debug; PRInt32 messages; PRIntervalTime dally; PRIntervalTime timeout; PRInt32 message_length; PRNetAddr serverAddress; } Shared; static PRIntervalTime Timeout(const Shared *shared) { PRIntervalTime timeout = shared->timeout; if (shared->random) { PRIntervalTime quarter = timeout >> 2; /* one quarter of the interval */ PRUint32 random = rand() % quarter; /* something in[0..timeout / 4) */ timeout = (((3 * quarter) + random) >> 2) + quarter; /* [75..125)% */ } return timeout; } /* Timeout */ static void CauseTimeout(const Shared *shared) { if (shared->intermittant) { PR_Sleep(Timeout(shared)); } } /* CauseTimeout */ static PRStatus MakeReceiver(Shared *shared) { PRStatus rv = PR_FAILURE; if (PR_IsNetAddrType(&shared->serverAddress, PR_IpAddrLoopback)) { char *argv[3]; char path[1024 + sizeof("/tmoacc")]; getcwd(path, sizeof(path)); (void)strcat(path, "/tmoacc"); #ifdef XP_PC (void)strcat(path, ".exe"); #endif argv[0] = path; if (shared->debug > 0) { argv[1] = "-d"; argv[2] = NULL; } else { argv[1] = NULL; } if (shared->debug > 1) { PR_fprintf(debug_out, " creating accept process %s ...", path); } fflush(stdout); rv = PR_CreateProcessDetached(path, argv, NULL, NULL); if (PR_SUCCESS == rv) { if (shared->debug > 1) { PR_fprintf(debug_out, " wait 5 seconds"); } if (shared->debug > 1) { PR_fprintf(debug_out, " before connecting to accept process ..."); } fflush(stdout); PR_Sleep(PR_SecondsToInterval(5)); return rv; } shared->failed = PR_TRUE; if (shared->debug > 0) { PL_FPrintError(debug_out, "PR_CreateProcessDetached failed"); } } return rv; } /* MakeReceiver */ static void Connect(void *arg) { PRStatus rv; char *buffer = NULL; PRFileDesc *clientSock; Shared *shared = (Shared*)arg; PRInt32 loop, bytes, flags = 0; struct Descriptor { PRInt32 length; PRUint32 checksum; } descriptor; debug_out = (0 == shared->debug) ? NULL : PR_GetSpecialFD(PR_StandardError); buffer = (char*)PR_MALLOC(shared->message_length); for (bytes = 0; bytes < shared->message_length; ++bytes) { buffer[bytes] = (char)bytes; } descriptor.checksum = 0; for (bytes = 0; bytes < shared->message_length; ++bytes) { PRUint32 overflow = descriptor.checksum & 0x80000000; descriptor.checksum = (descriptor.checksum << 1); if (0x00000000 != overflow) { descriptor.checksum += 1; } descriptor.checksum += buffer[bytes]; } descriptor.checksum = PR_htonl(descriptor.checksum); for (loop = 0; loop < shared->messages; ++loop) { if (shared->debug > 1) { PR_fprintf(debug_out, "[%d]socket ... ", loop); } clientSock = PR_NewTCPSocket(); if (clientSock) { /* * We need to slow down the rate of generating connect requests, * otherwise the listen backlog queue on the accept side may * become full and we will get connection refused or timeout * error. */ PR_Sleep(shared->dally); if (shared->debug > 1) { char buf[128]; PR_NetAddrToString(&shared->serverAddress, buf, sizeof(buf)); PR_fprintf(debug_out, "connecting to %s ... ", buf); } rv = PR_Connect( clientSock, &shared->serverAddress, Timeout(shared)); if (PR_SUCCESS == rv) { PRInt32 descriptor_length = (loop < (shared->messages - 1)) ? shared->message_length : 0; descriptor.length = PR_htonl(descriptor_length); if (shared->debug > 1) PR_fprintf( debug_out, "sending %d bytes ... ", descriptor_length); CauseTimeout(shared); /* might cause server to timeout */ bytes = PR_Send( clientSock, &descriptor, sizeof(descriptor), flags, Timeout(shared)); if (bytes != sizeof(descriptor)) { shared->failed = PR_TRUE; if (shared->debug > 0) { PL_FPrintError(debug_out, "PR_Send failed"); } } if (0 != descriptor_length) { CauseTimeout(shared); bytes = PR_Send( clientSock, buffer, descriptor_length, flags, Timeout(shared)); if (bytes != descriptor_length) { shared->failed = PR_TRUE; if (shared->debug > 0) { PL_FPrintError(debug_out, "PR_Send failed"); } } } if (shared->debug > 1) { PR_fprintf(debug_out, "closing ... "); } rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH); rv = PR_Close(clientSock); if (shared->debug > 1) { if (PR_SUCCESS == rv) { PR_fprintf(debug_out, "\n"); } else { PL_FPrintError(debug_out, "shutdown failed"); } } } else { if (shared->debug > 1) { PL_FPrintError(debug_out, "connect failed"); } PR_Close(clientSock); if ((loop == 0) && (PR_GetError() == PR_CONNECT_REFUSED_ERROR)) { if (MakeReceiver(shared) == PR_FAILURE) { break; } } else { if (shared->debug > 1) { PR_fprintf(debug_out, " exiting\n"); } break; } } } else { shared->failed = PR_TRUE; if (shared->debug > 0) { PL_FPrintError(debug_out, "create socket"); } break; } } PR_DELETE(buffer); } /* Connect */ int Tmocon(int argc, char **argv) { /* * USAGE * -d turn on debugging output (default = off) * -v turn on verbose output (default = off) * -h dns name of host serving the connection (default = self) * -i dally intermittantly to cause timeouts (default = off) * -m number of messages to send (default = 100) * -s size of each message (default = 100) * -t number of threads sending (default = 1) * -G use global threads (default = local) * -T timeout on I/O operations (seconds) (default = 10) * -D dally between connect requests (seconds)(default = 0) * -R randomize the dally types around 'T' (default = no) */ PRStatus rv; int exitStatus; PLOptStatus os; Shared *shared = NULL; PRThread **thread = NULL; PRIntn index, threads = DEFAULT_THREADS; PRThreadScope thread_scope = PR_LOCAL_THREAD; PRInt32 dally = DEFAULT_DALLY, timeout = DEFAULT_TIMEOUT; PLOptState *opt = PL_CreateOptState(argc, argv, "divGRh:m:s:t:T:D:"); shared = PR_NEWZAP(Shared); shared->debug = 0; shared->failed = PR_FALSE; shared->random = PR_FALSE; shared->messages = DEFAULT_MESSAGES; shared->message_length = DEFAULT_MESSAGESIZE; PR_STDIO_INIT(); memset(&shared->serverAddress, 0, sizeof(shared->serverAddress)); rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &shared->serverAddress); PR_ASSERT(PR_SUCCESS == rv); while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { if (PL_OPT_BAD == os) { continue; } switch (opt->option) { case 'd': if (0 == shared->debug) { shared->debug = 1; } break; case 'v': if (0 == shared->debug) { shared->debug = 2; } break; case 'i': shared->intermittant = PR_TRUE; break; case 'R': shared->random = PR_TRUE; break; case 'G': thread_scope = PR_GLOBAL_THREAD; break; case 'h': /* the value for backlock */ { PRIntn es = 0; PRHostEnt host; char buffer[1024]; (void)PR_GetHostByName( opt->value, buffer, sizeof(buffer), &host); es = PR_EnumerateHostEnt( es, &host, BASE_PORT, &shared->serverAddress); PR_ASSERT(es > 0); } break; case 'm': /* number of messages to send */ shared->messages = atoi(opt->value); break; case 't': /* number of threads sending */ threads = atoi(opt->value); break; case 'D': /* dally time between transmissions */ dally = atoi(opt->value); break; case 'T': /* timeout on I/O operations */ timeout = atoi(opt->value); break; case 's': /* total size of each message */ shared->message_length = atoi(opt->value); break; default: break; } } PL_DestroyOptState(opt); if (0 == timeout) { timeout = DEFAULT_TIMEOUT; } if (0 == threads) { threads = DEFAULT_THREADS; } if (0 == shared->messages) { shared->messages = DEFAULT_MESSAGES; } if (0 == shared->message_length) { shared->message_length = DEFAULT_MESSAGESIZE; } shared->dally = PR_SecondsToInterval(dally); shared->timeout = PR_SecondsToInterval(timeout); thread = (PRThread**)PR_CALLOC(threads * sizeof(PRThread*)); for (index = 0; index < threads; ++index) thread[index] = PR_CreateThread( PR_USER_THREAD, Connect, shared, PR_PRIORITY_NORMAL, thread_scope, PR_JOINABLE_THREAD, 0); for (index = 0; index < threads; ++index) { rv = PR_JoinThread(thread[index]); } PR_DELETE(thread); PR_fprintf( PR_GetSpecialFD(PR_StandardError), "%s\n", ((shared->failed) ? "FAILED" : "PASSED")); exitStatus = (shared->failed) ? 1 : 0; PR_DELETE(shared); return exitStatus; } int main(int argc, char **argv) { return (PR_VersionCheck(PR_VERSION)) ? PR_Initialize(Tmocon, argc, argv, 4) : -1; } /* main */ /* tmocon.c */