summaryrefslogtreecommitdiff
path: root/mozilla/nsprpub/pr/src/md/windows/w95sock.c
diff options
context:
space:
mode:
Diffstat (limited to 'mozilla/nsprpub/pr/src/md/windows/w95sock.c')
-rw-r--r--mozilla/nsprpub/pr/src/md/windows/w95sock.c798
1 files changed, 798 insertions, 0 deletions
diff --git a/mozilla/nsprpub/pr/src/md/windows/w95sock.c b/mozilla/nsprpub/pr/src/md/windows/w95sock.c
new file mode 100644
index 0000000..cb39da8
--- /dev/null
+++ b/mozilla/nsprpub/pr/src/md/windows/w95sock.c
@@ -0,0 +1,798 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* Win95 Sockets module
+ *
+ */
+
+#include "primpl.h"
+
+#define READ_FD 1
+#define WRITE_FD 2
+#define CONNECT_FD 3
+
+static PRInt32 socket_io_wait(
+ PROsfd osfd,
+ PRInt32 fd_type,
+ PRIntervalTime timeout);
+
+
+/* --- SOCKET IO --------------------------------------------------------- */
+
+/*
+ * we only want to call WSAIoctl() on Vista and later
+ * so don't pay for it at build time (and avoid including winsock2.h)
+ */
+
+/* from ws2def.h */
+#define IOC_IN 0x80000000 /* copy in parameters */
+#define IOC_VENDOR 0x18000000
+#define _WSAIOW(x,y) (IOC_IN|(x)|(y))
+/* from MSWSockDef.h */
+#define SIO_SET_COMPATIBILITY_MODE _WSAIOW(IOC_VENDOR,300)
+
+typedef enum _WSA_COMPATIBILITY_BEHAVIOR_ID {
+ WsaBehaviorAll = 0,
+ WsaBehaviorReceiveBuffering,
+ WsaBehaviorAutoTuning
+} WSA_COMPATIBILITY_BEHAVIOR_ID, *PWSA_COMPATIBILITY_BEHAVIOR_ID;
+
+/* from sdkddkver.h */
+#define NTDDI_WIN6 0x06000000 /* Windows Vista */
+
+/* from winsock2.h */
+#define WSAEVENT HANDLE
+
+#define WSAOVERLAPPED OVERLAPPED
+typedef struct _OVERLAPPED * LPWSAOVERLAPPED;
+
+typedef void (CALLBACK * LPWSAOVERLAPPED_COMPLETION_ROUTINE)(
+ IN DWORD dwError,
+ IN DWORD cbTransferred,
+ IN LPWSAOVERLAPPED lpOverlapped,
+ IN DWORD dwFlags
+);
+
+typedef int (__stdcall * WSAIOCTLPROC) (
+ SOCKET s,
+ DWORD dwIoControlCode,
+ LPVOID lpvInBuffer,
+ DWORD cbInBuffer,
+ LPVOID lpvOutBuffer,
+ DWORD cbOutBuffer,
+ LPDWORD lpcbBytesReturned,
+ LPWSAOVERLAPPED lpOverlapped,
+ LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
+);
+
+typedef struct _WSA_COMPATIBILITY_MODE {
+ WSA_COMPATIBILITY_BEHAVIOR_ID BehaviorId;
+ ULONG TargetOsVersion;
+} WSA_COMPATIBILITY_MODE, *PWSA_COMPATIBILITY_MODE;
+
+static HMODULE libWinsock2 = NULL;
+static WSAIOCTLPROC wsaioctlProc = NULL;
+static PRBool socketSetCompatMode = PR_FALSE;
+static PRBool socketFixInet6RcvBuf = PR_FALSE;
+
+void _PR_MD_InitSockets(void)
+{
+ OSVERSIONINFO osvi;
+
+ memset(&osvi, 0, sizeof(osvi));
+ osvi.dwOSVersionInfoSize = sizeof(osvi);
+ GetVersionEx(&osvi);
+
+ /* if Vista or later... */
+ if (osvi.dwMajorVersion >= 6)
+ {
+ libWinsock2 = LoadLibraryW(L"Ws2_32.dll");
+ if (libWinsock2)
+ {
+ wsaioctlProc = (WSAIOCTLPROC)GetProcAddress(libWinsock2,
+ "WSAIoctl");
+ if (wsaioctlProc)
+ {
+ socketSetCompatMode = PR_TRUE;
+ }
+ }
+ }
+ else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
+ {
+ /* if Windows XP (32-bit) */
+ socketFixInet6RcvBuf = PR_TRUE;
+ }
+}
+
+void _PR_MD_CleanupSockets(void)
+{
+ socketSetCompatMode = PR_FALSE;
+ wsaioctlProc = NULL;
+ if (libWinsock2)
+ {
+ FreeLibrary(libWinsock2);
+ libWinsock2 = NULL;
+ }
+}
+
+PROsfd
+_PR_MD_SOCKET(int af, int type, int flags)
+{
+ SOCKET sock;
+ u_long one = 1;
+
+ sock = socket(af, type, flags);
+
+ if (sock == INVALID_SOCKET )
+ {
+ _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError());
+ return (PROsfd)sock;
+ }
+
+ /*
+ ** Make the socket Non-Blocking
+ */
+ if (ioctlsocket( sock, FIONBIO, &one) != 0)
+ {
+ PR_SetError(PR_UNKNOWN_ERROR, WSAGetLastError());
+ closesocket(sock);
+ return -1;
+ }
+
+ if ((af == AF_INET || af == AF_INET6) &&
+ type == SOCK_STREAM && socketSetCompatMode)
+ {
+ WSA_COMPATIBILITY_MODE mode;
+ char dummy[4];
+ int ret_dummy;
+
+ mode.BehaviorId = WsaBehaviorAutoTuning;
+ mode.TargetOsVersion = NTDDI_WIN6;
+ if (wsaioctlProc(sock, SIO_SET_COMPATIBILITY_MODE,
+ (char *)&mode, sizeof(mode),
+ dummy, 4, &ret_dummy, 0, NULL) == SOCKET_ERROR)
+ {
+ int err = WSAGetLastError();
+ PR_LOG(_pr_io_lm, PR_LOG_DEBUG, ("WSAIoctl() failed with %d", err));
+
+ /* SIO_SET_COMPATIBILITY_MODE may not be supported.
+ ** If the call to WSAIoctl() fails with WSAEOPNOTSUPP,
+ ** don't close the socket.
+ */
+ }
+ }
+
+ if (af == AF_INET6 && socketFixInet6RcvBuf)
+ {
+ int bufsize;
+ int len = sizeof(bufsize);
+ int rv;
+
+ /* Windows XP 32-bit returns an error on getpeername() for AF_INET6
+ * sockets if the receive buffer size is greater than 65535 before
+ * the connection is initiated. The default receive buffer size may
+ * be 128000 so fix it here to always be <= 65535. See bug 513659
+ * and IBM DB2 support technote "Receive/Send IPv6 Socket Size
+ * Problem in Windows XP SP2 & SP3".
+ */
+ rv = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, &len);
+ if (rv == 0 && bufsize > 65535)
+ {
+ bufsize = 65535;
+ setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, len);
+ }
+ }
+
+ return (PROsfd)sock;
+}
+
+/*
+** _MD_CloseSocket() -- Close a socket
+**
+*/
+PRInt32
+_MD_CloseSocket(PROsfd osfd)
+{
+ PRInt32 rv;
+
+ rv = closesocket((SOCKET) osfd );
+ if (rv < 0)
+ _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError());
+
+ return rv;
+}
+
+PRInt32
+_MD_SocketAvailable(PRFileDesc *fd)
+{
+ PRInt32 result;
+
+ if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
+ return -1;
+ }
+ return result;
+}
+
+PROsfd _MD_Accept(
+ PRFileDesc *fd,
+ PRNetAddr *raddr,
+ PRUint32 *rlen,
+ PRIntervalTime timeout )
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ SOCKET sock;
+ PRInt32 rv, err;
+
+ while ((sock = accept(osfd, (struct sockaddr *) raddr, rlen)) == -1)
+ {
+ err = WSAGetLastError();
+ if ((err == WSAEWOULDBLOCK) && (!fd->secret->nonblocking))
+ {
+ if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0)
+ {
+ break;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ break;
+ }
+ }
+ return(sock);
+} /* end _MD_accept() */
+
+PRInt32
+_PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
+ PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv;
+ int err;
+
+ if ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1)
+ {
+ err = WSAGetLastError();
+ if ((!fd->secret->nonblocking) && (err == WSAEWOULDBLOCK))
+ {
+ rv = socket_io_wait(osfd, CONNECT_FD, timeout);
+ if ( rv < 0 )
+ {
+ return(-1);
+ }
+ else
+ {
+ PR_ASSERT(rv > 0);
+ /* it's connected */
+ return(0);
+ }
+ }
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
+{
+ PRInt32 rv;
+
+ rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen);
+
+ if (rv == SOCKET_ERROR) {
+ _PR_MD_MAP_BIND_ERROR(WSAGetLastError());
+ return -1;
+ }
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog)
+{
+ PRInt32 rv;
+
+ rv = listen(fd->secret->md.osfd, backlog);
+
+ if (rv == SOCKET_ERROR) {
+ _PR_MD_MAP_DEFAULT_ERROR(WSAGetLastError());
+ return -1;
+ }
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ int osflags;
+
+ if (0 == flags) {
+ osflags = 0;
+ } else {
+ PR_ASSERT(PR_MSG_PEEK == flags);
+ osflags = MSG_PEEK;
+ }
+ while ((rv = recv( osfd, buf, amount, osflags)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ rv = socket_io_wait(osfd, READ_FD, timeout);
+ if ( rv < 0 )
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_RECV_ERROR(err);
+ break;
+ }
+ } /* end while() */
+ return(rv);
+}
+
+PRInt32
+_PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRInt32 bytesSent = 0;
+
+ while(bytesSent < amount )
+ {
+ while ((rv = send( osfd, buf, amount, 0 )) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if ( rv < 0 )
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_SEND_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking)
+ {
+ break;
+ }
+ if (bytesSent < amount)
+ {
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if ( rv < 0 )
+ {
+ return -1;
+ }
+ }
+ }
+ return bytesSent;
+}
+
+PRInt32
+_PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRInt32 bytesSent = 0;
+
+ while(bytesSent < amount)
+ {
+ while ((rv = sendto( osfd, buf, amount, 0, (struct sockaddr *) addr,
+ addrlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if ( rv < 0 )
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking)
+ {
+ break;
+ }
+ if (bytesSent < amount)
+ {
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if (rv < 0)
+ {
+ return -1;
+ }
+ }
+ }
+ return bytesSent;
+}
+
+PRInt32
+_PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+
+ while ((rv = recvfrom( osfd, buf, amount, 0, (struct sockaddr *) addr,
+ addrlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ rv = socket_io_wait(osfd, READ_FD, timeout);
+ if ( rv < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+}
+
+PRInt32
+_PR_MD_WRITEV(PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
+{
+ int index;
+ int sent = 0;
+ int rv;
+
+ for (index=0; index < iov_size; index++)
+ {
+ rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0, timeout);
+ if (rv > 0)
+ sent += rv;
+ if ( rv != iov[index].iov_len )
+ {
+ if (rv < 0)
+ {
+ if (fd->secret->nonblocking
+ && (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+ && (sent > 0))
+ {
+ return sent;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ /* Only a nonblocking socket can have partial sends */
+ PR_ASSERT(fd->secret->nonblocking);
+ return sent;
+ }
+ }
+ return sent;
+}
+
+PRInt32
+_PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
+{
+PRInt32 rv;
+
+ rv = shutdown(fd->secret->md.osfd, how);
+ if (rv < 0)
+ _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError());
+ return rv;
+}
+
+PRStatus
+_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
+ if (rv==0) {
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getpeername((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
+ if (rv==0) {
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
+{
+ PRInt32 rv;
+
+ rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0) {
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
+{
+ PRInt32 rv;
+
+ rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0) {
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+void
+_MD_MakeNonblock(PRFileDesc *f)
+{
+ return; /* do nothing */
+}
+
+
+
+/*
+ * socket_io_wait --
+ *
+ * Wait for socket i/o, periodically checking for interrupt.
+ *
+ * This function returns 1 on success. On failure, it returns
+ * -1 and sets the error codes. It never returns 0.
+ */
+#define _PR_INTERRUPT_CHECK_INTERVAL_SECS 5
+
+static PRInt32 socket_io_wait(
+ PROsfd osfd,
+ PRInt32 fd_type,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv = -1;
+ struct timeval tv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRIntervalTime elapsed, remaining;
+ PRBool wait_for_remaining;
+ fd_set rd_wr, ex;
+ int err, len;
+
+ switch (timeout) {
+ case PR_INTERVAL_NO_WAIT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ break;
+ case PR_INTERVAL_NO_TIMEOUT:
+ /*
+ * This is a special case of the 'default' case below.
+ * Please see the comments there.
+ */
+ tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
+ tv.tv_usec = 0;
+ FD_ZERO(&rd_wr);
+ FD_ZERO(&ex);
+ do {
+ FD_SET(osfd, &rd_wr);
+ FD_SET(osfd, &ex);
+ switch( fd_type )
+ {
+ case READ_FD:
+ rv = _MD_SELECT(0, &rd_wr, NULL, NULL, &tv);
+ break;
+ case WRITE_FD:
+ rv = _MD_SELECT(0, NULL, &rd_wr, NULL, &tv);
+ break;
+ case CONNECT_FD:
+ rv = _MD_SELECT(0, NULL, &rd_wr, &ex, &tv);
+ break;
+ default:
+ PR_ASSERT(0);
+ break;
+ } /* end switch() */
+ if (rv == -1 )
+ {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if ( rv > 0 && fd_type == CONNECT_FD )
+ {
+ /*
+ * Call Sleep(0) to work around a Winsock timing bug.
+ */
+ Sleep(0);
+ if (FD_ISSET((SOCKET)osfd, &ex))
+ {
+ len = sizeof(err);
+ if (getsockopt(osfd, SOL_SOCKET, SO_ERROR,
+ (char *) &err, &len) == SOCKET_ERROR)
+ {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return -1;
+ }
+ if (err != 0)
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ else
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return -1;
+ }
+ if (FD_ISSET((SOCKET)osfd, &rd_wr))
+ {
+ /* it's connected */
+ return 1;
+ }
+ PR_ASSERT(0);
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ } while (rv == 0);
+ break;
+ default:
+ remaining = timeout;
+ FD_ZERO(&rd_wr);
+ FD_ZERO(&ex);
+ do {
+ /*
+ * We block in _MD_SELECT for at most
+ * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds,
+ * so that there is an upper limit on the delay
+ * before the interrupt bit is checked.
+ */
+ wait_for_remaining = PR_TRUE;
+ tv.tv_sec = PR_IntervalToSeconds(remaining);
+ if (tv.tv_sec > _PR_INTERRUPT_CHECK_INTERVAL_SECS) {
+ wait_for_remaining = PR_FALSE;
+ tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
+ tv.tv_usec = 0;
+ } else {
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ remaining -
+ PR_SecondsToInterval(tv.tv_sec));
+ }
+ FD_SET(osfd, &rd_wr);
+ FD_SET(osfd, &ex);
+ switch( fd_type )
+ {
+ case READ_FD:
+ rv = _MD_SELECT(0, &rd_wr, NULL, NULL, &tv);
+ break;
+ case WRITE_FD:
+ rv = _MD_SELECT(0, NULL, &rd_wr, NULL, &tv);
+ break;
+ case CONNECT_FD:
+ rv = _MD_SELECT(0, NULL, &rd_wr, &ex, &tv);
+ break;
+ default:
+ PR_ASSERT(0);
+ break;
+ } /* end switch() */
+ if (rv == -1)
+ {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if ( rv > 0 && fd_type == CONNECT_FD )
+ {
+ /*
+ * Call Sleep(0) to work around a Winsock timing bug.
+ */
+ Sleep(0);
+ if (FD_ISSET((SOCKET)osfd, &ex))
+ {
+ len = sizeof(err);
+ if (getsockopt(osfd, SOL_SOCKET, SO_ERROR,
+ (char *) &err, &len) == SOCKET_ERROR)
+ {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return -1;
+ }
+ if (err != 0)
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ else
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return -1;
+ }
+ if (FD_ISSET((SOCKET)osfd, &rd_wr))
+ {
+ /* it's connected */
+ return 1;
+ }
+ PR_ASSERT(0);
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ /*
+ * We loop again if _MD_SELECT timed out and the
+ * timeout deadline has not passed yet.
+ */
+ if (rv == 0 )
+ {
+ if (wait_for_remaining) {
+ elapsed = remaining;
+ } else {
+ elapsed = PR_SecondsToInterval(tv.tv_sec)
+ + PR_MicrosecondsToInterval(tv.tv_usec);
+ }
+ if (elapsed >= remaining) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ } else {
+ remaining = remaining - elapsed;
+ }
+ }
+ } while (rv == 0 );
+ break;
+ }
+ return(rv);
+} /* end socket_io_wait() */