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