| /* -*- Mode: C++; c-basic-offset: 4 -*- */ |
| /* ***** 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 ***** */ |
| |
| #include "primpl.h" |
| |
| #include <signal.h> |
| #include <unistd.h> |
| #include <memory.h> |
| #include <fcntl.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <sys/time.h> |
| #include <sys/ioctl.h> |
| |
| /* |
| * Make sure _PRSockLen_t is 32-bit, because we will cast a PRUint32* or |
| * PRInt32* pointer to a _PRSockLen_t* pointer. |
| */ |
| #define _PRSockLen_t int |
| |
| |
| /* |
| ** Global lock variable used to bracket calls into rusty libraries that |
| ** aren't thread safe (like libc, libX, etc). |
| */ |
| static PRLock *_pr_rename_lock = NULL; |
| static PRMonitor *_pr_Xfe_mon = NULL; |
| |
| #define READ_FD 1 |
| #define WRITE_FD 2 |
| |
| /* |
| ** This is a support routine to handle "deferred" i/o on sockets. |
| ** It uses "select", so it is subject to all of the BeOS limitations |
| ** (only READ notification, only sockets) |
| */ |
| |
| /* |
| * socket_io_wait -- |
| * |
| * wait for socket i/o, periodically checking for interrupt |
| * |
| */ |
| |
| static PRInt32 socket_io_wait(PRInt32 osfd, PRInt32 fd_type, |
| PRIntervalTime timeout) |
| { |
| PRInt32 rv = -1; |
| struct timeval tv; |
| PRThread *me = _PR_MD_CURRENT_THREAD(); |
| PRIntervalTime epoch, now, elapsed, remaining; |
| PRBool wait_for_remaining; |
| PRInt32 syserror; |
| fd_set rd_wr; |
| |
| 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); |
| do { |
| FD_SET(osfd, &rd_wr); |
| if (fd_type == READ_FD) |
| rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, &tv); |
| else |
| rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, &tv); |
| if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) { |
| #ifdef BONE_VERSION |
| _PR_MD_MAP_SELECT_ERROR(syserror); |
| #else |
| if (syserror == EBADF) { |
| PR_SetError(PR_BAD_DESCRIPTOR_ERROR, EBADF); |
| } else { |
| PR_SetError(PR_UNKNOWN_ERROR, syserror); |
| } |
| #endif |
| break; |
| } |
| if (_PR_PENDING_INTERRUPT(me)) { |
| me->flags &= ~_PR_INTERRUPT; |
| PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); |
| rv = -1; |
| break; |
| } |
| } while (rv == 0 || (rv == -1 && syserror == EINTR)); |
| break; |
| default: |
| now = epoch = PR_IntervalNow(); |
| remaining = timeout; |
| FD_ZERO(&rd_wr); |
| 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); |
| if (fd_type == READ_FD) |
| rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, &tv); |
| else |
| rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, &tv); |
| /* |
| * we don't consider EINTR a real error |
| */ |
| if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) { |
| #ifdef BONE_VERSION |
| _PR_MD_MAP_SELECT_ERROR(syserror); |
| #else |
| if (syserror == EBADF) { |
| PR_SetError(PR_BAD_DESCRIPTOR_ERROR, EBADF); |
| } else { |
| PR_SetError(PR_UNKNOWN_ERROR, syserror); |
| } |
| #endif |
| break; |
| } |
| 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 or got interrupted |
| * by a signal, and the timeout deadline has not passed yet. |
| */ |
| if (rv == 0 || (rv == -1 && syserror == EINTR)) { |
| /* |
| * If _MD_SELECT timed out, we know how much time |
| * we spent in blocking, so we can avoid a |
| * PR_IntervalNow() call. |
| */ |
| if (rv == 0) { |
| if (wait_for_remaining) { |
| now += remaining; |
| } else { |
| now += PR_SecondsToInterval(tv.tv_sec) |
| + PR_MicrosecondsToInterval(tv.tv_usec); |
| } |
| } else { |
| now = PR_IntervalNow(); |
| } |
| elapsed = (PRIntervalTime) (now - epoch); |
| if (elapsed >= timeout) { |
| PR_SetError(PR_IO_TIMEOUT_ERROR, 0); |
| rv = -1; |
| break; |
| } else { |
| remaining = timeout - elapsed; |
| } |
| } |
| } while (rv == 0 || (rv == -1 && syserror == EINTR)); |
| break; |
| } |
| return(rv); |
| } |
| |
| PRInt32 |
| _MD_recv (PRFileDesc *fd, void *buf, PRInt32 amount, PRInt32 flags, |
| PRIntervalTime timeout) |
| { |
| PRInt32 osfd = fd->secret->md.osfd; |
| PRInt32 rv, err; |
| PRThread *me = _PR_MD_CURRENT_THREAD(); |
| |
| #ifndef BONE_VERSION |
| if (fd->secret->md.sock_state & BE_SOCK_SHUTDOWN_READ) { |
| _PR_MD_MAP_RECV_ERROR(EPIPE); |
| return -1; |
| } |
| #endif |
| |
| #ifdef BONE_VERSION |
| /* |
| ** Gah, stupid hack. If reading a zero amount, instantly return success. |
| ** BONE beta 6 returns EINVAL for reads of zero bytes, which parts of |
| ** mozilla use to check for socket availability. |
| */ |
| |
| if( 0 == amount ) return(0); |
| #endif |
| |
| while ((rv = recv(osfd, buf, amount, flags)) == -1) { |
| err = _MD_ERRNO(); |
| |
| if ((err == EAGAIN) || (err == EWOULDBLOCK)) { |
| if (fd->secret->nonblocking) { |
| break; |
| } |
| /* If socket was supposed to be blocking, |
| wait a while for the condition to be |
| satisfied. */ |
| if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) |
| goto done; |
| } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){ |
| continue; |
| |
| } else |
| break; |
| } |
| |
| if (rv < 0) { |
| _PR_MD_MAP_RECV_ERROR(err); |
| } |
| |
| done: |
| return(rv); |
| } |
| |
| PRInt32 |
| _MD_recvfrom (PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, |
| PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout) |
| { |
| PRInt32 osfd = fd->secret->md.osfd; |
| PRInt32 rv, err; |
| PRThread *me = _PR_MD_CURRENT_THREAD(); |
| |
| while ((*addrlen = PR_NETADDR_SIZE(addr)), |
| ((rv = recvfrom(osfd, buf, amount, flags, |
| (struct sockaddr *) addr, |
| (_PRSockLen_t *)addrlen)) == -1)) { |
| err = _MD_ERRNO(); |
| |
| if ((err == EAGAIN) || (err == EWOULDBLOCK)) { |
| if (fd->secret->nonblocking) { |
| break; |
| } |
| if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) |
| goto done; |
| |
| } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { |
| continue; |
| } else { |
| break; |
| } |
| } |
| |
| if (rv < 0) { |
| _PR_MD_MAP_RECVFROM_ERROR(err); |
| } |
| |
| done: |
| #ifdef _PR_HAVE_SOCKADDR_LEN |
| if (rv != -1) { |
| /* ignore the sa_len field of struct sockaddr */ |
| if (addr) { |
| addr->raw.family = ((struct sockaddr *) addr)->sa_family; |
| } |
| } |
| #endif /* _PR_HAVE_SOCKADDR_LEN */ |
| return(rv); |
| } |
| |
| PRInt32 |
| _MD_send (PRFileDesc *fd, const void *buf, PRInt32 amount, PRInt32 flags, |
| PRIntervalTime timeout) |
| { |
| PRInt32 osfd = fd->secret->md.osfd; |
| PRInt32 rv, err; |
| PRThread *me = _PR_MD_CURRENT_THREAD(); |
| |
| #ifndef BONE_VERSION |
| if (fd->secret->md.sock_state & BE_SOCK_SHUTDOWN_WRITE) |
| { |
| _PR_MD_MAP_SEND_ERROR(EPIPE); |
| return -1; |
| } |
| #endif |
| |
| while ((rv = send(osfd, buf, amount, flags)) == -1) { |
| err = _MD_ERRNO(); |
| |
| if ((err == EAGAIN) || (err == EWOULDBLOCK)) { |
| if (fd->secret->nonblocking) { |
| break; |
| } |
| |
| #ifndef BONE_VERSION |
| if( _PR_PENDING_INTERRUPT(me)) { |
| |
| me->flags &= ~_PR_INTERRUPT; |
| PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); |
| return -1; |
| } |
| |
| /* in UNIX implementations, you could do a socket_io_wait here. |
| * but since BeOS doesn't yet support WRITE notification in select, |
| * you're spanked. |
| */ |
| snooze( 10000L ); |
| continue; |
| #else /* BONE_VERSION */ |
| if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))< 0) |
| goto done; |
| #endif |
| |
| } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { |
| continue; |
| |
| } else { |
| break; |
| } |
| } |
| |
| #ifdef BONE_VERSION |
| /* |
| * optimization; if bytes sent is less than "amount" call |
| * select before returning. This is because it is likely that |
| * the next writev() call will return EWOULDBLOCK. |
| */ |
| if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount) |
| && (timeout != PR_INTERVAL_NO_WAIT)) { |
| if (socket_io_wait(osfd, WRITE_FD, timeout) < 0) { |
| rv = -1; |
| goto done; |
| } |
| } |
| #endif /* BONE_VERSION */ |
| |
| if (rv < 0) { |
| _PR_MD_MAP_SEND_ERROR(err); |
| } |
| |
| #ifdef BONE_VERSION |
| done: |
| #endif |
| return(rv); |
| } |
| |
| PRInt32 |
| _MD_sendto (PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, |
| const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout) |
| { |
| PRInt32 osfd = fd->secret->md.osfd; |
| PRInt32 rv, err; |
| PRThread *me = _PR_MD_CURRENT_THREAD(); |
| #ifdef _PR_HAVE_SOCKADDR_LEN |
| PRNetAddr addrCopy; |
| |
| addrCopy = *addr; |
| ((struct sockaddr *) &addrCopy)->sa_len = addrlen; |
| ((struct sockaddr *) &addrCopy)->sa_family = addr->raw.family; |
| |
| while ((rv = sendto(osfd, buf, amount, flags, |
| (struct sockaddr *) &addrCopy, addrlen)) == -1) { |
| #else |
| while ((rv = sendto(osfd, buf, amount, flags, |
| (struct sockaddr *) addr, addrlen)) == -1) { |
| #endif |
| err = _MD_ERRNO(); |
| |
| if ((err == EAGAIN) || (err == EWOULDBLOCK)) { |
| if (fd->secret->nonblocking) { |
| break; |
| } |
| |
| #ifdef BONE_VERSION |
| if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))< 0) |
| goto done; |
| #endif |
| } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { |
| continue; |
| |
| } else { |
| break; |
| } |
| } |
| |
| if (rv < 0) { |
| _PR_MD_MAP_SENDTO_ERROR(err); |
| } |
| |
| #ifdef BONE_VERSION |
| done: |
| #endif |
| return(rv); |
| } |
| |
| #ifdef BONE_VERSION |
| |
| PRInt32 _MD_writev( |
| PRFileDesc *fd, const PRIOVec *iov, |
| PRInt32 iov_size, PRIntervalTime timeout) |
| { |
| PRInt32 rv, err; |
| PRThread *me = _PR_MD_CURRENT_THREAD(); |
| PRInt32 index, amount = 0; |
| PRInt32 osfd = fd->secret->md.osfd; |
| struct iovec osiov[PR_MAX_IOVECTOR_SIZE]; |
| |
| /* Ensured by PR_Writev */ |
| PR_ASSERT(iov_size <= PR_MAX_IOVECTOR_SIZE); |
| |
| /* |
| * We can't pass iov to writev because PRIOVec and struct iovec |
| * may not be binary compatible. Make osiov a copy of iov and |
| * pass osiov to writev. |
| */ |
| for (index = 0; index < iov_size; index++) { |
| osiov[index].iov_base = iov[index].iov_base; |
| osiov[index].iov_len = iov[index].iov_len; |
| } |
| |
| /* |
| * Calculate the total number of bytes to be sent; needed for |
| * optimization later. |
| * We could avoid this if this number was passed in; but it is |
| * probably not a big deal because iov_size is usually small (less than |
| * 3) |
| */ |
| if (!fd->secret->nonblocking) { |
| for (index=0; index<iov_size; index++) { |
| amount += iov[index].iov_len; |
| } |
| } |
| |
| while ((rv = writev(osfd, osiov, iov_size)) == -1) { |
| err = _MD_ERRNO(); |
| if ((err == EAGAIN) || (err == EWOULDBLOCK)) { |
| if (fd->secret->nonblocking) { |
| break; |
| } |
| if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))<0) |
| goto done; |
| |
| } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){ |
| continue; |
| } else { |
| break; |
| } |
| } |
| |
| /* |
| * optimization; if bytes sent is less than "amount" call |
| * select before returning. This is because it is likely that |
| * the next writev() call will return EWOULDBLOCK. |
| */ |
| if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount) |
| && (timeout != PR_INTERVAL_NO_WAIT)) { |
| if (socket_io_wait(osfd, WRITE_FD, timeout) < 0) { |
| rv = -1; |
| goto done; |
| } |
| } |
| |
| |
| if (rv < 0) { |
| _PR_MD_MAP_WRITEV_ERROR(err); |
| } |
| done: |
| return(rv); |
| } |
| |
| #endif /* BONE_VERSION */ |
| |
| PRInt32 |
| _MD_accept (PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen, |
| PRIntervalTime timeout) |
| { |
| PRInt32 osfd = fd->secret->md.osfd; |
| PRInt32 rv, err; |
| PRThread *me = _PR_MD_CURRENT_THREAD(); |
| |
| while ((rv = accept(osfd, (struct sockaddr *) addr, |
| (_PRSockLen_t *)addrlen)) == -1) { |
| err = _MD_ERRNO(); |
| |
| if ((err == EAGAIN) || (err == EWOULDBLOCK)) { |
| if (fd->secret->nonblocking) { |
| break; |
| } |
| /* If it's SUPPOSED to be a blocking thread, wait |
| * a while to see if the triggering condition gets |
| * satisfied. |
| */ |
| /* Assume that we're always using a native thread */ |
| if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) |
| goto done; |
| } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { |
| continue; |
| } else { |
| break; |
| } |
| } |
| if (rv < 0) { |
| _PR_MD_MAP_ACCEPT_ERROR(err); |
| } else if (addr != NULL) { |
| /* bug 134099 */ |
| err = getpeername(rv, (struct sockaddr *) addr, (_PRSockLen_t *)addrlen); |
| } |
| done: |
| #ifdef _PR_HAVE_SOCKADDR_LEN |
| if (rv != -1) { |
| /* Mask off the first byte of struct sockaddr (the length field) */ |
| if (addr) { |
| addr->raw.family = ((struct sockaddr *) addr)->sa_family; |
| } |
| } |
| #endif /* _PR_HAVE_SOCKADDR_LEN */ |
| return(rv); |
| } |
| |
| PRInt32 |
| _MD_connect (PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen, |
| PRIntervalTime timeout) |
| { |
| PRInt32 rv, err; |
| PRThread *me = _PR_MD_CURRENT_THREAD(); |
| PRInt32 osfd = fd->secret->md.osfd; |
| |
| #ifndef BONE_VERSION |
| fd->secret->md.connectValueValid = PR_FALSE; |
| #endif |
| #ifdef _PR_HAVE_SOCKADDR_LEN |
| PRNetAddr addrCopy; |
| |
| addrCopy = *addr; |
| ((struct sockaddr *) &addrCopy)->sa_len = addrlen; |
| ((struct sockaddr *) &addrCopy)->sa_family = addr->raw.family; |
| #endif |
| |
| /* (Copied from unix.c) |
| * We initiate the connection setup by making a nonblocking connect() |
| * call. If the connect() call fails, there are two cases we handle |
| * specially: |
| * 1. The connect() call was interrupted by a signal. In this case |
| * we simply retry connect(). |
| * 2. The NSPR socket is nonblocking and connect() fails with |
| * EINPROGRESS. We first wait until the socket becomes writable. |
| * Then we try to find out whether the connection setup succeeded |
| * or failed. |
| */ |
| |
| retry: |
| #ifdef _PR_HAVE_SOCKADDR_LEN |
| if ((rv = connect(osfd, (struct sockaddr *)&addrCopy, addrlen)) == -1) { |
| #else |
| if ((rv = connect(osfd, (struct sockaddr *)addr, addrlen)) == -1) { |
| #endif |
| err = _MD_ERRNO(); |
| #ifndef BONE_VERSION |
| fd->secret->md.connectReturnValue = rv; |
| fd->secret->md.connectReturnError = err; |
| fd->secret->md.connectValueValid = PR_TRUE; |
| #endif |
| if( err == EINTR ) { |
| |
| if( _PR_PENDING_INTERRUPT(me)) { |
| |
| me->flags &= ~_PR_INTERRUPT; |
| PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); |
| return -1; |
| } |
| #ifndef BONE_VERSION |
| snooze( 100000L ); |
| #endif |
| goto retry; |
| } |
| |
| #ifndef BONE_VERSION |
| if(!fd->secret->nonblocking && ((err == EINPROGRESS) || (err==EAGAIN) || (err==EALREADY))) { |
| |
| /* |
| ** There's no timeout on this connect, but that's not |
| ** a big deal, since the connect times out anyways |
| ** after 30 seconds. Just sleep for 1/10th of a second |
| ** and retry until we go through or die. |
| */ |
| |
| if( _PR_PENDING_INTERRUPT(me)) { |
| me->flags &= ~_PR_INTERRUPT; |
| PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); |
| return -1; |
| } |
| |
| goto retry; |
| } |
| |
| if( fd->secret->nonblocking && ((err == EAGAIN) || (err == EINPROGRESS))) { |
| PR_Lock(_connectLock); |
| if (connectCount < sizeof(connectList)/sizeof(connectList[0])) { |
| connectList[connectCount].osfd = osfd; |
| memcpy(&connectList[connectCount].addr, addr, addrlen); |
| connectList[connectCount].addrlen = addrlen; |
| connectList[connectCount].timeout = timeout; |
| connectCount++; |
| PR_Unlock(_connectLock); |
| _PR_MD_MAP_CONNECT_ERROR(err); |
| } else { |
| PR_Unlock(_connectLock); |
| PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); |
| } |
| return rv; |
| } |
| #else /* BONE_VERSION */ |
| if(!fd->secret->nonblocking && (err == EINTR)) { |
| |
| rv = socket_io_wait(osfd, WRITE_FD, timeout); |
| if (rv == -1) { |
| return -1; |
| } |
| |
| PR_ASSERT(rv == 1); |
| if (_PR_PENDING_INTERRUPT(me)) { |
| me->flags &= ~_PR_INTERRUPT; |
| PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); |
| return -1; |
| } |
| err = _MD_beos_get_nonblocking_connect_error(osfd); |
| if (err != 0) { |
| _PR_MD_MAP_CONNECT_ERROR(err); |
| return -1; |
| } |
| return 0; |
| } |
| #endif |
| |
| _PR_MD_MAP_CONNECT_ERROR(err); |
| } |
| |
| return rv; |
| } |
| |
| PRInt32 |
| _MD_bind (PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen) |
| { |
| PRInt32 rv, err; |
| #ifdef _PR_HAVE_SOCKADDR_LEN |
| PRNetAddr addrCopy; |
| |
| addrCopy = *addr; |
| ((struct sockaddr *) &addrCopy)->sa_len = addrlen; |
| ((struct sockaddr *) &addrCopy)->sa_family = addr->raw.family; |
| rv = bind(fd->secret->md.osfd, (struct sockaddr *) &addrCopy, (int )addrlen); |
| #else |
| rv = bind(fd->secret->md.osfd, (struct sockaddr *) addr, (int )addrlen); |
| #endif |
| if (rv < 0) { |
| err = _MD_ERRNO(); |
| _PR_MD_MAP_BIND_ERROR(err); |
| } |
| |
| return(rv); |
| } |
| |
| PRInt32 |
| _MD_listen (PRFileDesc *fd, PRIntn backlog) |
| { |
| PRInt32 rv, err; |
| |
| #ifndef BONE_VERSION |
| /* Bug workaround! Setting listen to 0 on Be accepts no connections. |
| ** On most UN*Xes this sets the default. |
| */ |
| |
| if( backlog == 0 ) backlog = 5; |
| #endif |
| |
| rv = listen(fd->secret->md.osfd, backlog); |
| if (rv < 0) { |
| err = _MD_ERRNO(); |
| _PR_MD_MAP_LISTEN_ERROR(err); |
| } |
| |
| return(rv); |
| } |
| |
| PRInt32 |
| _MD_shutdown (PRFileDesc *fd, PRIntn how) |
| { |
| PRInt32 rv, err; |
| |
| #ifndef BONE_VERSION |
| if (how == PR_SHUTDOWN_SEND) |
| fd->secret->md.sock_state = BE_SOCK_SHUTDOWN_WRITE; |
| else if (how == PR_SHUTDOWN_RCV) |
| fd->secret->md.sock_state = BE_SOCK_SHUTDOWN_READ; |
| else if (how == PR_SHUTDOWN_BOTH) { |
| fd->secret->md.sock_state = (BE_SOCK_SHUTDOWN_WRITE | BE_SOCK_SHUTDOWN_READ); |
| } |
| |
| return 0; |
| #else /* BONE_VERSION */ |
| rv = shutdown(fd->secret->md.osfd, how); |
| if (rv < 0) { |
| err = _MD_ERRNO(); |
| _PR_MD_MAP_SHUTDOWN_ERROR(err); |
| } |
| return(rv); |
| #endif |
| } |
| |
| PRInt32 |
| _MD_socketpair (int af, int type, int flags, PRInt32 *osfd) |
| { |
| return PR_NOT_IMPLEMENTED_ERROR; |
| } |
| |
| PRInt32 |
| _MD_close_socket (PRInt32 osfd) |
| { |
| #ifdef BONE_VERSION |
| close( osfd ); |
| #else |
| closesocket( osfd ); |
| #endif |
| } |
| |
| PRStatus |
| _MD_getsockname (PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen) |
| { |
| PRInt32 rv, err; |
| |
| rv = getsockname(fd->secret->md.osfd, |
| (struct sockaddr *) addr, (_PRSockLen_t *)addrlen); |
| #ifdef _PR_HAVE_SOCKADDR_LEN |
| if (rv == 0) { |
| /* ignore the sa_len field of struct sockaddr */ |
| if (addr) { |
| addr->raw.family = ((struct sockaddr *) addr)->sa_family; |
| } |
| } |
| #endif /* _PR_HAVE_SOCKADDR_LEN */ |
| if (rv < 0) { |
| err = _MD_ERRNO(); |
| _PR_MD_MAP_GETSOCKNAME_ERROR(err); |
| } |
| |
| return rv==0?PR_SUCCESS:PR_FAILURE; |
| } |
| |
| PRStatus |
| _MD_getpeername (PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen) |
| { |
| PRInt32 rv, err; |
| |
| rv = getpeername(fd->secret->md.osfd, |
| (struct sockaddr *) addr, (_PRSockLen_t *)addrlen); |
| |
| #ifdef _PR_HAVE_SOCKADDR_LEN |
| if (rv == 0) { |
| /* ignore the sa_len field of struct sockaddr */ |
| if (addr) { |
| addr->raw.family = ((struct sockaddr *) addr)->sa_family; |
| } |
| } |
| #endif /* _PR_HAVE_SOCKADDR_LEN */ |
| |
| if (rv < 0) { |
| err = _MD_ERRNO(); |
| _PR_MD_MAP_GETPEERNAME_ERROR(err); |
| } |
| return rv==0?PR_SUCCESS:PR_FAILURE; |
| } |
| |
| PRStatus |
| _MD_getsockopt (PRFileDesc *fd, PRInt32 level, |
| PRInt32 optname, char* optval, PRInt32* optlen) |
| { |
| PRInt32 rv, err; |
| |
| rv = getsockopt(fd->secret->md.osfd, level, optname, |
| optval, (_PRSockLen_t *)optlen); |
| if (rv < 0) { |
| err = _MD_ERRNO(); |
| _PR_MD_MAP_GETSOCKOPT_ERROR(err); |
| } |
| |
| return rv==0?PR_SUCCESS:PR_FAILURE; |
| } |
| |
| PRStatus |
| _MD_setsockopt (PRFileDesc *fd, PRInt32 level, |
| PRInt32 optname, const char* optval, PRInt32 optlen) |
| { |
| PRInt32 rv, err; |
| |
| rv = setsockopt(fd->secret->md.osfd, level, optname, optval, optlen); |
| if (rv < 0) { |
| err = _MD_ERRNO(); |
| _PR_MD_MAP_SETSOCKOPT_ERROR(err); |
| } |
| return rv==0?PR_SUCCESS:PR_FAILURE; |
| } |
| |
| PRInt32 |
| _MD_accept_read (PRFileDesc *sd, PRInt32 *newSock, PRNetAddr **raddr, |
| void *buf, PRInt32 amount, PRIntervalTime timeout) |
| { |
| return PR_NOT_IMPLEMENTED_ERROR; |
| } |
| |
| #ifndef BONE_VERSION |
| PRInt32 |
| _MD_socket (int af, int type, int flags) |
| { |
| PRInt32 osfd, err; |
| |
| osfd = socket( af, type, 0 ); |
| |
| if( -1 == osfd ) { |
| |
| err = _MD_ERRNO(); |
| _PR_MD_MAP_SOCKET_ERROR( err ); |
| } |
| |
| return( osfd ); |
| } |
| #else |
| PRInt32 |
| _MD_socket(PRInt32 domain, PRInt32 type, PRInt32 proto) |
| { |
| PRInt32 osfd, err; |
| |
| osfd = socket(domain, type, proto); |
| |
| if (osfd == -1) { |
| err = _MD_ERRNO(); |
| _PR_MD_MAP_SOCKET_ERROR(err); |
| } |
| |
| return(osfd); |
| } |
| #endif |
| |
| PRInt32 |
| _MD_socketavailable (PRFileDesc *fd) |
| { |
| #ifdef BONE_VERSION |
| PRInt32 result; |
| |
| if (ioctl(fd->secret->md.osfd, FIONREAD, &result) < 0) { |
| _PR_MD_MAP_SOCKETAVAILABLE_ERROR(_MD_ERRNO()); |
| return -1; |
| } |
| return result; |
| #else |
| return PR_NOT_IMPLEMENTED_ERROR; |
| #endif |
| } |
| |
| PRInt32 |
| _MD_get_socket_error (void) |
| { |
| return PR_NOT_IMPLEMENTED_ERROR; |
| } |
| |
| PRStatus |
| _MD_gethostname (char *name, PRUint32 namelen) |
| { |
| PRInt32 rv, err; |
| |
| rv = gethostname(name, namelen); |
| if (rv == 0) |
| { |
| err = _MD_ERRNO(); |
| _PR_MD_MAP_GETHOSTNAME_ERROR(err); |
| return PR_FAILURE; |
| } |
| return PR_SUCCESS; |
| } |
| |
| #ifndef BONE_VERSION |
| PRInt32 |
| _MD_beos_get_nonblocking_connect_error(PRFileDesc *fd) |
| { |
| int rv; |
| int flags = 0; |
| |
| rv = recv(fd->secret->md.osfd, NULL, 0, flags); |
| PR_ASSERT(-1 == rv || 0 == rv); |
| if (-1 == rv && errno != EAGAIN && errno != EWOULDBLOCK) { |
| return errno; |
| } |
| return 0; /* no error */ |
| } |
| #else |
| PRInt32 |
| _MD_beos_get_nonblocking_connect_error(int osfd) |
| { |
| return PR_NOT_IMPLEMENTED_ERROR; |
| // int err; |
| // _PRSockLen_t optlen = sizeof(err); |
| // if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, (char *) &err, &optlen) == -1) { |
| // return errno; |
| // } else { |
| // return err; |
| // } |
| } |
| #endif /* BONE_VERSION */ |