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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Win95 Sockets module
*
*/
#if defined(_WIN64)
# include <winsock2.h>
#endif
#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 --------------------------------------------------------- */
static PRBool socketFixInet6RcvBuf = PR_FALSE;
void _PR_MD_InitSockets(void) {
OSVERSIONINFO osvi;
memset(&osvi, 0, sizeof(osvi));
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx(&osvi);
if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
/* if Windows XP (32-bit) */
socketFixInet6RcvBuf = PR_TRUE;
}
}
void _PR_MD_CleanupSockets(void) { socketFixInet6RcvBuf = PR_FALSE; }
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_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;
do {
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;
}
}
} while (bytesSent < amount);
return bytesSent;
}
#if defined(_WIN64)
static PRCallOnceType _pr_has_connectex_once;
typedef BOOL(PASCAL FAR* _pr_win_connectex_ptr)(
_In_ SOCKET s, _In_reads_bytes_(namelen) const struct sockaddr FAR* name,
_In_ int namelen, _In_reads_bytes_opt_(dwSendDataLength) PVOID lpSendBuffer,
_In_ DWORD dwSendDataLength, _Out_ LPDWORD lpdwBytesSent,
_Inout_ LPOVERLAPPED lpOverlapped);
# ifndef WSAID_CONNECTEX
# define WSAID_CONNECTEX \
{ \
0x25a207b9, 0xddf3, 0x4660, { \
0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e \
} \
}
# endif
# ifndef SIO_GET_EXTENSION_FUNCTION_POINTER
# define SIO_GET_EXTENSION_FUNCTION_POINTER 0xC8000006
# endif
# ifndef TCP_FASTOPEN
# define TCP_FASTOPEN 15
# endif
# ifndef SO_UPDATE_CONNECT_CONTEXT
# define SO_UPDATE_CONNECT_CONTEXT 0x7010
# endif
static _pr_win_connectex_ptr _pr_win_connectex = NULL;
static PRStatus PR_CALLBACK _pr_set_connectex(void) {
_pr_win_connectex = NULL;
SOCKET sock;
PRInt32 dwBytes;
int rc;
/* Dummy socket needed for WSAIoctl */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
return PR_SUCCESS;
}
GUID guid = WSAID_CONNECTEX;
rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid),
&_pr_win_connectex, sizeof(_pr_win_connectex), &dwBytes, NULL,
NULL);
if (rc != 0) {
_pr_win_connectex = NULL;
return PR_SUCCESS;
}
rc = closesocket(sock);
return PR_SUCCESS;
}
PRInt32 _PR_MD_TCPSENDTO(PRFileDesc* fd, const void* buf, PRInt32 amount,
PRIntn flags, const PRNetAddr* addr, PRUint32 addrlen,
PRIntervalTime timeout) {
if (!_fd_waiting_for_overlapped_done_lock) {
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
return PR_FAILURE;
}
if (PR_CallOnce(&_pr_has_connectex_once, _pr_set_connectex) != PR_SUCCESS) {
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
return PR_FAILURE;
}
if (_pr_win_connectex == NULL) {
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
return PR_FAILURE;
}
PROsfd osfd = fd->secret->md.osfd;
PRInt32 rv, err;
PRInt32 bytesSent = 0;
DWORD rvSent;
BOOL option = 1;
rv = setsockopt((SOCKET)osfd, IPPROTO_TCP, TCP_FASTOPEN, (char*)&option,
sizeof(option));
if (rv != 0) {
err = WSAGetLastError();
PR_LOG(_pr_io_lm, PR_LOG_MIN,
("_PR_MD_TCPSENDTO error set opt TCP_FASTOPEN failed %d\n", err));
if (err == WSAENOPROTOOPT) {
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
} else {
_PR_MD_MAP_SETSOCKOPT_ERROR(err);
}
return -1;
}
/* ConnectEx requires the socket to be initially bound. We will use
* INADDR_ANY. */
PRNetAddr bindAddr;
memset(&bindAddr, 0, sizeof(bindAddr));
bindAddr.raw.family = addr->raw.family;
rv = bind((SOCKET)osfd, (const struct sockaddr*)&(bindAddr.inet),
PR_NETADDR_SIZE(&bindAddr));
if (rv != 0) {
err = WSAGetLastError();
PR_LOG(_pr_io_lm, PR_LOG_MIN,
("_PR_MD_TCPSENDTO error bind failed %d\n", err));
_PR_MD_MAP_SETSOCKOPT_ERROR(err);
return -1;
}
PR_LOG(_pr_io_lm, PR_LOG_MIN,
("_PR_MD_TCPSENDTO calling _pr_win_connectex %d %p\n", amount,
(char*)buf));
rvSent = 0;
memset(&fd->secret->ol, 0, sizeof(fd->secret->ol));
/* ConnectEx return TRUE on a success and FALSE on an error. */
if (_pr_win_connectex((SOCKET)osfd, (struct sockaddr*)addr, addrlen, buf,
amount, &rvSent, &fd->secret->ol) == TRUE) {
/* When ConnectEx is used, all previously set socket options and
* property are not enabled and to enable them
* SO_UPDATE_CONNECT_CONTEXT option need to be set. */
rv = setsockopt((SOCKET)osfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL,
0);
if (rv != 0) {
err = WSAGetLastError();
PR_LOG(_pr_io_lm, PR_LOG_MIN,
("_PR_MD_TCPSENDTO setting SO_UPDATE_CONNECT_CONTEXT failed %d\n",
err));
_PR_MD_MAP_SETSOCKOPT_ERROR(err);
return -1;
}
/* We imitate Linux here. SendTo will return number of bytes send but
* it can not return connection success at the same time, so we return
* number of bytes send and "connection success" will be return on the
* connectcontinue. */
fd->secret->alreadyConnected = PR_TRUE;
return rvSent;
} else {
err = WSAGetLastError();
PR_LOG(_pr_io_lm, PR_LOG_MIN,
("_PR_MD_TCPSENDTO error _pr_win_connectex failed %d\n", err));
if (err != ERROR_IO_PENDING) {
_PR_MD_MAP_CONNECT_ERROR(err);
return -1;
} else if (fd->secret->nonblocking) {
/* Remember that overlapped structure is set. We will need to get
* the final result of ConnectEx call. */
fd->secret->overlappedActive = PR_TRUE;
/* ConnectEx will copy supplied data to a internal buffer and send
* them during Fast Open or after connect. Therefore we can assumed
* this data already send. */
if (amount > 0) {
return amount;
}
_PR_MD_MAP_CONNECT_ERROR(WSAEWOULDBLOCK);
return -1;
}
// err is ERROR_IO_PENDING and socket is blocking, so query
// GetOverlappedResult.
err = ERROR_IO_INCOMPLETE;
while (err == ERROR_IO_INCOMPLETE) {
rv = socket_io_wait(osfd, WRITE_FD, timeout);
if (rv < 0) {
return -1;
}
rv = GetOverlappedResult((HANDLE)osfd, &fd->secret->ol, &rvSent, FALSE);
if (rv == TRUE) {
return rvSent;
} else {
err = WSAGetLastError();
if (err != ERROR_IO_INCOMPLETE) {
_PR_MD_MAP_CONNECT_ERROR(err);
return -1;
}
}
}
}
return -1;
}
#endif
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() */