tor-browser

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

prmapopt.c (14883B)


      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 * This file defines _PR_MapOptionName().  The purpose of putting
      8 * _PR_MapOptionName() in a separate file is to work around a Winsock
      9 * header file problem on Windows NT.
     10 *
     11 * On Windows NT, if we define _WIN32_WINNT to be 0x0400 (in order
     12 * to use Service Pack 3 extensions), windows.h includes winsock2.h
     13 * (instead of winsock.h), which doesn't define many socket options
     14 * defined in winsock.h.
     15 *
     16 * We need the socket options defined in winsock.h.  So this file
     17 * includes winsock.h, with _WIN32_WINNT undefined.
     18 */
     19 
     20 #if defined(WINNT) || defined(__MINGW32__)
     21 #  include <winsock.h>
     22 #endif
     23 
     24 /* MinGW doesn't define these in its winsock.h. */
     25 #ifdef __MINGW32__
     26 #  ifndef IP_TTL
     27 #    define IP_TTL 7
     28 #  endif
     29 #  ifndef IP_TOS
     30 #    define IP_TOS 8
     31 #  endif
     32 #endif
     33 
     34 #include "primpl.h"
     35 
     36 #if defined(LINUX) || defined(ANDROID)
     37 #  include <netinet/in.h>
     38 #endif
     39 
     40 #ifdef DARWIN
     41 #  include <netinet/in.h>
     42 #  include <netinet/ip.h>
     43 #endif
     44 
     45 #ifdef HAVE_NETINET_TCP_H
     46 #  include <netinet/tcp.h> /* TCP_NODELAY, TCP_MAXSEG */
     47 #endif
     48 
     49 #ifndef _PR_PTHREADS
     50 
     51 PRStatus PR_CALLBACK _PR_SocketGetSocketOption(PRFileDesc* fd,
     52                                               PRSocketOptionData* data) {
     53  PRStatus rv;
     54  PRInt32 length;
     55  PRInt32 level, name;
     56 
     57  /*
     58   * PR_SockOpt_Nonblocking is a special case that does not
     59   * translate to a getsockopt() call
     60   */
     61  if (PR_SockOpt_Nonblocking == data->option) {
     62    data->value.non_blocking = fd->secret->nonblocking;
     63    return PR_SUCCESS;
     64  }
     65 
     66  rv = _PR_MapOptionName(data->option, &level, &name);
     67  if (PR_SUCCESS == rv) {
     68    switch (data->option) {
     69      case PR_SockOpt_Linger: {
     70        struct linger linger;
     71        length = sizeof(linger);
     72        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&linger, &length);
     73        if (PR_SUCCESS == rv) {
     74          PR_ASSERT(sizeof(linger) == length);
     75          data->value.linger.polarity = (linger.l_onoff) ? PR_TRUE : PR_FALSE;
     76          data->value.linger.linger = PR_SecondsToInterval(linger.l_linger);
     77        }
     78        break;
     79      }
     80      case PR_SockOpt_Reuseaddr:
     81      case PR_SockOpt_Keepalive:
     82      case PR_SockOpt_NoDelay:
     83      case PR_SockOpt_Broadcast:
     84      case PR_SockOpt_Reuseport: {
     85 #  ifdef WIN32 /* Winsock */
     86        BOOL value;
     87 #  else
     88        PRIntn value;
     89 #  endif
     90        length = sizeof(value);
     91        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&value, &length);
     92        if (PR_SUCCESS == rv) {
     93          data->value.reuse_addr = (0 == value) ? PR_FALSE : PR_TRUE;
     94        }
     95        break;
     96      }
     97      case PR_SockOpt_McastLoopback: {
     98 #  ifdef WIN32 /* Winsock */
     99        BOOL bool;
    100 #  else
    101        PRUint8 bool;
    102 #  endif
    103        length = sizeof(bool);
    104        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&bool, &length);
    105        if (PR_SUCCESS == rv) {
    106          data->value.mcast_loopback = (0 == bool) ? PR_FALSE : PR_TRUE;
    107        }
    108        break;
    109      }
    110      case PR_SockOpt_RecvBufferSize:
    111      case PR_SockOpt_SendBufferSize:
    112      case PR_SockOpt_MaxSegment: {
    113        PRIntn value;
    114        length = sizeof(value);
    115        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&value, &length);
    116        if (PR_SUCCESS == rv) {
    117          data->value.recv_buffer_size = value;
    118        }
    119        break;
    120      }
    121      case PR_SockOpt_IpTimeToLive:
    122      case PR_SockOpt_IpTypeOfService: {
    123        /* These options should really be an int (or PRIntn). */
    124        length = sizeof(PRUintn);
    125        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&data->value.ip_ttl,
    126                               &length);
    127        break;
    128      }
    129      case PR_SockOpt_McastTimeToLive: {
    130 #  ifdef WIN32 /* Winsock */
    131        int ttl;
    132 #  else
    133        PRUint8 ttl;
    134 #  endif
    135        length = sizeof(ttl);
    136        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&ttl, &length);
    137        if (PR_SUCCESS == rv) {
    138          data->value.mcast_ttl = ttl;
    139        }
    140        break;
    141      }
    142 #  ifdef IP_ADD_MEMBERSHIP
    143      case PR_SockOpt_AddMember:
    144      case PR_SockOpt_DropMember: {
    145        struct ip_mreq mreq;
    146        length = sizeof(mreq);
    147        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&mreq, &length);
    148        if (PR_SUCCESS == rv) {
    149          data->value.add_member.mcaddr.inet.ip = mreq.imr_multiaddr.s_addr;
    150          data->value.add_member.ifaddr.inet.ip = mreq.imr_interface.s_addr;
    151        }
    152        break;
    153      }
    154 #  endif /* IP_ADD_MEMBERSHIP */
    155      case PR_SockOpt_McastInterface: {
    156        /* This option is a struct in_addr. */
    157        length = sizeof(data->value.mcast_if.inet.ip);
    158        rv = _PR_MD_GETSOCKOPT(fd, level, name,
    159                               (char*)&data->value.mcast_if.inet.ip, &length);
    160        break;
    161      }
    162      case PR_SockOpt_DontFrag: {
    163 #  if !defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && \
    164      !defined(ANDROID)
    165        PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
    166        rv = PR_FAILURE;
    167 #  else
    168 #    ifdef WIN32 /* Winsock */
    169        DWORD value;
    170 #    else
    171        PRIntn value;
    172 #    endif
    173        length = sizeof(value);
    174        rv = _PR_MD_GETSOCKOPT(fd, level, name, (char*)&value, &length);
    175 #    if defined(WIN32) || defined(DARWIN)
    176        data->value.dont_fragment = value;
    177 #    else
    178        data->value.dont_fragment = (value == IP_PMTUDISC_DO) ? 1 : 0;
    179 #    endif
    180 #  endif /* !(!defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && \
    181            !defined(ANDROID)) */
    182        break;
    183      }
    184      default:
    185        PR_NOT_REACHED("Unknown socket option");
    186        break;
    187    }
    188  }
    189  return rv;
    190 } /* _PR_SocketGetSocketOption */
    191 
    192 PRStatus PR_CALLBACK _PR_SocketSetSocketOption(PRFileDesc* fd,
    193                                               const PRSocketOptionData* data) {
    194  PRStatus rv;
    195  PRInt32 level, name;
    196 
    197  /*
    198   * PR_SockOpt_Nonblocking is a special case that does not
    199   * translate to a setsockopt call.
    200   */
    201  if (PR_SockOpt_Nonblocking == data->option) {
    202 #  ifdef WINNT
    203    PR_ASSERT((fd->secret->md.io_model_committed == PR_FALSE) ||
    204              (fd->secret->nonblocking == data->value.non_blocking));
    205    if (fd->secret->md.io_model_committed &&
    206        (fd->secret->nonblocking != data->value.non_blocking)) {
    207      /*
    208       * On NT, once we have associated a socket with the io
    209       * completion port, we can't disassociate it.  So we
    210       * can't change the nonblocking option of the socket
    211       * afterwards.
    212       */
    213      PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    214      return PR_FAILURE;
    215    }
    216 #  endif
    217    fd->secret->nonblocking = data->value.non_blocking;
    218    return PR_SUCCESS;
    219  }
    220 
    221  rv = _PR_MapOptionName(data->option, &level, &name);
    222  if (PR_SUCCESS == rv) {
    223    switch (data->option) {
    224      case PR_SockOpt_Linger: {
    225        struct linger linger;
    226        linger.l_onoff = data->value.linger.polarity;
    227        linger.l_linger = PR_IntervalToSeconds(data->value.linger.linger);
    228        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&linger, sizeof(linger));
    229        break;
    230      }
    231      case PR_SockOpt_Reuseaddr:
    232      case PR_SockOpt_Keepalive:
    233      case PR_SockOpt_NoDelay:
    234      case PR_SockOpt_Broadcast:
    235      case PR_SockOpt_Reuseport: {
    236 #  ifdef WIN32 /* Winsock */
    237        BOOL value;
    238 #  else
    239        PRIntn value;
    240 #  endif
    241        value = (data->value.reuse_addr) ? 1 : 0;
    242        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&value, sizeof(value));
    243        break;
    244      }
    245      case PR_SockOpt_McastLoopback: {
    246 #  ifdef WIN32 /* Winsock */
    247        BOOL bool;
    248 #  else
    249        PRUint8 bool;
    250 #  endif
    251        bool = data->value.mcast_loopback ? 1 : 0;
    252        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&bool, sizeof(bool));
    253        break;
    254      }
    255      case PR_SockOpt_RecvBufferSize:
    256      case PR_SockOpt_SendBufferSize:
    257      case PR_SockOpt_MaxSegment: {
    258        PRIntn value = data->value.recv_buffer_size;
    259        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&value, sizeof(value));
    260        break;
    261      }
    262      case PR_SockOpt_IpTimeToLive:
    263      case PR_SockOpt_IpTypeOfService: {
    264        /* These options should really be an int (or PRIntn). */
    265        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&data->value.ip_ttl,
    266                               sizeof(PRUintn));
    267        break;
    268      }
    269      case PR_SockOpt_McastTimeToLive: {
    270 #  ifdef WIN32 /* Winsock */
    271        int ttl;
    272 #  else
    273        PRUint8 ttl;
    274 #  endif
    275        ttl = data->value.mcast_ttl;
    276        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&ttl, sizeof(ttl));
    277        break;
    278      }
    279 #  ifdef IP_ADD_MEMBERSHIP
    280      case PR_SockOpt_AddMember:
    281      case PR_SockOpt_DropMember: {
    282        struct ip_mreq mreq;
    283        mreq.imr_multiaddr.s_addr = data->value.add_member.mcaddr.inet.ip;
    284        mreq.imr_interface.s_addr = data->value.add_member.ifaddr.inet.ip;
    285        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&mreq, sizeof(mreq));
    286        break;
    287      }
    288 #  endif /* IP_ADD_MEMBERSHIP */
    289      case PR_SockOpt_McastInterface: {
    290        /* This option is a struct in_addr. */
    291        rv = _PR_MD_SETSOCKOPT(fd, level, name,
    292                               (char*)&data->value.mcast_if.inet.ip,
    293                               sizeof(data->value.mcast_if.inet.ip));
    294        break;
    295      }
    296      case PR_SockOpt_DontFrag: {
    297 #  if !defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && \
    298      !defined(ANDROID)
    299        PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
    300        rv = PR_FAILURE;
    301 #  else
    302 #    if defined(WIN32) /* Winsock */
    303        DWORD value;
    304        value = (data->value.dont_fragment) ? 1 : 0;
    305 #    elif defined(LINUX) || defined(ANDROID)
    306        PRIntn value;
    307        value = (data->value.dont_fragment) ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
    308 #    elif defined(DARWIN)
    309        PRIntn value;
    310        value = data->value.dont_fragment;
    311 #    endif
    312        rv = _PR_MD_SETSOCKOPT(fd, level, name, (char*)&value, sizeof(value));
    313 #  endif /* !(!defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && \
    314            !defined(ANDROID)) */
    315        break;
    316      }
    317      default:
    318        PR_NOT_REACHED("Unknown socket option");
    319        break;
    320    }
    321  }
    322  return rv;
    323 } /* _PR_SocketSetSocketOption */
    324 
    325 #endif /* ! _PR_PTHREADS */
    326 
    327 /*
    328 *********************************************************************
    329 *********************************************************************
    330 **
    331 ** Make sure that the following is at the end of this file,
    332 ** because we will be playing with macro redefines.
    333 **
    334 *********************************************************************
    335 *********************************************************************
    336 */
    337 
    338 /*
    339 * Not every platform has all the socket options we want to
    340 * support.  Some older operating systems such as SunOS 4.1.3
    341 * don't have the IP multicast socket options.  Win32 doesn't
    342 * have TCP_MAXSEG.
    343 *
    344 * To deal with this problem, we define the missing socket
    345 * options as _PR_NO_SUCH_SOCKOPT.  _PR_MapOptionName() fails with
    346 * PR_OPERATION_NOT_SUPPORTED_ERROR if a socket option not
    347 * available on the platform is requested.
    348 */
    349 
    350 /*
    351 * Sanity check.  SO_LINGER and TCP_NODELAY should be available
    352 * on all platforms.  Just to make sure we have included the
    353 * appropriate header files.  Then any undefined socket options
    354 * are really missing.
    355 */
    356 
    357 #if !defined(SO_LINGER)
    358 #  error "SO_LINGER is not defined"
    359 #endif
    360 
    361 #if !defined(TCP_NODELAY)
    362 #  error "TCP_NODELAY is not defined"
    363 #endif
    364 
    365 /*
    366 * Make sure the value of _PR_NO_SUCH_SOCKOPT is not
    367 * a valid socket option.
    368 */
    369 #define _PR_NO_SUCH_SOCKOPT -1
    370 
    371 #ifndef SO_KEEPALIVE
    372 #  define SO_KEEPALIVE _PR_NO_SUCH_SOCKOPT
    373 #endif
    374 
    375 #ifndef SO_SNDBUF
    376 #  define SO_SNDBUF _PR_NO_SUCH_SOCKOPT
    377 #endif
    378 
    379 #ifndef SO_RCVBUF
    380 #  define SO_RCVBUF _PR_NO_SUCH_SOCKOPT
    381 #endif
    382 
    383 #ifndef IP_MULTICAST_IF /* set/get IP multicast interface   */
    384 #  define IP_MULTICAST_IF _PR_NO_SUCH_SOCKOPT
    385 #endif
    386 
    387 #ifndef IP_MULTICAST_TTL /* set/get IP multicast timetolive  */
    388 #  define IP_MULTICAST_TTL _PR_NO_SUCH_SOCKOPT
    389 #endif
    390 
    391 #ifndef IP_MULTICAST_LOOP /* set/get IP multicast loopback    */
    392 #  define IP_MULTICAST_LOOP _PR_NO_SUCH_SOCKOPT
    393 #endif
    394 
    395 #ifndef IP_ADD_MEMBERSHIP /* add  an IP group membership      */
    396 #  define IP_ADD_MEMBERSHIP _PR_NO_SUCH_SOCKOPT
    397 #endif
    398 
    399 #ifndef IP_DROP_MEMBERSHIP /* drop an IP group membership      */
    400 #  define IP_DROP_MEMBERSHIP _PR_NO_SUCH_SOCKOPT
    401 #endif
    402 
    403 #ifndef IP_TTL /* set/get IP Time To Live          */
    404 #  define IP_TTL _PR_NO_SUCH_SOCKOPT
    405 #endif
    406 
    407 #ifndef IP_TOS /* set/get IP Type Of Service       */
    408 #  define IP_TOS _PR_NO_SUCH_SOCKOPT
    409 #endif
    410 
    411 /* set/get IP do not fragment */
    412 #if defined(WIN32)
    413 #  ifndef IP_DONTFRAGMENT
    414 #    define IP_DONTFRAGMENT _PR_NO_SUCH_SOCKOPT
    415 #  endif
    416 
    417 #elif defined(LINUX) || defined(ANDROID)
    418 #  ifndef IP_MTU_DISCOVER
    419 #    define IP_MTU_DISCOVER _PR_NO_SUCH_SOCKOPT
    420 #  endif
    421 
    422 #elif defined(DARWIN)
    423 #  ifndef IP_DONTFRAG
    424 #    define IP_DONTFRAG _PR_NO_SUCH_SOCKOPT
    425 #  endif
    426 #endif
    427 
    428 #ifndef TCP_NODELAY /* don't delay to coalesce data     */
    429 #  define TCP_NODELAY _PR_NO_SUCH_SOCKOPT
    430 #endif
    431 
    432 #ifndef TCP_MAXSEG /* maxumum segment size for tcp     */
    433 #  define TCP_MAXSEG _PR_NO_SUCH_SOCKOPT
    434 #endif
    435 
    436 #ifndef SO_BROADCAST /* enable broadcast on UDP sockets  */
    437 #  define SO_BROADCAST _PR_NO_SUCH_SOCKOPT
    438 #endif
    439 
    440 #ifndef SO_REUSEPORT /* allow local address & port reuse */
    441 #  define SO_REUSEPORT _PR_NO_SUCH_SOCKOPT
    442 #endif
    443 
    444 PRStatus _PR_MapOptionName(PRSockOption optname, PRInt32* level,
    445                           PRInt32* name) {
    446  static PRInt32 socketOptions[PR_SockOpt_Last] = {
    447      0,
    448      SO_LINGER,
    449      SO_REUSEADDR,
    450      SO_KEEPALIVE,
    451      SO_RCVBUF,
    452      SO_SNDBUF,
    453      IP_TTL,
    454      IP_TOS,
    455      IP_ADD_MEMBERSHIP,
    456      IP_DROP_MEMBERSHIP,
    457      IP_MULTICAST_IF,
    458      IP_MULTICAST_TTL,
    459      IP_MULTICAST_LOOP,
    460      TCP_NODELAY,
    461      TCP_MAXSEG,
    462      SO_BROADCAST,
    463      SO_REUSEPORT,
    464 #if defined(WIN32)
    465      IP_DONTFRAGMENT,
    466 #elif defined(LINUX) || defined(ANDROID)
    467      IP_MTU_DISCOVER,
    468 #elif defined(DARWIN)
    469      IP_DONTFRAG,
    470 #else
    471      _PR_NO_SUCH_SOCKOPT,
    472 #endif
    473  };
    474  static PRInt32 socketLevels[PR_SockOpt_Last] = {
    475      0,          SOL_SOCKET,  SOL_SOCKET,  SOL_SOCKET, SOL_SOCKET, SOL_SOCKET,
    476      IPPROTO_IP, IPPROTO_IP,  IPPROTO_IP,  IPPROTO_IP, IPPROTO_IP, IPPROTO_IP,
    477      IPPROTO_IP, IPPROTO_TCP, IPPROTO_TCP, SOL_SOCKET, SOL_SOCKET, IPPROTO_IP};
    478 
    479  if ((optname < PR_SockOpt_Linger) || (optname >= PR_SockOpt_Last)) {
    480    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    481    return PR_FAILURE;
    482  }
    483 
    484  if (socketOptions[optname] == _PR_NO_SUCH_SOCKOPT) {
    485    PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
    486    return PR_FAILURE;
    487  }
    488  *name = socketOptions[optname];
    489  *level = socketLevels[optname];
    490  return PR_SUCCESS;
    491 } /* _PR_MapOptionName */