static PRStatus PR_CALLBACK SocketClose(PRFileDesc *fd) { if (!fd || !fd->secret || (fd->secret->state != _PR_FILEDESC_OPEN && fd->secret->state != _PR_FILEDESC_CLOSED)) { PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); return PR_FAILURE; } if (fd->secret->state == _PR_FILEDESC_OPEN) { if (_PR_MD_CLOSE_SOCKET(fd->secret->md.osfd) < 0) { return PR_FAILURE; } fd->secret->state = _PR_FILEDESC_CLOSED; } #ifdef _PR_HAVE_PEEK_BUFFER if (fd->secret->peekBuffer) { PR_ASSERT(fd->secret->peekBufSize > 0); PR_DELETE(fd->secret->peekBuffer); fd->secret->peekBufSize = 0; fd->secret->peekBytes = 0; } #endif PR_FreeFileDesc(fd); return PR_SUCCESS; }
static PRInt32 PR_CALLBACK SocketAcceptRead(PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, void *buf, PRInt32 amount, PRIntervalTime timeout) { PRInt32 rv; PRThread *me = _PR_MD_CURRENT_THREAD(); if (_PR_PENDING_INTERRUPT(me)) { me->flags &= ~_PR_INTERRUPT; PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); return -1; } if (_PR_IO_PENDING(me)) { PR_SetError(PR_IO_PENDING_ERROR, 0); return -1; } /* The socket must be in blocking mode. */ if (sd->secret->nonblocking) { PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); return -1; } *nd = NULL; #if defined(WINNT) { PROsfd newSock; PRNetAddr *raddrCopy; if (raddr == NULL) { raddr = &raddrCopy; } rv = _PR_MD_ACCEPT_READ(sd, &newSock, raddr, buf, amount, timeout); if (rv < 0) { rv = -1; } else { /* Successfully accepted and read; create the new PRFileDesc */ *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods()); if (*nd == 0) { _PR_MD_CLOSE_SOCKET(newSock); /* PR_AllocFileDesc() has invoked PR_SetError(). */ rv = -1; } else { (*nd)->secret->md.io_model_committed = PR_TRUE; (*nd)->secret->md.accepted_socket = PR_TRUE; memcpy(&(*nd)->secret->md.peer_addr, *raddr, PR_NETADDR_SIZE(*raddr)); #ifdef _PR_INET6 if (AF_INET6 == *raddr->raw.family) *raddr->raw.family = PR_AF_INET6; #endif } } } #else rv = PR_EmulateAcceptRead(sd, nd, raddr, buf, amount, timeout); #endif return rv; }
PR_IMPLEMENT(PRStatus) PR_NewTCPSocketPair(PRFileDesc *f[]) { #ifdef XP_UNIX PRInt32 rv, osfd[2]; rv = _PR_MD_SOCKETPAIR(AF_UNIX, SOCK_STREAM, 0, osfd); if (rv == -1) { return PR_FAILURE; } f[0] = PR_AllocFileDesc(osfd[0], PR_GetTCPMethods()); if (!f[0]) { _PR_MD_CLOSE_SOCKET(osfd[0]); _PR_MD_CLOSE_SOCKET(osfd[1]); /* PR_AllocFileDesc() has invoked PR_SetError(). */ return PR_FAILURE; } f[1] = PR_AllocFileDesc(osfd[1], PR_GetTCPMethods()); if (!f[1]) { PR_Close(f[0]); _PR_MD_CLOSE_SOCKET(osfd[1]); /* PR_AllocFileDesc() has invoked PR_SetError(). */ return PR_FAILURE; } _PR_MD_MAKE_NONBLOCK(f[0]); _PR_MD_MAKE_NONBLOCK(f[1]); return PR_SUCCESS; #endif /* XXX: this needs to be implemented for MAC and NT */ #ifdef XP_MAC #pragma unused (f) PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr); return PR_FAILURE; #endif #ifdef XP_PC PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); return PR_FAILURE; #endif }
PR_IMPLEMENT(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto) { PRInt32 osfd; int one = 1; PRFileDesc *fd; if (!_pr_initialized) _PR_ImplicitInitialization(); if (AF_INET != domain #if defined(_PR_INET6) && AF_INET6 != domain #endif ) { PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); return NULL; } osfd = _PR_MD_SOCKET(domain, type, proto); if (osfd == -1) { return 0; } #ifdef HAVE_SOCKET_KEEPALIVE /* "Keep-alive" packets are specific to TCP. */ if (domain == AF_INET && type == SOCK_STREAM) { if (setsockopt(osfd, (int)SOL_SOCKET, SO_KEEPALIVE, (const void *) &one, sizeof(one) ) < 0) { _PR_MD_CLOSE_SOCKET(osfd); return 0; } } #endif if (type == SOCK_STREAM) fd = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); else fd = PR_AllocFileDesc(osfd, PR_GetUDPMethods()); /* * Make the sockets non-blocking */ if (fd != NULL) _PR_MD_MAKE_NONBLOCK(fd); else _PR_MD_CLOSE_SOCKET(osfd); return fd; }
PR_IMPLEMENT(PRBool) _pr_test_ipv6_socket() { PROsfd osfd; osfd = _PR_MD_SOCKET(AF_INET6, SOCK_STREAM, 0); if (osfd != -1) { _PR_MD_CLOSE_SOCKET(osfd); return PR_TRUE; } return PR_FALSE; }
PR_IMPLEMENT(PRFileDesc *) PR_ImportUDPSocket(PRInt32 osfd) { PRFileDesc *fd; fd = PR_AllocFileDesc(osfd, PR_GetUDPMethods()); if (fd != NULL) _PR_MD_MAKE_NONBLOCK(fd); else _PR_MD_CLOSE_SOCKET(osfd); return(fd); }
PR_IMPLEMENT(PRInt32) PR_NTFast_AcceptRead_WithTimeoutCallback( PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, void *buf, PRInt32 amount, PRIntervalTime timeout, _PR_AcceptTimeoutCallback callback, void *callbackArg) { PRInt32 rv; PROsfd newSock; PRThread *me = _PR_MD_CURRENT_THREAD(); PRNetAddr *raddrCopy; if (_PR_PENDING_INTERRUPT(me)) { me->flags &= ~_PR_INTERRUPT; PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); return -1; } if (_PR_IO_PENDING(me)) { PR_SetError(PR_IO_PENDING_ERROR, 0); return -1; } *nd = NULL; if (raddr == NULL) { raddr = &raddrCopy; } rv = _PR_MD_FAST_ACCEPT_READ(sd, &newSock, raddr, buf, amount, timeout, PR_TRUE, callback, callbackArg); if (rv < 0) { rv = -1; } else { /* Successfully accepted and read; create the new PRFileDesc */ *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods()); if (*nd == 0) { _PR_MD_CLOSE_SOCKET(newSock); /* PR_AllocFileDesc() has invoked PR_SetError(). */ rv = -1; } else { (*nd)->secret->md.io_model_committed = PR_TRUE; (*nd)->secret->md.accepted_socket = PR_TRUE; memcpy(&(*nd)->secret->md.peer_addr, *raddr, PR_NETADDR_SIZE(*raddr)); #ifdef _PR_INET6 if (AF_INET6 == *raddr->raw.family) *raddr->raw.family = PR_AF_INET6; #endif #ifdef _PR_NEED_SECRET_AF (*nd)->secret->af = sd->secret->af; #endif } } return rv; }
PR_IMPLEMENT(PRFileDesc *) PR_ImportUDPSocket(PROsfd osfd) { PRFileDesc *fd; if (!_pr_initialized) _PR_ImplicitInitialization(); fd = PR_AllocFileDesc(osfd, PR_GetUDPMethods()); if (fd != NULL) { _PR_MD_MAKE_NONBLOCK(fd); _PR_MD_INIT_FD_INHERITABLE(fd, PR_TRUE); } else _PR_MD_CLOSE_SOCKET(osfd); return(fd); }
PR_IMPLEMENT(PRFileDesc*) PR_NTFast_Accept(PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout) { PROsfd osfd; PRFileDesc *fd2; PRIntn al; PRThread *me = _PR_MD_CURRENT_THREAD(); PRNetAddr addrCopy; if (_PR_PENDING_INTERRUPT(me)) { me->flags &= ~_PR_INTERRUPT; PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); return 0; } if (_PR_IO_PENDING(me)) { PR_SetError(PR_IO_PENDING_ERROR, 0); return 0; } if (addr == NULL) { addr = &addrCopy; } al = PR_NETADDR_SIZE(addr); osfd = _PR_MD_FAST_ACCEPT(fd, addr, &al, timeout, PR_TRUE, NULL, NULL); if (osfd == -1) { return 0; } fd2 = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); if (!fd2) { _PR_MD_CLOSE_SOCKET(osfd); } else { fd2->secret->nonblocking = fd->secret->nonblocking; fd2->secret->md.io_model_committed = PR_TRUE; PR_ASSERT(al == PR_NETADDR_SIZE(addr)); fd2->secret->md.accepted_socket = PR_TRUE; memcpy(&fd2->secret->md.peer_addr, addr, al); #ifdef _PR_INET6 if (AF_INET6 == addr->raw.family) addr->raw.family = PR_AF_INET6; #endif #ifdef _PR_NEED_SECRET_AF fd2->secret->af = fd->secret->af; #endif } return fd2; }
static PRStatus PR_CALLBACK SocketClose(PRFileDesc *fd) { PRInt32 rv; if (!fd || fd->secret->state != _PR_FILEDESC_OPEN) { PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); return PR_FAILURE; } fd->secret->state = _PR_FILEDESC_CLOSED; rv = _PR_MD_CLOSE_SOCKET(fd->secret->md.osfd); PR_FreeFileDesc(fd); if (rv < 0) { return PR_FAILURE; } return PR_SUCCESS; }
PR_IMPLEMENT(PRFileDesc *) PR_ImportTCPSocket(PROsfd osfd) { PRFileDesc *fd; if (!_pr_initialized) _PR_ImplicitInitialization(); fd = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); if (fd != NULL) { _PR_MD_MAKE_NONBLOCK(fd); _PR_MD_INIT_FD_INHERITABLE(fd, PR_TRUE); #ifdef _PR_NEED_SECRET_AF /* this means we can only import IPv4 sockets here. * but this is what the function in ptio.c does. * We need a way to import IPv6 sockets, too. */ fd->secret->af = AF_INET; #endif } else _PR_MD_CLOSE_SOCKET(osfd); return(fd); }
static PRFileDesc* PR_CALLBACK SocketAccept(PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout) { PROsfd osfd; PRFileDesc *fd2; PRUint32 al; PRThread *me = _PR_MD_CURRENT_THREAD(); #ifdef WINNT PRNetAddr addrCopy; #endif if (_PR_PENDING_INTERRUPT(me)) { me->flags &= ~_PR_INTERRUPT; PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); return 0; } if (_PR_IO_PENDING(me)) { PR_SetError(PR_IO_PENDING_ERROR, 0); return 0; } #ifdef WINNT if (addr == NULL) { addr = &addrCopy; } #endif al = sizeof(PRNetAddr); osfd = _PR_MD_ACCEPT(fd, addr, &al, timeout); if (osfd == -1) return 0; fd2 = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); if (!fd2) { _PR_MD_CLOSE_SOCKET(osfd); return NULL; } fd2->secret->nonblocking = fd->secret->nonblocking; fd2->secret->inheritable = fd->secret->inheritable; #ifdef WINNT if (!fd2->secret->nonblocking && fd2->secret->inheritable != _PR_TRI_TRUE) { /* * The new socket has been associated with an I/O * completion port. There is no going back. */ fd2->secret->md.io_model_committed = PR_TRUE; } PR_ASSERT(al == PR_NETADDR_SIZE(addr)); fd2->secret->md.accepted_socket = PR_TRUE; memcpy(&fd2->secret->md.peer_addr, addr, al); #endif /* * On some platforms, the new socket created by accept() * inherits the nonblocking (or overlapped io) attribute * of the listening socket. As an optimization, these * platforms can skip the following _PR_MD_MAKE_NONBLOCK * call. */ #if !defined(SOLARIS) && !defined(IRIX) && !defined(WINNT) _PR_MD_MAKE_NONBLOCK(fd2); #endif #ifdef _PR_INET6 if (addr && (AF_INET6 == addr->raw.family)) addr->raw.family = PR_AF_INET6; #endif PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); PR_ASSERT(IsValidNetAddrLen(addr, al) == PR_TRUE); return fd2; }
PR_IMPLEMENT(PRStatus) PR_NewTCPSocketPair(PRFileDesc *f[]) { #ifdef XP_UNIX PRInt32 rv, osfd[2]; if (!_pr_initialized) _PR_ImplicitInitialization(); rv = _PR_MD_SOCKETPAIR(AF_UNIX, SOCK_STREAM, 0, osfd); if (rv == -1) { return PR_FAILURE; } f[0] = PR_AllocFileDesc(osfd[0], PR_GetTCPMethods()); if (!f[0]) { _PR_MD_CLOSE_SOCKET(osfd[0]); _PR_MD_CLOSE_SOCKET(osfd[1]); /* PR_AllocFileDesc() has invoked PR_SetError(). */ return PR_FAILURE; } f[1] = PR_AllocFileDesc(osfd[1], PR_GetTCPMethods()); if (!f[1]) { PR_Close(f[0]); _PR_MD_CLOSE_SOCKET(osfd[1]); /* PR_AllocFileDesc() has invoked PR_SetError(). */ return PR_FAILURE; } _PR_MD_MAKE_NONBLOCK(f[0]); _PR_MD_INIT_FD_INHERITABLE(f[0], PR_FALSE); _PR_MD_MAKE_NONBLOCK(f[1]); _PR_MD_INIT_FD_INHERITABLE(f[1], PR_FALSE); return PR_SUCCESS; #elif defined(WINNT) /* * A socket pair is often used for interprocess communication, * so we need to make sure neither socket is associated with * the I/O completion port; otherwise it can't be used by a * child process. * * The default implementation below cannot be used for NT * because PR_Accept would have associated the I/O completion * port with the listening and accepted sockets. */ SOCKET listenSock; SOCKET osfd[2]; struct sockaddr_in selfAddr, peerAddr; int addrLen; if (!_pr_initialized) _PR_ImplicitInitialization(); osfd[0] = osfd[1] = INVALID_SOCKET; listenSock = socket(AF_INET, SOCK_STREAM, 0); if (listenSock == INVALID_SOCKET) { goto failed; } selfAddr.sin_family = AF_INET; selfAddr.sin_port = 0; selfAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* BugZilla: 35408 */ addrLen = sizeof(selfAddr); if (bind(listenSock, (struct sockaddr *) &selfAddr, addrLen) == SOCKET_ERROR) { goto failed; } if (getsockname(listenSock, (struct sockaddr *) &selfAddr, &addrLen) == SOCKET_ERROR) { goto failed; } if (listen(listenSock, 5) == SOCKET_ERROR) { goto failed; } osfd[0] = socket(AF_INET, SOCK_STREAM, 0); if (osfd[0] == INVALID_SOCKET) { goto failed; } selfAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* * Only a thread is used to do the connect and accept. * I am relying on the fact that connect returns * successfully as soon as the connect request is put * into the listen queue (but before accept is called). * This is the behavior of the BSD socket code. If * connect does not return until accept is called, we * will need to create another thread to call connect. */ if (connect(osfd[0], (struct sockaddr *) &selfAddr, addrLen) == SOCKET_ERROR) { goto failed; } /* * A malicious local process may connect to the listening * socket, so we need to verify that the accepted connection * is made from our own socket osfd[0]. */ if (getsockname(osfd[0], (struct sockaddr *) &selfAddr, &addrLen) == SOCKET_ERROR) { goto failed; } osfd[1] = accept(listenSock, (struct sockaddr *) &peerAddr, &addrLen); if (osfd[1] == INVALID_SOCKET) { goto failed; } if (peerAddr.sin_port != selfAddr.sin_port) { /* the connection we accepted is not from osfd[0] */ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); goto failed; } closesocket(listenSock); f[0] = PR_AllocFileDesc(osfd[0], PR_GetTCPMethods()); if (!f[0]) { closesocket(osfd[0]); closesocket(osfd[1]); /* PR_AllocFileDesc() has invoked PR_SetError(). */ return PR_FAILURE; } f[1] = PR_AllocFileDesc(osfd[1], PR_GetTCPMethods()); if (!f[1]) { PR_Close(f[0]); closesocket(osfd[1]); /* PR_AllocFileDesc() has invoked PR_SetError(). */ return PR_FAILURE; } _PR_MD_INIT_FD_INHERITABLE(f[0], PR_FALSE); _PR_MD_INIT_FD_INHERITABLE(f[1], PR_FALSE); return PR_SUCCESS; failed: if (listenSock != INVALID_SOCKET) { closesocket(listenSock); } if (osfd[0] != INVALID_SOCKET) { closesocket(osfd[0]); } if (osfd[1] != INVALID_SOCKET) { closesocket(osfd[1]); } return PR_FAILURE; #else /* not Unix or NT */ /* * default implementation */ PRFileDesc *listenSock; PRNetAddr selfAddr, peerAddr; PRUint16 port; f[0] = f[1] = NULL; listenSock = PR_NewTCPSocket(); if (listenSock == NULL) { goto failed; } PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &selfAddr); /* BugZilla: 35408 */ if (PR_Bind(listenSock, &selfAddr) == PR_FAILURE) { goto failed; } if (PR_GetSockName(listenSock, &selfAddr) == PR_FAILURE) { goto failed; } port = ntohs(selfAddr.inet.port); if (PR_Listen(listenSock, 5) == PR_FAILURE) { goto failed; } f[0] = PR_NewTCPSocket(); if (f[0] == NULL) { goto failed; } #ifdef _PR_CONNECT_DOES_NOT_BIND /* * If connect does not implicitly bind the socket (e.g., on * BeOS), we have to bind the socket so that we can get its * port with getsockname later. */ PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &selfAddr); if (PR_Bind(f[0], &selfAddr) == PR_FAILURE) { goto failed; } #endif PR_InitializeNetAddr(PR_IpAddrLoopback, port, &selfAddr); /* * Only a thread is used to do the connect and accept. * I am relying on the fact that PR_Connect returns * successfully as soon as the connect request is put * into the listen queue (but before PR_Accept is called). * This is the behavior of the BSD socket code. If * connect does not return until accept is called, we * will need to create another thread to call connect. */ if (PR_Connect(f[0], &selfAddr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) { goto failed; } /* * A malicious local process may connect to the listening * socket, so we need to verify that the accepted connection * is made from our own socket f[0]. */ if (PR_GetSockName(f[0], &selfAddr) == PR_FAILURE) { goto failed; } f[1] = PR_Accept(listenSock, &peerAddr, PR_INTERVAL_NO_TIMEOUT); if (f[1] == NULL) { goto failed; } if (peerAddr.inet.port != selfAddr.inet.port) { /* the connection we accepted is not from f[0] */ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); goto failed; } PR_Close(listenSock); return PR_SUCCESS; failed: if (listenSock) { PR_Close(listenSock); } if (f[0]) { PR_Close(f[0]); } if (f[1]) { PR_Close(f[1]); } return PR_FAILURE; #endif }
PR_IMPLEMENT(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto) { PROsfd osfd; PRFileDesc *fd; PRInt32 tmp_domain = domain; if (!_pr_initialized) _PR_ImplicitInitialization(); if (PR_AF_INET != domain && PR_AF_INET6 != domain #if defined(XP_UNIX) || defined(XP_OS2) && PR_AF_LOCAL != domain #endif ) { PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); return NULL; } #if defined(_PR_INET6_PROBE) if (PR_AF_INET6 == domain) domain = _pr_ipv6_is_present() ? AF_INET6 : AF_INET; #elif defined(_PR_INET6) if (PR_AF_INET6 == domain) domain = AF_INET6; #else if (PR_AF_INET6 == domain) domain = AF_INET; #endif /* _PR_INET6 */ osfd = _PR_MD_SOCKET(domain, type, proto); if (osfd == -1) { return 0; } if (type == SOCK_STREAM) fd = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); else fd = PR_AllocFileDesc(osfd, PR_GetUDPMethods()); /* * Make the sockets non-blocking */ if (fd != NULL) { _PR_MD_MAKE_NONBLOCK(fd); _PR_MD_INIT_FD_INHERITABLE(fd, PR_FALSE); #ifdef _PR_NEED_SECRET_AF fd->secret->af = domain; #endif #if defined(_PR_INET6_PROBE) || !defined(_PR_INET6) /* * For platforms with no support for IPv6 * create layered socket for IPv4-mapped IPv6 addresses */ if (PR_AF_INET6 == tmp_domain && PR_AF_INET == domain) { if (PR_FAILURE == _pr_push_ipv6toipv4_layer(fd)) { PR_Close(fd); fd = NULL; } } #endif } else _PR_MD_CLOSE_SOCKET(osfd); return fd; }