Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
/*
* 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 <winsock.h>
#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 <netinet/in.h>
#endif
#ifdef DARWIN
# include <netinet/in.h>
# include <netinet/ip.h>
#endif
#ifdef HAVE_NETINET_TCP_H
# include <netinet/tcp.h> /* 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 */