tor-browser

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

nbconn.c (14758B)


      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 * A test for nonblocking connect.  Functions tested include PR_Connect,
      8 * PR_Poll, and PR_GetConnectStatus.
      9 *
     10 * The test should be invoked with a host name, for example:
     11 *     nbconn www.netscape.com
     12 * It will do a nonblocking connect to port 80 (HTTP) on that host,
     13 * and when connected, issue the "GET /" HTTP command.
     14 *
     15 * You should run this test in three ways:
     16 * 1. To a known web site, such as www.netscape.com.  The HTML of the
     17 *    top-level page at the web site should be printed.
     18 * 2. To a machine not running a web server at port 80.  This test should
     19 *    fail.  Ideally the error code should be PR_CONNECT_REFUSED_ERROR.
     20 *    But it is possible to return PR_UNKNOWN_ERROR on certain platforms.
     21 * 3. To an unreachable machine, for example, a machine that is off line.
     22 *    The test should fail after the connect times out.  Ideally the
     23 *    error code should be PR_IO_TIMEOUT_ERROR, but it is possible to
     24 *    return PR_UNKNOWN_ERROR on certain platforms.
     25 */
     26 
     27 #include "nspr.h"
     28 #include "plgetopt.h"
     29 #include <stdio.h>
     30 #include <string.h>
     31 
     32 #define SERVER_MAX_BIND_COUNT 100
     33 #define DATA_BUF_SIZE 256
     34 #define TCP_SERVER_PORT 10000
     35 #define TCP_UNUSED_PORT 211
     36 
     37 typedef struct Server_Param {
     38  PRFileDesc* sp_fd; /* server port */
     39 } Server_Param;
     40 static void PR_CALLBACK TCP_Server(void* arg);
     41 
     42 int _debug_on;
     43 #define DPRINTF(arg) \
     44  if (_debug_on) printf arg
     45 
     46 static PRIntn connection_success_test();
     47 static PRIntn connection_failure_test();
     48 
     49 int main(int argc, char** argv) {
     50  PRHostEnt he;
     51  char buf[1024];
     52  PRNetAddr addr;
     53  PRPollDesc pd;
     54  PRStatus rv;
     55  PRSocketOptionData optData;
     56  const char* hostname = NULL;
     57  PRIntn default_case, n, bytes_read, bytes_sent;
     58  PRInt32 failed_already = 0;
     59 
     60  /*
     61   * -d           debug mode
     62   */
     63 
     64  PLOptStatus os;
     65  PLOptState* opt = PL_CreateOptState(argc, argv, "d");
     66  while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
     67    if (PL_OPT_BAD == os) {
     68      continue;
     69    }
     70    switch (opt->option) {
     71      case 0: /* debug mode */
     72        hostname = opt->value;
     73        break;
     74      case 'd': /* debug mode */
     75        _debug_on = 1;
     76        break;
     77      default:
     78        break;
     79    }
     80  }
     81  PL_DestroyOptState(opt);
     82 
     83  if (hostname) {
     84    default_case = 0;
     85  } else {
     86    default_case = 1;
     87  }
     88 
     89  if (default_case) {
     90    /*
     91     * In the default case the following tests are executed:
     92     *  1. successful connection: a server thread accepts a connection
     93     *     from the main thread
     94     *  2. unsuccessful connection: the main thread tries to connect to a
     95     *     nonexistent port and expects to get an error
     96     */
     97    rv = connection_success_test();
     98    if (rv == 0) {
     99      rv = connection_failure_test();
    100    }
    101    return rv;
    102  } else {
    103    PRFileDesc* sock;
    104 
    105    if (PR_GetHostByName(argv[1], buf, sizeof(buf), &he) == PR_FAILURE) {
    106      printf("Unknown host: %s\n", argv[1]);
    107      exit(1);
    108    } else {
    109      printf("host: %s\n", buf);
    110    }
    111    PR_EnumerateHostEnt(0, &he, 80, &addr);
    112 
    113    sock = PR_NewTCPSocket();
    114    optData.option = PR_SockOpt_Nonblocking;
    115    optData.value.non_blocking = PR_TRUE;
    116    PR_SetSocketOption(sock, &optData);
    117    rv = PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
    118    if (rv == PR_FAILURE && PR_GetError() == PR_IN_PROGRESS_ERROR) {
    119      printf("Connect in progress\n");
    120    }
    121 
    122    pd.fd = sock;
    123    pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
    124    n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
    125    if (n == -1) {
    126      printf("PR_Poll failed\n");
    127      exit(1);
    128    }
    129    printf("PR_Poll returns %d\n", n);
    130    if (pd.out_flags & PR_POLL_READ) {
    131      printf("PR_POLL_READ\n");
    132    }
    133    if (pd.out_flags & PR_POLL_WRITE) {
    134      printf("PR_POLL_WRITE\n");
    135    }
    136    if (pd.out_flags & PR_POLL_EXCEPT) {
    137      printf("PR_POLL_EXCEPT\n");
    138    }
    139    if (pd.out_flags & PR_POLL_ERR) {
    140      printf("PR_POLL_ERR\n");
    141    }
    142    if (pd.out_flags & PR_POLL_NVAL) {
    143      printf("PR_POLL_NVAL\n");
    144    }
    145 
    146    if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
    147      printf("PR_GetConnectStatus: connect succeeded\n");
    148      PR_Write(sock, "GET /\r\n\r\n", 9);
    149      PR_Shutdown(sock, PR_SHUTDOWN_SEND);
    150      pd.in_flags = PR_POLL_READ;
    151      while (1) {
    152        n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
    153        printf("poll returns %d\n", n);
    154        n = PR_Read(sock, buf, sizeof(buf));
    155        printf("read returns %d\n", n);
    156        if (n <= 0) {
    157          break;
    158        }
    159        PR_Write(PR_STDOUT, buf, n);
    160      }
    161    } else {
    162      if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
    163        printf("PR_GetConnectStatus: connect still in progress\n");
    164        exit(1);
    165      }
    166      printf("PR_GetConnectStatus: connect failed: (%ld, %ld)\n", PR_GetError(),
    167             PR_GetOSError());
    168    }
    169    PR_Close(sock);
    170    printf("PASS\n");
    171    return 0;
    172  }
    173 }
    174 
    175 /*
    176 * TCP Server
    177 *    Server Thread
    178 *    Accept a connection from the client and write some data
    179 */
    180 static void PR_CALLBACK TCP_Server(void* arg) {
    181  Server_Param* sp = (Server_Param*)arg;
    182  PRFileDesc *sockfd, *newsockfd;
    183  char data_buf[DATA_BUF_SIZE];
    184  PRIntn rv, bytes_read;
    185 
    186  sockfd = sp->sp_fd;
    187  if ((newsockfd = PR_Accept(sockfd, NULL, PR_INTERVAL_NO_TIMEOUT)) == NULL) {
    188    fprintf(stderr, "ERROR - PR_Accept failed: (%d,%d)\n", PR_GetError(),
    189            PR_GetOSError());
    190    return;
    191  }
    192  bytes_read = 0;
    193  while (bytes_read != DATA_BUF_SIZE) {
    194    rv = PR_Read(newsockfd, data_buf + bytes_read, DATA_BUF_SIZE - bytes_read);
    195    if (rv < 0) {
    196      fprintf(stderr, "Error - PR_Read failed: (%d, %d)\n", PR_GetError(),
    197              PR_GetOSError());
    198      PR_Close(newsockfd);
    199      return;
    200    }
    201    PR_ASSERT(rv != 0);
    202    bytes_read += rv;
    203  }
    204  DPRINTF(("Bytes read from client - %d\n", bytes_read));
    205  rv = PR_Write(newsockfd, data_buf, DATA_BUF_SIZE);
    206  if (rv < 0) {
    207    fprintf(stderr, "Error - PR_Write failed: (%d, %d)\n", PR_GetError(),
    208            PR_GetOSError());
    209    PR_Close(newsockfd);
    210    return;
    211  }
    212  PR_ASSERT(rv == DATA_BUF_SIZE);
    213  DPRINTF(("Bytes written to client - %d\n", rv));
    214  PR_Close(newsockfd);
    215 }
    216 
    217 /*
    218 * test for successful connection using a non-blocking socket
    219 */
    220 static PRIntn connection_success_test() {
    221  PRFileDesc *sockfd = NULL, *conn_fd = NULL;
    222  PRNetAddr netaddr;
    223  PRInt32 i, rv;
    224  PRPollDesc pd;
    225  PRSocketOptionData optData;
    226  PRThread* thr = NULL;
    227  Server_Param sp;
    228  char send_buf[DATA_BUF_SIZE], recv_buf[DATA_BUF_SIZE];
    229  PRIntn default_case, n, bytes_read, bytes_sent;
    230  PRIntn failed_already = 0;
    231 
    232  /*
    233   * Create a tcp socket
    234   */
    235  if ((sockfd = PR_NewTCPSocket()) == NULL) {
    236    fprintf(stderr, "Error - PR_NewTCPSocket failed\n");
    237    failed_already = 1;
    238    goto def_exit;
    239  }
    240  memset(&netaddr, 0, sizeof(netaddr));
    241  netaddr.inet.family = PR_AF_INET;
    242  netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
    243  netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
    244  /*
    245   * try a few times to bind server's address, if addresses are in
    246   * use
    247   */
    248  i = 0;
    249  while (PR_Bind(sockfd, &netaddr) < 0) {
    250    if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
    251      netaddr.inet.port += 2;
    252      if (i++ < SERVER_MAX_BIND_COUNT) {
    253        continue;
    254      }
    255    }
    256    fprintf(stderr, "ERROR - PR_Bind failed: (%d,%d)\n", PR_GetError(),
    257            PR_GetOSError());
    258    failed_already = 1;
    259    goto def_exit;
    260  }
    261 
    262  if (PR_Listen(sockfd, 32) < 0) {
    263    fprintf(stderr, "ERROR - PR_Listen failed: (%d,%d)\n", PR_GetError(),
    264            PR_GetOSError());
    265    failed_already = 1;
    266    goto def_exit;
    267  }
    268 
    269  if (PR_GetSockName(sockfd, &netaddr) < 0) {
    270    fprintf(stderr, "ERROR - PR_GetSockName failed: (%d,%d)\n", PR_GetError(),
    271            PR_GetOSError());
    272    failed_already = 1;
    273    goto def_exit;
    274  }
    275  if ((conn_fd = PR_NewTCPSocket()) == NULL) {
    276    fprintf(stderr, "Error - PR_NewTCPSocket failed\n");
    277    failed_already = 1;
    278    goto def_exit;
    279  }
    280  optData.option = PR_SockOpt_Nonblocking;
    281  optData.value.non_blocking = PR_TRUE;
    282  PR_SetSocketOption(conn_fd, &optData);
    283  rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT);
    284  if (rv == PR_FAILURE) {
    285    if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
    286      DPRINTF(("Connect in progress\n"));
    287    } else {
    288      fprintf(stderr, "Error - PR_Connect failed: (%d, %d)\n", PR_GetError(),
    289              PR_GetOSError());
    290      failed_already = 1;
    291      goto def_exit;
    292    }
    293  }
    294  /*
    295   * Now create a thread to accept a connection
    296   */
    297  sp.sp_fd = sockfd;
    298  thr = PR_CreateThread(PR_USER_THREAD, TCP_Server, (void*)&sp,
    299                        PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD,
    300                        0);
    301  if (thr == NULL) {
    302    fprintf(stderr, "Error - PR_CreateThread failed: (%d,%d)\n", PR_GetError(),
    303            PR_GetOSError());
    304    failed_already = 1;
    305    goto def_exit;
    306  }
    307  DPRINTF(("Created TCP_Server thread [0x%x]\n", thr));
    308  pd.fd = conn_fd;
    309  pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
    310  n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
    311  if (n == -1) {
    312    fprintf(stderr, "Error - PR_Poll failed: (%d, %d)\n", PR_GetError(),
    313            PR_GetOSError());
    314    failed_already = 1;
    315    goto def_exit;
    316  }
    317  if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
    318    PRInt32 rv;
    319 
    320    DPRINTF(("Connection successful\n"));
    321 
    322    /*
    323     * Write some data, read it back and check data integrity to
    324     * make sure the connection is good
    325     */
    326    pd.in_flags = PR_POLL_WRITE;
    327    bytes_sent = 0;
    328    memset(send_buf, 'a', DATA_BUF_SIZE);
    329    while (bytes_sent != DATA_BUF_SIZE) {
    330      rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
    331      if (rv < 0) {
    332        fprintf(stderr, "Error - PR_Poll failed: (%d, %d)\n", PR_GetError(),
    333                PR_GetOSError());
    334        failed_already = 1;
    335        goto def_exit;
    336      }
    337      PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_WRITE));
    338      rv = PR_Write(conn_fd, send_buf + bytes_sent, DATA_BUF_SIZE - bytes_sent);
    339      if (rv < 0) {
    340        fprintf(stderr, "Error - PR_Write failed: (%d, %d)\n", PR_GetError(),
    341                PR_GetOSError());
    342        failed_already = 1;
    343        goto def_exit;
    344      }
    345      PR_ASSERT(rv > 0);
    346      bytes_sent += rv;
    347    }
    348    DPRINTF(("Bytes written to server - %d\n", bytes_sent));
    349    PR_Shutdown(conn_fd, PR_SHUTDOWN_SEND);
    350    pd.in_flags = PR_POLL_READ;
    351    bytes_read = 0;
    352    memset(recv_buf, 0, DATA_BUF_SIZE);
    353    while (bytes_read != DATA_BUF_SIZE) {
    354      rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
    355      if (rv < 0) {
    356        fprintf(stderr, "Error - PR_Poll failed: (%d, %d)\n", PR_GetError(),
    357                PR_GetOSError());
    358        failed_already = 1;
    359        goto def_exit;
    360      }
    361      PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_READ));
    362      rv = PR_Read(conn_fd, recv_buf + bytes_read, DATA_BUF_SIZE - bytes_read);
    363      if (rv < 0) {
    364        fprintf(stderr, "Error - PR_Read failed: (%d, %d)\n", PR_GetError(),
    365                PR_GetOSError());
    366        failed_already = 1;
    367        goto def_exit;
    368      }
    369      PR_ASSERT(rv != 0);
    370      bytes_read += rv;
    371    }
    372    DPRINTF(("Bytes read from server - %d\n", bytes_read));
    373    /*
    374     * verify the data read
    375     */
    376    if (memcmp(send_buf, recv_buf, DATA_BUF_SIZE) != 0) {
    377      fprintf(stderr, "ERROR - data corruption\n");
    378      failed_already = 1;
    379      goto def_exit;
    380    }
    381    DPRINTF(("Data integrity verified\n"));
    382  } else {
    383    fprintf(stderr, "PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
    384            PR_GetError(), PR_GetOSError());
    385    failed_already = 1;
    386    goto def_exit;
    387  }
    388 def_exit:
    389  if (thr) {
    390    PR_JoinThread(thr);
    391    thr = NULL;
    392  }
    393  if (sockfd) {
    394    PR_Close(sockfd);
    395    sockfd = NULL;
    396  }
    397  if (conn_fd) {
    398    PR_Close(conn_fd);
    399    conn_fd = NULL;
    400  }
    401  if (failed_already) {
    402    return 1;
    403  } else {
    404    return 0;
    405  }
    406 }
    407 
    408 /*
    409 * test for connection to a nonexistent port using a non-blocking socket
    410 */
    411 static PRIntn connection_failure_test() {
    412  PRFileDesc *sockfd = NULL, *conn_fd = NULL;
    413  PRNetAddr netaddr;
    414  PRInt32 i, rv;
    415  PRPollDesc pd;
    416  PRSocketOptionData optData;
    417  PRIntn n, failed_already = 0;
    418 
    419  /*
    420   * Create a tcp socket
    421   */
    422  if ((sockfd = PR_NewTCPSocket()) == NULL) {
    423    fprintf(stderr, "Error - PR_NewTCPSocket failed\n");
    424    failed_already = 1;
    425    goto def_exit;
    426  }
    427  memset(&netaddr, 0, sizeof(netaddr));
    428  netaddr.inet.family = PR_AF_INET;
    429  netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
    430  netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
    431  /*
    432   * try a few times to bind server's address, if addresses are in
    433   * use
    434   */
    435  i = 0;
    436  while (PR_Bind(sockfd, &netaddr) < 0) {
    437    if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
    438      netaddr.inet.port += 2;
    439      if (i++ < SERVER_MAX_BIND_COUNT) {
    440        continue;
    441      }
    442    }
    443    fprintf(stderr, "ERROR - PR_Bind failed: (%d,%d)\n", PR_GetError(),
    444            PR_GetOSError());
    445    failed_already = 1;
    446    goto def_exit;
    447  }
    448 
    449  if (PR_GetSockName(sockfd, &netaddr) < 0) {
    450    fprintf(stderr, "ERROR - PR_GetSockName failed: (%d,%d)\n", PR_GetError(),
    451            PR_GetOSError());
    452    failed_already = 1;
    453    goto def_exit;
    454  }
    455 #ifdef AIX
    456  /*
    457   * On AIX, set to unused/reserved port
    458   */
    459  netaddr.inet.port = PR_htons(TCP_UNUSED_PORT);
    460 #endif
    461  if ((conn_fd = PR_NewTCPSocket()) == NULL) {
    462    fprintf(stderr, "Error - PR_NewTCPSocket failed\n");
    463    failed_already = 1;
    464    goto def_exit;
    465  }
    466  optData.option = PR_SockOpt_Nonblocking;
    467  optData.value.non_blocking = PR_TRUE;
    468  PR_SetSocketOption(conn_fd, &optData);
    469  rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT);
    470  if (rv == PR_FAILURE) {
    471    DPRINTF(("PR_Connect to a non-listen port failed: (%d, %d)\n",
    472             PR_GetError(), PR_GetOSError()));
    473  } else {
    474    PR_ASSERT(rv == PR_SUCCESS);
    475    fprintf(stderr, "Error - PR_Connect succeeded, expected to fail\n");
    476    failed_already = 1;
    477    goto def_exit;
    478  }
    479  pd.fd = conn_fd;
    480  pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
    481  n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
    482  if (n == -1) {
    483    fprintf(stderr, "Error - PR_Poll failed: (%d, %d)\n", PR_GetError(),
    484            PR_GetOSError());
    485    failed_already = 1;
    486    goto def_exit;
    487  }
    488  if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
    489    PRInt32 rv;
    490    fprintf(stderr, "PR_GetConnectStatus succeeded, expected to fail\n");
    491    failed_already = 1;
    492    goto def_exit;
    493  }
    494  rv = PR_GetError();
    495  DPRINTF(("Connection failed, successfully with PR_Error %d\n", rv));
    496 def_exit:
    497  if (sockfd) {
    498    PR_Close(sockfd);
    499    sockfd = NULL;
    500  }
    501  if (conn_fd) {
    502    PR_Close(conn_fd);
    503    conn_fd = NULL;
    504  }
    505  if (failed_already) {
    506    return 1;
    507  } else {
    508    return 0;
    509  }
    510 }