summaryrefslogtreecommitdiffstats
path: root/nsprpub/pr/src/io/prmapopt.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--nsprpub/pr/src/io/prmapopt.c530
1 files changed, 530 insertions, 0 deletions
diff --git a/nsprpub/pr/src/io/prmapopt.c b/nsprpub/pr/src/io/prmapopt.c
new file mode 100644
index 0000000000..375a73d2a9
--- /dev/null
+++ b/nsprpub/pr/src/io/prmapopt.c
@@ -0,0 +1,530 @@
+/* -*- 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 <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 */