/* -*- 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/. */ /* * This file defines _PR_MapOptionName(). The purpose of putting * _PR_MapOptionName() in a separate file is to work around a Winsock * header file problem on Windows NT. * * On Windows NT, if we define _WIN32_WINNT to be 0x0400 (in order * to use Service Pack 3 extensions), windows.h includes winsock2.h * (instead of winsock.h), which doesn't define many socket options * defined in winsock.h. * * We need the socket options defined in winsock.h. So this file * includes winsock.h, with _WIN32_WINNT undefined. */ #if defined(WINNT) || defined(__MINGW32__) #include #endif /* MinGW doesn't define these in its winsock.h. */ #ifdef __MINGW32__ #ifndef IP_TTL #define IP_TTL 7 #endif #ifndef IP_TOS #define IP_TOS 8 #endif #endif #include "primpl.h" #if defined(LINUX) || defined(ANDROID) #include #endif #ifdef DARWIN #include #include #endif #ifdef HAVE_NETINET_TCP_H #include /* TCP_NODELAY, TCP_MAXSEG */ #endif #ifndef _PR_PTHREADS PRStatus PR_CALLBACK _PR_SocketGetSocketOption(PRFileDesc *fd, PRSocketOptionData *data) { PRStatus rv; PRInt32 length; PRInt32 level, name; /* * PR_SockOpt_Nonblocking is a special case that does not * translate to a getsockopt() call */ if (PR_SockOpt_Nonblocking == data->option) { data->value.non_blocking = fd->secret->nonblocking; return PR_SUCCESS; } rv = _PR_MapOptionName(data->option, &level, &name); if (PR_SUCCESS == rv) { switch (data->option) { case PR_SockOpt_Linger: { struct linger linger; length = sizeof(linger); rv = _PR_MD_GETSOCKOPT( fd, level, name, (char *) &linger, &length); if (PR_SUCCESS == rv) { PR_ASSERT(sizeof(linger) == length); data->value.linger.polarity = (linger.l_onoff) ? PR_TRUE : PR_FALSE; data->value.linger.linger = PR_SecondsToInterval(linger.l_linger); } break; } case PR_SockOpt_Reuseaddr: case PR_SockOpt_Keepalive: case PR_SockOpt_NoDelay: case PR_SockOpt_Broadcast: case PR_SockOpt_Reuseport: { #ifdef WIN32 /* Winsock */ BOOL value; #else PRIntn value; #endif length = sizeof(value); rv = _PR_MD_GETSOCKOPT( fd, level, name, (char*)&value, &length); if (PR_SUCCESS == rv) { data->value.reuse_addr = (0 == value) ? PR_FALSE : PR_TRUE; } break; } case PR_SockOpt_McastLoopback: { #ifdef WIN32 /* Winsock */ BOOL bool; #else PRUint8 bool; #endif length = sizeof(bool); rv = _PR_MD_GETSOCKOPT( fd, level, name, (char*)&bool, &length); if (PR_SUCCESS == rv) { data->value.mcast_loopback = (0 == bool) ? PR_FALSE : PR_TRUE; } break; } case PR_SockOpt_RecvBufferSize: case PR_SockOpt_SendBufferSize: case PR_SockOpt_MaxSegment: { PRIntn value; length = sizeof(value); rv = _PR_MD_GETSOCKOPT( fd, level, name, (char*)&value, &length); if (PR_SUCCESS == rv) { data->value.recv_buffer_size = value; } break; } case PR_SockOpt_IpTimeToLive: case PR_SockOpt_IpTypeOfService: { /* These options should really be an int (or PRIntn). */ length = sizeof(PRUintn); rv = _PR_MD_GETSOCKOPT( fd, level, name, (char*)&data->value.ip_ttl, &length); break; } case PR_SockOpt_McastTimeToLive: { #ifdef WIN32 /* Winsock */ int ttl; #else PRUint8 ttl; #endif length = sizeof(ttl); rv = _PR_MD_GETSOCKOPT( fd, level, name, (char*)&ttl, &length); if (PR_SUCCESS == rv) { data->value.mcast_ttl = ttl; } break; } #ifdef IP_ADD_MEMBERSHIP case PR_SockOpt_AddMember: case PR_SockOpt_DropMember: { struct ip_mreq mreq; length = sizeof(mreq); rv = _PR_MD_GETSOCKOPT( fd, level, name, (char*)&mreq, &length); if (PR_SUCCESS == rv) { data->value.add_member.mcaddr.inet.ip = mreq.imr_multiaddr.s_addr; data->value.add_member.ifaddr.inet.ip = mreq.imr_interface.s_addr; } break; } #endif /* IP_ADD_MEMBERSHIP */ case PR_SockOpt_McastInterface: { /* This option is a struct in_addr. */ length = sizeof(data->value.mcast_if.inet.ip); rv = _PR_MD_GETSOCKOPT( fd, level, name, (char*)&data->value.mcast_if.inet.ip, &length); break; } case PR_SockOpt_DontFrag: { #if !defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID) PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0); rv = PR_FAILURE; #else #ifdef WIN32 /* Winsock */ DWORD value; #else PRIntn value; #endif length = sizeof(value); rv = _PR_MD_GETSOCKOPT( fd, level, name, (char*)&value, &length); #if defined(WIN32) || defined(DARWIN) data->value.dont_fragment = value; #else data->value.dont_fragment = (value == IP_PMTUDISC_DO) ? 1 : 0; #endif #endif /* !(!defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)) */ break; } default: PR_NOT_REACHED("Unknown socket option"); break; } } return rv; } /* _PR_SocketGetSocketOption */ PRStatus PR_CALLBACK _PR_SocketSetSocketOption(PRFileDesc *fd, const PRSocketOptionData *data) { PRStatus rv; PRInt32 level, name; /* * PR_SockOpt_Nonblocking is a special case that does not * translate to a setsockopt call. */ if (PR_SockOpt_Nonblocking == data->option) { #ifdef WINNT PR_ASSERT((fd->secret->md.io_model_committed == PR_FALSE) || (fd->secret->nonblocking == data->value.non_blocking)); if (fd->secret->md.io_model_committed && (fd->secret->nonblocking != data->value.non_blocking)) { /* * On NT, once we have associated a socket with the io * completion port, we can't disassociate it. So we * can't change the nonblocking option of the socket * afterwards. */ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); return PR_FAILURE; } #endif fd->secret->nonblocking = data->value.non_blocking; return PR_SUCCESS; } rv = _PR_MapOptionName(data->option, &level, &name); if (PR_SUCCESS == rv) { switch (data->option) { case PR_SockOpt_Linger: { struct linger linger; linger.l_onoff = data->value.linger.polarity; linger.l_linger = PR_IntervalToSeconds(data->value.linger.linger); rv = _PR_MD_SETSOCKOPT( fd, level, name, (char*)&linger, sizeof(linger)); break; } case PR_SockOpt_Reuseaddr: case PR_SockOpt_Keepalive: case PR_SockOpt_NoDelay: case PR_SockOpt_Broadcast: case PR_SockOpt_Reuseport: { #ifdef WIN32 /* Winsock */ BOOL value; #else PRIntn value; #endif value = (data->value.reuse_addr) ? 1 : 0; rv = _PR_MD_SETSOCKOPT( fd, level, name, (char*)&value, sizeof(value)); break; } case PR_SockOpt_McastLoopback: { #ifdef WIN32 /* Winsock */ BOOL bool; #else PRUint8 bool; #endif bool = data->value.mcast_loopback ? 1 : 0; rv = _PR_MD_SETSOCKOPT( fd, level, name, (char*)&bool, sizeof(bool)); break; } case PR_SockOpt_RecvBufferSize: case PR_SockOpt_SendBufferSize: case PR_SockOpt_MaxSegment: { PRIntn value = data->value.recv_buffer_size; rv = _PR_MD_SETSOCKOPT( fd, level, name, (char*)&value, sizeof(value)); break; } case PR_SockOpt_IpTimeToLive: case PR_SockOpt_IpTypeOfService: { /* These options should really be an int (or PRIntn). */ rv = _PR_MD_SETSOCKOPT( fd, level, name, (char*)&data->value.ip_ttl, sizeof(PRUintn)); break; } case PR_SockOpt_McastTimeToLive: { #ifdef WIN32 /* Winsock */ int ttl; #else PRUint8 ttl; #endif ttl = data->value.mcast_ttl; rv = _PR_MD_SETSOCKOPT( fd, level, name, (char*)&ttl, sizeof(ttl)); break; } #ifdef IP_ADD_MEMBERSHIP case PR_SockOpt_AddMember: case PR_SockOpt_DropMember: { struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = data->value.add_member.mcaddr.inet.ip; mreq.imr_interface.s_addr = data->value.add_member.ifaddr.inet.ip; rv = _PR_MD_SETSOCKOPT( fd, level, name, (char*)&mreq, sizeof(mreq)); break; } #endif /* IP_ADD_MEMBERSHIP */ case PR_SockOpt_McastInterface: { /* This option is a struct in_addr. */ rv = _PR_MD_SETSOCKOPT( fd, level, name, (char*)&data->value.mcast_if.inet.ip, sizeof(data->value.mcast_if.inet.ip)); break; } case PR_SockOpt_DontFrag: { #if !defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID) PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0); rv = PR_FAILURE; #else #if defined(WIN32) /* Winsock */ DWORD value; value = (data->value.dont_fragment) ? 1 : 0; #elif defined(LINUX) || defined(ANDROID) PRIntn value; value = (data->value.dont_fragment) ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT; #elif defined(DARWIN) PRIntn value; value = data->value.dont_fragment; #endif rv = _PR_MD_SETSOCKOPT( fd, level, name, (char*)&value, sizeof(value)); #endif /* !(!defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)) */ break; } default: PR_NOT_REACHED("Unknown socket option"); break; } } return rv; } /* _PR_SocketSetSocketOption */ #endif /* ! _PR_PTHREADS */ /* ********************************************************************* ********************************************************************* ** ** Make sure that the following is at the end of this file, ** because we will be playing with macro redefines. ** ********************************************************************* ********************************************************************* */ /* * Not every platform has all the socket options we want to * support. Some older operating systems such as SunOS 4.1.3 * don't have the IP multicast socket options. Win32 doesn't * have TCP_MAXSEG. * * To deal with this problem, we define the missing socket * options as _PR_NO_SUCH_SOCKOPT. _PR_MapOptionName() fails with * PR_OPERATION_NOT_SUPPORTED_ERROR if a socket option not * available on the platform is requested. */ /* * Sanity check. SO_LINGER and TCP_NODELAY should be available * on all platforms. Just to make sure we have included the * appropriate header files. Then any undefined socket options * are really missing. */ #if !defined(SO_LINGER) #error "SO_LINGER is not defined" #endif #if !defined(TCP_NODELAY) #error "TCP_NODELAY is not defined" #endif /* * Make sure the value of _PR_NO_SUCH_SOCKOPT is not * a valid socket option. */ #define _PR_NO_SUCH_SOCKOPT -1 #ifndef SO_KEEPALIVE #define SO_KEEPALIVE _PR_NO_SUCH_SOCKOPT #endif #ifndef SO_SNDBUF #define SO_SNDBUF _PR_NO_SUCH_SOCKOPT #endif #ifndef SO_RCVBUF #define SO_RCVBUF _PR_NO_SUCH_SOCKOPT #endif #ifndef IP_MULTICAST_IF /* set/get IP multicast interface */ #define IP_MULTICAST_IF _PR_NO_SUCH_SOCKOPT #endif #ifndef IP_MULTICAST_TTL /* set/get IP multicast timetolive */ #define IP_MULTICAST_TTL _PR_NO_SUCH_SOCKOPT #endif #ifndef IP_MULTICAST_LOOP /* set/get IP multicast loopback */ #define IP_MULTICAST_LOOP _PR_NO_SUCH_SOCKOPT #endif #ifndef IP_ADD_MEMBERSHIP /* add an IP group membership */ #define IP_ADD_MEMBERSHIP _PR_NO_SUCH_SOCKOPT #endif #ifndef IP_DROP_MEMBERSHIP /* drop an IP group membership */ #define IP_DROP_MEMBERSHIP _PR_NO_SUCH_SOCKOPT #endif #ifndef IP_TTL /* set/get IP Time To Live */ #define IP_TTL _PR_NO_SUCH_SOCKOPT #endif #ifndef IP_TOS /* set/get IP Type Of Service */ #define IP_TOS _PR_NO_SUCH_SOCKOPT #endif /* set/get IP do not fragment */ #if defined(WIN32) #ifndef IP_DONTFRAGMENT #define IP_DONTFRAGMENT _PR_NO_SUCH_SOCKOPT #endif #elif defined(LINUX) || defined(ANDROID) #ifndef IP_MTU_DISCOVER #define IP_MTU_DISCOVER _PR_NO_SUCH_SOCKOPT #endif #elif defined(DARWIN) #ifndef IP_DONTFRAG #define IP_DONTFRAG _PR_NO_SUCH_SOCKOPT #endif #endif #ifndef TCP_NODELAY /* don't delay to coalesce data */ #define TCP_NODELAY _PR_NO_SUCH_SOCKOPT #endif #ifndef TCP_MAXSEG /* maxumum segment size for tcp */ #define TCP_MAXSEG _PR_NO_SUCH_SOCKOPT #endif #ifndef SO_BROADCAST /* enable broadcast on UDP sockets */ #define SO_BROADCAST _PR_NO_SUCH_SOCKOPT #endif #ifndef SO_REUSEPORT /* allow local address & port reuse */ #define SO_REUSEPORT _PR_NO_SUCH_SOCKOPT #endif PRStatus _PR_MapOptionName( PRSockOption optname, PRInt32 *level, PRInt32 *name) { static PRInt32 socketOptions[PR_SockOpt_Last] = { 0, SO_LINGER, SO_REUSEADDR, SO_KEEPALIVE, SO_RCVBUF, SO_SNDBUF, IP_TTL, IP_TOS, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_MULTICAST_IF, IP_MULTICAST_TTL, IP_MULTICAST_LOOP, TCP_NODELAY, TCP_MAXSEG, SO_BROADCAST, SO_REUSEPORT, #if defined(WIN32) IP_DONTFRAGMENT, #elif defined(LINUX) || defined(ANDROID) IP_MTU_DISCOVER, #elif defined(DARWIN) IP_DONTFRAG, #else _PR_NO_SUCH_SOCKOPT, #endif }; static PRInt32 socketLevels[PR_SockOpt_Last] = { 0, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, IPPROTO_TCP, IPPROTO_TCP, SOL_SOCKET, SOL_SOCKET, IPPROTO_IP }; if ((optname < PR_SockOpt_Linger) || (optname >= PR_SockOpt_Last)) { PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); return PR_FAILURE; } if (socketOptions[optname] == _PR_NO_SUCH_SOCKOPT) { PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0); return PR_FAILURE; } *name = socketOptions[optname]; *level = socketLevels[optname]; return PR_SUCCESS; } /* _PR_MapOptionName */