andre@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: /* Win95 Sockets module andre@0: * andre@0: */ andre@0: andre@0: #include "primpl.h" andre@0: andre@0: #define READ_FD 1 andre@0: #define WRITE_FD 2 andre@0: #define CONNECT_FD 3 andre@0: andre@0: static PRInt32 socket_io_wait( andre@0: PROsfd osfd, andre@0: PRInt32 fd_type, andre@0: PRIntervalTime timeout); andre@0: andre@0: andre@0: /* --- SOCKET IO --------------------------------------------------------- */ andre@0: andre@0: static PRBool socketFixInet6RcvBuf = PR_FALSE; andre@0: andre@0: void _PR_MD_InitSockets(void) andre@0: { andre@0: OSVERSIONINFO osvi; andre@0: andre@0: memset(&osvi, 0, sizeof(osvi)); andre@0: osvi.dwOSVersionInfoSize = sizeof(osvi); andre@0: GetVersionEx(&osvi); andre@0: andre@0: if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) andre@0: { andre@0: /* if Windows XP (32-bit) */ andre@0: socketFixInet6RcvBuf = PR_TRUE; andre@0: } andre@0: } andre@0: andre@0: void _PR_MD_CleanupSockets(void) andre@0: { andre@0: socketFixInet6RcvBuf = PR_FALSE; andre@0: } andre@0: andre@0: PROsfd andre@0: _PR_MD_SOCKET(int af, int type, int flags) andre@0: { andre@0: SOCKET sock; andre@0: u_long one = 1; andre@0: andre@0: sock = socket(af, type, flags); andre@0: andre@0: if (sock == INVALID_SOCKET ) andre@0: { andre@0: _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError()); andre@0: return (PROsfd)sock; andre@0: } andre@0: andre@0: /* andre@0: ** Make the socket Non-Blocking andre@0: */ andre@0: if (ioctlsocket( sock, FIONBIO, &one) != 0) andre@0: { andre@0: PR_SetError(PR_UNKNOWN_ERROR, WSAGetLastError()); andre@0: closesocket(sock); andre@0: return -1; andre@0: } andre@0: andre@0: if (af == AF_INET6 && socketFixInet6RcvBuf) andre@0: { andre@0: int bufsize; andre@0: int len = sizeof(bufsize); andre@0: int rv; andre@0: andre@0: /* Windows XP 32-bit returns an error on getpeername() for AF_INET6 andre@0: * sockets if the receive buffer size is greater than 65535 before andre@0: * the connection is initiated. The default receive buffer size may andre@0: * be 128000 so fix it here to always be <= 65535. See bug 513659 andre@0: * and IBM DB2 support technote "Receive/Send IPv6 Socket Size andre@0: * Problem in Windows XP SP2 & SP3". andre@0: */ andre@0: rv = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, &len); andre@0: if (rv == 0 && bufsize > 65535) andre@0: { andre@0: bufsize = 65535; andre@0: setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, len); andre@0: } andre@0: } andre@0: andre@0: return (PROsfd)sock; andre@0: } andre@0: andre@0: /* andre@0: ** _MD_CloseSocket() -- Close a socket andre@0: ** andre@0: */ andre@0: PRInt32 andre@0: _MD_CloseSocket(PROsfd osfd) andre@0: { andre@0: PRInt32 rv; andre@0: andre@0: rv = closesocket((SOCKET) osfd ); andre@0: if (rv < 0) andre@0: _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError()); andre@0: andre@0: return rv; andre@0: } andre@0: andre@0: PRInt32 andre@0: _MD_SocketAvailable(PRFileDesc *fd) andre@0: { andre@0: PRInt32 result; andre@0: andre@0: if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) { andre@0: PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError()); andre@0: return -1; andre@0: } andre@0: return result; andre@0: } andre@0: andre@0: PROsfd _MD_Accept( andre@0: PRFileDesc *fd, andre@0: PRNetAddr *raddr, andre@0: PRUint32 *rlen, andre@0: PRIntervalTime timeout ) andre@0: { andre@0: PROsfd osfd = fd->secret->md.osfd; andre@0: SOCKET sock; andre@0: PRInt32 rv, err; andre@0: andre@0: while ((sock = accept(osfd, (struct sockaddr *) raddr, rlen)) == -1) andre@0: { andre@0: err = WSAGetLastError(); andre@0: if ((err == WSAEWOULDBLOCK) && (!fd->secret->nonblocking)) andre@0: { andre@0: if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) andre@0: { andre@0: break; andre@0: } andre@0: } andre@0: else andre@0: { andre@0: _PR_MD_MAP_ACCEPT_ERROR(err); andre@0: break; andre@0: } andre@0: } andre@0: return(sock); andre@0: } /* end _MD_accept() */ andre@0: andre@0: PRInt32 andre@0: _PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen, andre@0: PRIntervalTime timeout) andre@0: { andre@0: PROsfd osfd = fd->secret->md.osfd; andre@0: PRInt32 rv; andre@0: int err; andre@0: andre@0: if ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1) andre@0: { andre@0: err = WSAGetLastError(); andre@0: if ((!fd->secret->nonblocking) && (err == WSAEWOULDBLOCK)) andre@0: { andre@0: rv = socket_io_wait(osfd, CONNECT_FD, timeout); andre@0: if ( rv < 0 ) andre@0: { andre@0: return(-1); andre@0: } andre@0: else andre@0: { andre@0: PR_ASSERT(rv > 0); andre@0: /* it's connected */ andre@0: return(0); andre@0: } andre@0: } andre@0: _PR_MD_MAP_CONNECT_ERROR(err); andre@0: } andre@0: return rv; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen) andre@0: { andre@0: PRInt32 rv; andre@0: andre@0: rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen); andre@0: andre@0: if (rv == SOCKET_ERROR) { andre@0: _PR_MD_MAP_BIND_ERROR(WSAGetLastError()); andre@0: return -1; andre@0: } andre@0: andre@0: return 0; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog) andre@0: { andre@0: PRInt32 rv; andre@0: andre@0: rv = listen(fd->secret->md.osfd, backlog); andre@0: andre@0: if (rv == SOCKET_ERROR) { andre@0: _PR_MD_MAP_DEFAULT_ERROR(WSAGetLastError()); andre@0: return -1; andre@0: } andre@0: andre@0: return 0; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, andre@0: PRIntervalTime timeout) andre@0: { andre@0: PROsfd osfd = fd->secret->md.osfd; andre@0: PRInt32 rv, err; andre@0: int osflags; andre@0: andre@0: if (0 == flags) { andre@0: osflags = 0; andre@0: } else { andre@0: PR_ASSERT(PR_MSG_PEEK == flags); andre@0: osflags = MSG_PEEK; andre@0: } andre@0: while ((rv = recv( osfd, buf, amount, osflags)) == -1) andre@0: { andre@0: if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) andre@0: && (!fd->secret->nonblocking)) andre@0: { andre@0: rv = socket_io_wait(osfd, READ_FD, timeout); andre@0: if ( rv < 0 ) andre@0: { andre@0: return -1; andre@0: } andre@0: } andre@0: else andre@0: { andre@0: _PR_MD_MAP_RECV_ERROR(err); andre@0: break; andre@0: } andre@0: } /* end while() */ andre@0: return(rv); andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, andre@0: PRIntervalTime timeout) andre@0: { andre@0: PROsfd osfd = fd->secret->md.osfd; andre@0: PRInt32 rv, err; andre@0: PRInt32 bytesSent = 0; andre@0: andre@0: while(bytesSent < amount ) andre@0: { andre@0: while ((rv = send( osfd, buf, amount, 0 )) == -1) andre@0: { andre@0: if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) andre@0: && (!fd->secret->nonblocking)) andre@0: { andre@0: rv = socket_io_wait(osfd, WRITE_FD, timeout); andre@0: if ( rv < 0 ) andre@0: { andre@0: return -1; andre@0: } andre@0: } andre@0: else andre@0: { andre@0: _PR_MD_MAP_SEND_ERROR(err); andre@0: return -1; andre@0: } andre@0: } andre@0: bytesSent += rv; andre@0: if (fd->secret->nonblocking) andre@0: { andre@0: break; andre@0: } andre@0: if (bytesSent < amount) andre@0: { andre@0: rv = socket_io_wait(osfd, WRITE_FD, timeout); andre@0: if ( rv < 0 ) andre@0: { andre@0: return -1; andre@0: } andre@0: } andre@0: } andre@0: return bytesSent; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, andre@0: const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout) andre@0: { andre@0: PROsfd osfd = fd->secret->md.osfd; andre@0: PRInt32 rv, err; andre@0: PRInt32 bytesSent = 0; andre@0: andre@0: while(bytesSent < amount) andre@0: { andre@0: while ((rv = sendto( osfd, buf, amount, 0, (struct sockaddr *) addr, andre@0: addrlen)) == -1) andre@0: { andre@0: if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) andre@0: && (!fd->secret->nonblocking)) andre@0: { andre@0: rv = socket_io_wait(osfd, WRITE_FD, timeout); andre@0: if ( rv < 0 ) andre@0: { andre@0: return -1; andre@0: } andre@0: } andre@0: else andre@0: { andre@0: _PR_MD_MAP_SENDTO_ERROR(err); andre@0: return -1; andre@0: } andre@0: } andre@0: bytesSent += rv; andre@0: if (fd->secret->nonblocking) andre@0: { andre@0: break; andre@0: } andre@0: if (bytesSent < amount) andre@0: { andre@0: rv = socket_io_wait(osfd, WRITE_FD, timeout); andre@0: if (rv < 0) andre@0: { andre@0: return -1; andre@0: } andre@0: } andre@0: } andre@0: return bytesSent; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, andre@0: PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout) andre@0: { andre@0: PROsfd osfd = fd->secret->md.osfd; andre@0: PRInt32 rv, err; andre@0: andre@0: while ((rv = recvfrom( osfd, buf, amount, 0, (struct sockaddr *) addr, andre@0: addrlen)) == -1) andre@0: { andre@0: if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) andre@0: && (!fd->secret->nonblocking)) andre@0: { andre@0: rv = socket_io_wait(osfd, READ_FD, timeout); andre@0: if ( rv < 0) andre@0: { andre@0: return -1; andre@0: } andre@0: } andre@0: else andre@0: { andre@0: _PR_MD_MAP_RECVFROM_ERROR(err); andre@0: break; andre@0: } andre@0: } andre@0: return(rv); andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_WRITEV(PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout) andre@0: { andre@0: int index; andre@0: int sent = 0; andre@0: int rv; andre@0: andre@0: for (index=0; index < iov_size; index++) andre@0: { andre@0: rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0, timeout); andre@0: if (rv > 0) andre@0: sent += rv; andre@0: if ( rv != iov[index].iov_len ) andre@0: { andre@0: if (rv < 0) andre@0: { andre@0: if (fd->secret->nonblocking andre@0: && (PR_GetError() == PR_WOULD_BLOCK_ERROR) andre@0: && (sent > 0)) andre@0: { andre@0: return sent; andre@0: } andre@0: else andre@0: { andre@0: return -1; andre@0: } andre@0: } andre@0: /* Only a nonblocking socket can have partial sends */ andre@0: PR_ASSERT(fd->secret->nonblocking); andre@0: return sent; andre@0: } andre@0: } andre@0: return sent; andre@0: } andre@0: andre@0: PRInt32 andre@0: _PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how) andre@0: { andre@0: PRInt32 rv; andre@0: andre@0: rv = shutdown(fd->secret->md.osfd, how); andre@0: if (rv < 0) andre@0: _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError()); andre@0: return rv; andre@0: } andre@0: andre@0: PRStatus andre@0: _PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len) andre@0: { andre@0: PRInt32 rv; andre@0: andre@0: rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len); andre@0: if (rv==0) { andre@0: return PR_SUCCESS; andre@0: } else { andre@0: _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: } andre@0: andre@0: PRStatus andre@0: _PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len) andre@0: { andre@0: PRInt32 rv; andre@0: andre@0: rv = getpeername((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len); andre@0: if (rv==0) { andre@0: return PR_SUCCESS; andre@0: } else { andre@0: _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: } andre@0: andre@0: PRStatus andre@0: _PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen) andre@0: { andre@0: PRInt32 rv; andre@0: andre@0: rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen); andre@0: if (rv==0) { andre@0: return PR_SUCCESS; andre@0: } else { andre@0: _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: } andre@0: andre@0: PRStatus andre@0: _PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen) andre@0: { andre@0: PRInt32 rv; andre@0: andre@0: rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen); andre@0: if (rv==0) { andre@0: return PR_SUCCESS; andre@0: } else { andre@0: _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: } andre@0: andre@0: void andre@0: _MD_MakeNonblock(PRFileDesc *f) andre@0: { andre@0: return; /* do nothing */ andre@0: } andre@0: andre@0: andre@0: andre@0: /* andre@0: * socket_io_wait -- andre@0: * andre@0: * Wait for socket i/o, periodically checking for interrupt. andre@0: * andre@0: * This function returns 1 on success. On failure, it returns andre@0: * -1 and sets the error codes. It never returns 0. andre@0: */ andre@0: #define _PR_INTERRUPT_CHECK_INTERVAL_SECS 5 andre@0: andre@0: static PRInt32 socket_io_wait( andre@0: PROsfd osfd, andre@0: PRInt32 fd_type, andre@0: PRIntervalTime timeout) andre@0: { andre@0: PRInt32 rv = -1; andre@0: struct timeval tv; andre@0: PRThread *me = _PR_MD_CURRENT_THREAD(); andre@0: PRIntervalTime elapsed, remaining; andre@0: PRBool wait_for_remaining; andre@0: fd_set rd_wr, ex; andre@0: int err, len; andre@0: andre@0: switch (timeout) { andre@0: case PR_INTERVAL_NO_WAIT: andre@0: PR_SetError(PR_IO_TIMEOUT_ERROR, 0); andre@0: break; andre@0: case PR_INTERVAL_NO_TIMEOUT: andre@0: /* andre@0: * This is a special case of the 'default' case below. andre@0: * Please see the comments there. andre@0: */ andre@0: tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS; andre@0: tv.tv_usec = 0; andre@0: FD_ZERO(&rd_wr); andre@0: FD_ZERO(&ex); andre@0: do { andre@0: FD_SET(osfd, &rd_wr); andre@0: FD_SET(osfd, &ex); andre@0: switch( fd_type ) andre@0: { andre@0: case READ_FD: andre@0: rv = _MD_SELECT(0, &rd_wr, NULL, NULL, &tv); andre@0: break; andre@0: case WRITE_FD: andre@0: rv = _MD_SELECT(0, NULL, &rd_wr, NULL, &tv); andre@0: break; andre@0: case CONNECT_FD: andre@0: rv = _MD_SELECT(0, NULL, &rd_wr, &ex, &tv); andre@0: break; andre@0: default: andre@0: PR_ASSERT(0); andre@0: break; andre@0: } /* end switch() */ andre@0: if (rv == -1 ) andre@0: { andre@0: _PR_MD_MAP_SELECT_ERROR(WSAGetLastError()); andre@0: break; andre@0: } andre@0: if ( rv > 0 && fd_type == CONNECT_FD ) andre@0: { andre@0: /* andre@0: * Call Sleep(0) to work around a Winsock timing bug. andre@0: */ andre@0: Sleep(0); andre@0: if (FD_ISSET((SOCKET)osfd, &ex)) andre@0: { andre@0: len = sizeof(err); andre@0: if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, andre@0: (char *) &err, &len) == SOCKET_ERROR) andre@0: { andre@0: _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError()); andre@0: return -1; andre@0: } andre@0: if (err != 0) andre@0: _PR_MD_MAP_CONNECT_ERROR(err); andre@0: else andre@0: PR_SetError(PR_UNKNOWN_ERROR, 0); andre@0: return -1; andre@0: } andre@0: if (FD_ISSET((SOCKET)osfd, &rd_wr)) andre@0: { andre@0: /* it's connected */ andre@0: return 1; andre@0: } andre@0: PR_ASSERT(0); andre@0: } andre@0: if (_PR_PENDING_INTERRUPT(me)) { andre@0: me->flags &= ~_PR_INTERRUPT; andre@0: PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); andre@0: rv = -1; andre@0: break; andre@0: } andre@0: } while (rv == 0); andre@0: break; andre@0: default: andre@0: remaining = timeout; andre@0: FD_ZERO(&rd_wr); andre@0: FD_ZERO(&ex); andre@0: do { andre@0: /* andre@0: * We block in _MD_SELECT for at most andre@0: * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds, andre@0: * so that there is an upper limit on the delay andre@0: * before the interrupt bit is checked. andre@0: */ andre@0: wait_for_remaining = PR_TRUE; andre@0: tv.tv_sec = PR_IntervalToSeconds(remaining); andre@0: if (tv.tv_sec > _PR_INTERRUPT_CHECK_INTERVAL_SECS) { andre@0: wait_for_remaining = PR_FALSE; andre@0: tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS; andre@0: tv.tv_usec = 0; andre@0: } else { andre@0: tv.tv_usec = PR_IntervalToMicroseconds( andre@0: remaining - andre@0: PR_SecondsToInterval(tv.tv_sec)); andre@0: } andre@0: FD_SET(osfd, &rd_wr); andre@0: FD_SET(osfd, &ex); andre@0: switch( fd_type ) andre@0: { andre@0: case READ_FD: andre@0: rv = _MD_SELECT(0, &rd_wr, NULL, NULL, &tv); andre@0: break; andre@0: case WRITE_FD: andre@0: rv = _MD_SELECT(0, NULL, &rd_wr, NULL, &tv); andre@0: break; andre@0: case CONNECT_FD: andre@0: rv = _MD_SELECT(0, NULL, &rd_wr, &ex, &tv); andre@0: break; andre@0: default: andre@0: PR_ASSERT(0); andre@0: break; andre@0: } /* end switch() */ andre@0: if (rv == -1) andre@0: { andre@0: _PR_MD_MAP_SELECT_ERROR(WSAGetLastError()); andre@0: break; andre@0: } andre@0: if ( rv > 0 && fd_type == CONNECT_FD ) andre@0: { andre@0: /* andre@0: * Call Sleep(0) to work around a Winsock timing bug. andre@0: */ andre@0: Sleep(0); andre@0: if (FD_ISSET((SOCKET)osfd, &ex)) andre@0: { andre@0: len = sizeof(err); andre@0: if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, andre@0: (char *) &err, &len) == SOCKET_ERROR) andre@0: { andre@0: _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError()); andre@0: return -1; andre@0: } andre@0: if (err != 0) andre@0: _PR_MD_MAP_CONNECT_ERROR(err); andre@0: else andre@0: PR_SetError(PR_UNKNOWN_ERROR, 0); andre@0: return -1; andre@0: } andre@0: if (FD_ISSET((SOCKET)osfd, &rd_wr)) andre@0: { andre@0: /* it's connected */ andre@0: return 1; andre@0: } andre@0: PR_ASSERT(0); andre@0: } andre@0: if (_PR_PENDING_INTERRUPT(me)) { andre@0: me->flags &= ~_PR_INTERRUPT; andre@0: PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); andre@0: rv = -1; andre@0: break; andre@0: } andre@0: /* andre@0: * We loop again if _MD_SELECT timed out and the andre@0: * timeout deadline has not passed yet. andre@0: */ andre@0: if (rv == 0 ) andre@0: { andre@0: if (wait_for_remaining) { andre@0: elapsed = remaining; andre@0: } else { andre@0: elapsed = PR_SecondsToInterval(tv.tv_sec) andre@0: + PR_MicrosecondsToInterval(tv.tv_usec); andre@0: } andre@0: if (elapsed >= remaining) { andre@0: PR_SetError(PR_IO_TIMEOUT_ERROR, 0); andre@0: rv = -1; andre@0: break; andre@0: } else { andre@0: remaining = remaining - elapsed; andre@0: } andre@0: } andre@0: } while (rv == 0 ); andre@0: break; andre@0: } andre@0: return(rv); andre@0: } /* end socket_io_wait() */