tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

tmoacc.c (8849B)


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