ncbi_socket.c
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:153k
- /*
- * ===========================================================================
- * PRODUCTION $Log: ncbi_socket.c,v $
- * PRODUCTION Revision 1000.4 2004/06/01 18:45:25 gouriano
- * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R6.145
- * PRODUCTION
- * ===========================================================================
- */
- /* $Id: ncbi_socket.c,v 1000.4 2004/06/01 18:45:25 gouriano Exp $
- * ===========================================================================
- *
- * PUBLIC DOMAIN NOTICE
- * National Center for Biotechnology Information
- *
- * This software/database is a "United States Government Work" under the
- * terms of the United States Copyright Act. It was written as part of
- * the author's official duties as a United States Government employee and
- * thus cannot be copyrighted. This software/database is freely available
- * to the public for use. The National Library of Medicine and the U.S.
- * Government have not placed any restriction on its use or reproduction.
- *
- * Although all reasonable efforts have been taken to ensure the accuracy
- * and reliability of the software and data, the NLM and the U.S.
- * Government do not and cannot warrant the performance or results that
- * may be obtained by using this software or data. The NLM and the U.S.
- * Government disclaim all warranties, express or implied, including
- * warranties of performance, merchantability or fitness for any particular
- * purpose.
- *
- * Please cite the author in any work or product based on this material.
- *
- * ===========================================================================
- *
- * Author: Denis Vakatov
- *
- * File Description:
- * Plain portable TCP/IP socket API for: UNIX, MS-Win, MacOS
- * [UNIX ] -DNCBI_OS_UNIX -lresolv -lsocket -lnsl
- * [MSWIN] -DNCBI_OS_MSWIN wsock32.lib
- * [MacOS] -DNCBI_OS_MAC NCSASOCK -- BSD-style socket emulation lib
- *
- */
- /* NCBI core headers
- */
- #include "ncbi_ansi_ext.h"
- #include "ncbi_priv.h"
- /* The next header implicitly includes <connect/ncbi_socket.h> */
- #include <connect/ncbi_connutil.h>
- /* OS must be specified in the command-line ("-D....") or in the conf. header
- */
- #if !defined(NCBI_OS_UNIX) && !defined(NCBI_OS_MSWIN) && !defined(NCBI_OS_MAC)
- # error "Unknown OS, must be one of NCBI_OS_UNIX, NCBI_OS_MSWIN, NCBI_OS_MAC!"
- #endif /*supported platforms*/
- /* Uncomment these(or specify "-DHAVE_GETADDRINFO -DHAVE_GETNAMEINFO") only if:
- * 0) you are compiling this outside of the NCBI C or C++ Toolkits
- * (USE_NCBICONF is not #define'd), and
- * 1) your platform has "getaddrinfo()" and "getnameinfo()", and
- * 2) you are going to use this API code in multi-thread application, and
- * 3) "gethostbyname()" gets called somewhere else in your code
- */
- /* #define HAVE_GETADDRINFO 1 */
- /* #define HAVE_GETNAMEINFO 1 */
- /* Uncomment this (or specify "-DHAVE_GETHOSTBY***_R=") only if:
- * 0) you are compiling this outside of the NCBI C or C++ Toolkits
- * (USE_NCBICONF is not #define'd), and
- * 1) your platform has "gethostbyname_r()" but not "getnameinfo()", and
- * 2) you are going to use this API code in multi-thread application, and
- * 3) "gethostbyname()" gets called somewhere else in your code
- */
- /* Solaris: */
- /* #define HAVE_GETHOSTBYNAME_R 5 */
- /* #define HAVE_GETHOSTBYADDR_R 7 */
- /* Linux, IRIX: */
- /* #define HAVE_GETHOSTBYNAME_R 6 */
- /* #define HAVE_GETHOSTBYADDR_R 8 */
- /* Uncomment this (or specify "-DHAVE_SIN_LEN") only if:
- * 0) you are compiling this outside of the NCBI C or C++ Toolkits
- * (USE_NCBICONF is not #define'd), and
- * 1) on your platform, struct sockaddr_in contains a field called "sin_len"
- */
- /* #define HAVE_SIN_LEN 1 */
- /* Platform-specific system headers
- */
- #if defined(NCBI_OS_UNIX)
- # include <sys/time.h>
- # include <unistd.h>
- # ifdef NCBI_COMPILER_MW_MSL
- # include <ncbi_mslextras.h>
- # else
- # include <netdb.h>
- # endif
- # include <fcntl.h>
- # include <sys/socket.h>
- # include <netinet/in.h>
- # if !defined(NCBI_OS_BEOS) && !defined(NCBI_COMPILER_MW_MSL)
- # include <arpa/inet.h>
- # endif /*NCBI_OS_BEOS*/
- # include <signal.h>
- # include <sys/un.h>
- #elif defined(NCBI_OS_MSWIN)
- # ifndef COMP_METRO
- # include <winsock2.h>
- # else
- # define SD_RECEIVE 0x00
- # define SD_SEND 0x01
- # define SD_BOTH 0x02
- # endif
- #elif defined(NCBI_OS_MAC)
- # include <unistd.h>
- # include <sock_ext.h>
- # include <netdb.h>
- # include <s_types.h>
- # include <s_socket.h>
- # include <neti_in.h>
- # include <a_inet.h>
- # include <neterrno.h> /* missing error numbers on Mac */
- #else
- # error "Unsupported platform, must be one of NCBI_OS_UNIX, NCBI_OS_MSWIN, NCBI_OS_MAC !!!"
- #endif /* platform-specific headers (for UNIX, MSWIN, MAC) */
- /* Portable standard C headers
- */
- #include <errno.h>
- #include <stdlib.h>
- /******************************************************************************
- * TYPEDEFS & MACROS
- */
- /* Minimal size of the data buffer chunk in the socket internal buffer(s) */
- #define SOCK_BUF_CHUNK_SIZE 4096
- /* Macro #def for the platform-dependent constants, error codes and functions
- */
- #if defined(NCBI_OS_MSWIN)
- typedef SOCKET TSOCK_Handle;
- # define SOCK_INVALID INVALID_SOCKET
- # define SOCK_ERRNO WSAGetLastError()
- # define SOCK_EINTR WSAEINTR
- # define SOCK_EWOULDBLOCK WSAEWOULDBLOCK
- # define SOCK_EADDRINUSE WSAEADDRINUSE
- # define SOCK_ECONNRESET WSAECONNRESET
- # define SOCK_EPIPE WSAESHUTDOWN
- # define SOCK_EAGAIN WSAEINPROGRESS
- # define SOCK_EINPROGRESS WSAEINPROGRESS
- # define SOCK_EALREADY WSAEALREADY
- # define SOCK_ENOTCONN WSAENOTCONN
- # define SOCK_ECONNABORTED WSAECONNABORTED
- # define SOCK_ECONNREFUSED WSAECONNREFUSED
- # define SOCK_ENETRESET WSAENETRESET
- # define SOCK_NFDS(s) 0
- # define SOCK_CLOSE(s) closesocket(s)
- # define SOCK_SHUTDOWN(s,h) shutdown(s,h)
- # define SOCK_SHUTDOWN_RD SD_RECEIVE
- # define SOCK_SHUTDOWN_WR SD_SEND
- # define SOCK_SHUTDOWN_RDWR SD_BOTH
- # define SOCK_STRERROR(err) s_StrError(err)
- /* NCBI_OS_MSWIN */
- #elif defined(NCBI_OS_UNIX)
- typedef int TSOCK_Handle;
- # define SOCK_INVALID (-1)
- # define SOCK_ERRNO errno
- # define SOCK_EINTR EINTR
- # define SOCK_EWOULDBLOCK EWOULDBLOCK
- # define SOCK_EADDRINUSE EADDRINUSE
- # define SOCK_ECONNRESET ECONNRESET
- # define SOCK_EPIPE EPIPE
- # define SOCK_EAGAIN EAGAIN
- # define SOCK_EINPROGRESS EINPROGRESS
- # define SOCK_EALREADY EALREADY
- # define SOCK_ENOTCONN ENOTCONN
- # define SOCK_ECONNABORTED ECONNABORTED
- # define SOCK_ECONNREFUSED ECONNREFUSED
- # define SOCK_ENETRESET ENETRESET
- # define SOCK_NFDS(s) (s + 1)
- # ifdef NCBI_OS_BEOS
- # define SOCK_CLOSE(s) closesocket(s)
- # else
- # define SOCK_CLOSE(s) close(s)
- # endif /*NCBI_OS_BEOS*/
- # define SOCK_SHUTDOWN(s,h) shutdown(s,h)
- # ifndef SHUT_RD
- # define SHUT_RD 0
- # endif /*SHUT_RD*/
- # define SOCK_SHUTDOWN_RD SHUT_RD
- # ifndef SHUT_WR
- # define SHUT_WR 1
- # endif /*SHUT_WR*/
- # define SOCK_SHUTDOWN_WR SHUT_WR
- # ifndef SHUT_RDWR
- # define SHUT_RDWR 2
- # endif /*SHUT_RDWR*/
- # define SOCK_SHUTDOWN_RDWR SHUT_RDWR
- # ifndef INADDR_NONE
- # define INADDR_NONE (unsigned int)(-1)
- # endif /*INADDR_NONE*/
- # define SOCK_STRERROR(err) s_StrError(err)
- /* NCBI_OS_UNIX */
- #elif defined(NCBI_OS_MAC)
- # if TARGET_API_MAC_CARBON
- # define O_NONBLOCK kO_NONBLOCK
- # endif /*TARGET_API_MAC_CARBON*/
- typedef int TSOCK_Handle;
- # define SOCK_INVALID (-1)
- # ifndef SOCK_ERRNO
- # define SOCK_ERRNO errno
- # endif /*SOCK_ERRNO*/
- # define SOCK_EINTR EINTR
- # define SOCK_EWOULDBLOCK EWOULDBLOCK
- # define SOCK_EADDRINUSE EADDRINUSE
- # define SOCK_ECONNRESET ECONNRESET
- # define SOCK_EPIPE EPIPE
- # define SOCK_EAGAIN EAGAIN
- # define SOCK_EINPROGRESS EINPROGRESS
- # define SOCK_EALREADY EALREADY
- # define SOCK_ENOTCONN ENOTCONN
- # define SOCK_ECONNABORTED ECONNABORTED
- # define SOCK_ECONNREFUSED ECONNREFUSED
- # define SOCK_ENETRESET ENETRESET
- # define SOCK_NFDS(s) (s + 1)
- # define SOCK_CLOSE(s) close(s)
- # define SOCK_SHUTDOWN(s,h) shutdown(s,h)
- # define SOCK_SHUTDOWN_RD 0
- # define SOCK_SHUTDOWN_WR 1
- # define SOCK_SHUTDOWN_RDWR 2
- # define SOCK_STRERROR(err) s_StrError(err)
- # ifdef NETDB_INTERNAL
- # undef NETDB_INTERNAL
- # endif /*NETDB_INTERNAL*/
- #endif /*NCBI_OS_MSWIN, NCBI_OS_UNIX, NCBI_OS_MAC*/
- #ifdef HAVE_SOCKLEN_T
- typedef socklen_t SOCK_socklen_t;
- #else
- typedef int SOCK_socklen_t;
- #endif /*HAVE_SOCKLEN_T*/
- /* Type of connecting socket (except listening)
- */
- typedef enum {
- eSOCK_Datagram = 0,
- eSOCK_ClientSide,
- eSOCK_ServerSide,
- eSOCK_ServerSideKeep
- } ESockType;
- #if 0/*defined(__GNUC__)*/
- typedef ESwitch EBSwitch;
- typedef EIO_Status EBIO_Status;
- typedef ESockType EBSockType;
- #else
- typedef unsigned EBSwitch;
- typedef unsigned EBIO_Status;
- typedef unsigned EBSockType;
- #endif
- #define SET_LISTENING(s) ((s)->r_on_w = (unsigned) eDefault + 1)
- #define IS_LISTENING(s) ((s)->r_on_w == (unsigned) eDefault + 1)
- /* Listening socket
- */
- struct LSOCK_tag {
- TSOCK_Handle sock; /* OS-specific socket handle */
- unsigned int id; /* the internal ID (see also "s_ID_Counter") */
- unsigned int n_accept; /* total number of accepted clients */
- unsigned short port; /* port on which the socket is listening */
- /* type, status, EOF, log, read-on-write etc bit-field indicators */
- EBSwitch log:2; /* how to log events and data for this socket*/
- EBSockType type:2; /* MBZ (NB: eSOCK_Datagram) */
- EBSwitch r_on_w:2; /* 3 [=(int)eDefault + 1] */
- EBSwitch i_on_sig:2; /* eDefault */
- EBIO_Status r_status:3; /* MBZ (NB: eIO_Success) */
- unsigned/*bool*/ eof:1; /* 0 */
- EBIO_Status w_status:3; /* MBZ (NB: eIO_Success) */
- unsigned/*bool*/ pending:1; /* 0 */
- };
- /* Socket [it must be in one-2-one correspondence with LSOCK above]
- */
- struct SOCK_tag {
- TSOCK_Handle sock; /* OS-specific socket handle */
- unsigned int id; /* the internal ID (see also "s_ID_Counter") */
- /* connection point */
- unsigned int host; /* peer host (in the network byte order) */
- unsigned short port; /* peer port (in the network byte order) */
- /* type, status, EOF, log, read-on-write etc bit-field indicators */
- EBSwitch log:2; /* how to log events and data for this socket*/
- EBSockType type:2; /* socket type: client- or server-side, dgram*/
- EBSwitch r_on_w:2; /* enable/disable automatic read-on-write */
- EBSwitch i_on_sig:2; /* enable/disable I/O restart on signals */
- EBIO_Status r_status:3; /* read status: eIO_Closed if was shut down*/
- unsigned/*bool*/ eof:1; /* Stream sockets: 'End of file' seen on read
- Datagram socks: 'End of message' written */
- EBIO_Status w_status:3; /* write status: eIO_Closed if was shut down*/
- unsigned/*bool*/ pending:1; /* != 0 if connection is still pending */
- /* timeouts */
- const struct timeval* r_timeout;/* NULL if infinite, or points to "r_tv" */
- struct timeval r_tv; /* finite read timeout value */
- STimeout r_to; /* finite read timeout value (aux., temp.) */
- const struct timeval* w_timeout;/* NULL if infinite, or points to "w_tv" */
- struct timeval w_tv; /* finite write timeout value */
- STimeout w_to; /* finite write timeout value (aux., temp.) */
- const struct timeval* c_timeout;/* NULL if infinite, or points to "c_tv" */
- struct timeval c_tv; /* finite close timeout value */
- STimeout c_to; /* finite close timeout value (aux., temp.) */
- /* aux I/O data */
- BUF r_buf; /* read buffer */
- BUF w_buf; /* write buffer */
- size_t w_len; /* SOCK: how much data is pending for output */
- /* statistics */
- size_t n_read; /* DSOCK: total #; SOCK: last connect/ only */
- size_t n_written; /* DSOCK: total #; SOCK: last /session only */
- size_t n_in; /* DSOCK: msg #; SOCK: total # of bytes read */
- size_t n_out; /* DSOCK: msg #; SOCK: total # of bytes sent */
- #ifdef NCBI_OS_UNIX
- /* filename for UNIX socket */
- char file[1]; /* must go last */
- #endif /*NCBI_OS_UNIX*/
- };
- /*
- * Please note the following implementation details:
- *
- * 1. w_buf is used for stream sockets to keep initial data segment
- * that has to be sent upon the connection establishment.
- *
- * 2. eof is used differently for stream and datagram sockets:
- * =1 for stream sockets means that read had hit EOF;
- * =1 for datagram sockets means that the message in w_buf is complete.
- *
- * 3. r_status keeps completion code of the last low-level read call;
- * however, eIO_Closed is there when the socket is shut down for reading;
- * see the table below for full details on stream sockets.
- *
- * 4. w_status keeps completion code of the last low-level write call;
- * however, eIO_Closed is there when the socket is shut down for writing.
- *
- * 5. The following table depicts r_status and eof combinations and their
- * meanings for stream sockets:
- * -------------------------------+--------------------------------------------
- * Field |
- * ---------------+---------------+ Meaning
- * sock->r_status | sock->eof | (stream sockets only)
- * ---------------+---------------+--------------------------------------------
- * eIO_Closed | 0 | Socket shut down for reading
- * eIO_Closed | 1 | Read severely failed
- * not eIO_Closed | 0 | Read completed with r_status error
- * not eIO_Closed | 1 | Read hit EOF (and later r_status)
- * ---------------+---------------+--------------------------------------------
- */
- /* Globals:
- */
- /* Flag to indicate whether the API has been initialized */
- static int/*bool*/ s_Initialized = 0/*false*/;
- /* SOCK counter */
- static unsigned int s_ID_Counter = 0;
- /* Read-while-writing switch */
- static ESwitch s_ReadOnWrite = eOff; /* no read-on-write by default */
- /* Reuse address flag for newly created stream sockets */
- static int/*bool*/ s_ReuseAddress = 0; /* off by default */
- /* I/O restart on signals */
- static ESwitch s_InterruptOnSignal = eOff; /* restart I/O by default */
- /* Data/event logging */
- static ESwitch s_Log = eOff; /* no logging by default */
- /* Select restart timeout */
- static const struct timeval* s_SelectTimeout = 0; /* =0 (disabled) by default*/
- /* Flag to indicate whether API should mask SIGPIPE (during initialization) */
- #ifdef NCBI_OS_UNIX
- static int/*bool*/ s_AllowSigPipe = 0/*false - mask SIGPIPE out*/;
- #endif /*NCBI_OS_UNIX*/
- /******************************************************************************
- * Error reporting
- */
- static const char* s_StrError(int error)
- {
- static struct {
- int errnum;
- const char* errtxt;
- } errmap[] = {
- #ifdef NCBI_OS_MSWIN
- {WSAEINTR, "Interrupted system call"},
- {WSAEBADF, "Bad file number"},
- {WSAEACCES, "Access denied"},
- {WSAEFAULT, "Segmentation fault"},
- {WSAEINVAL, "Invalid agrument"},
- {WSAEMFILE, "Too many open files"},
- /*
- * Windows Sockets definitions of regular Berkeley error constants
- */
- {WSAEWOULDBLOCK, "Resource temporarily unavailable"},
- {WSAEINPROGRESS, "Operation now in progress"},
- {WSAEALREADY, "Operation already in progress"},
- {WSAENOTSOCK, "Not a socket"},
- {WSAEDESTADDRREQ, "Destination address required"},
- {WSAEMSGSIZE, "Invalid message size"},
- {WSAEPROTOTYPE, "Wrong protocol type"},
- {WSAENOPROTOOPT, "Bad protocol option"},
- {WSAEPROTONOSUPPORT, "Protocol not supported"},
- {WSAESOCKTNOSUPPORT, "Socket type not supported"},
- {WSAEOPNOTSUPP, "Operation not supported"},
- {WSAEPFNOSUPPORT, "Protocol family not supported"},
- {WSAEAFNOSUPPORT, "Address family not supported"},
- {WSAEADDRINUSE, "Address already in use"},
- {WSAEADDRNOTAVAIL, "Cannot assign requested address"},
- {WSAENETDOWN, "Network is down"},
- {WSAENETUNREACH, "Network is unreachable"},
- {WSAENETRESET, "Connection dropped on network reset"},
- {WSAECONNABORTED, "Software caused connection abort"},
- {WSAECONNRESET, "Connection reset by peer"},
- {WSAENOBUFS, "No buffer space available"},
- {WSAEISCONN, "Socket is already connected"},
- {WSAENOTCONN, "Socket is not connected"},
- {WSAESHUTDOWN, "Cannot send after socket shutdown"},
- {WSAETOOMANYREFS, "Too many references"},
- {WSAETIMEDOUT, "Operation timed out"},
- {WSAECONNREFUSED, "Connection refused"},
- {WSAELOOP, "Infinite loop"},
- {WSAENAMETOOLONG, "Name too long"},
- {WSAEHOSTDOWN, "Host is down"},
- {WSAEHOSTUNREACH, "Host unreachable"},
- {WSAENOTEMPTY, "Not empty"},
- {WSAEPROCLIM, "Too many processes"},
- {WSAEUSERS, "Too many users"},
- {WSAEDQUOT, "Quota exceeded"},
- {WSAESTALE, "Stale descriptor"},
- {WSAEREMOTE, "Remote error"},
- /*
- * Extended Windows Sockets error constant definitions
- */
- {WSASYSNOTREADY, "Network subsystem is unavailable"},
- {WSAVERNOTSUPPORTED, "Winsock.dll version out of range"},
- {WSANOTINITIALISED, "Not yet initialized"},
- {WSAEDISCON, "Graceful shutdown in progress"},
- {WSAENOMORE, "No more retries"},
- {WSAECANCELLED, "Cancelled"},
- {WSAEINVALIDPROCTABLE, "Invalid procedure table"},
- {WSAEINVALIDPROVIDER, "Invalid provider version number"},
- {WSAEPROVIDERFAILEDINIT, "Cannot init provider"},
- {WSASYSCALLFAILURE, "System call failed"},
- {WSASERVICE_NOT_FOUND, "Service not found"},
- {WSATYPE_NOT_FOUND, "Class type not found"},
- {WSA_E_NO_MORE, "WSA_E_NO_MORE"},
- {WSA_E_CANCELLED, "WSA_E_CANCELLED"},
- {WSAEREFUSED, "Refused"},
- #endif /*NCBI_OS_MSWIN*/
- #ifdef NCBI_OS_MSWIN
- # define EAI_BASE 0
- #else
- # define EAI_BASE 100000
- #endif /*NCBI_OS_MSWIN*/
- #ifdef EAI_ADDRFAMILY
- {EAI_ADDRFAMILY + EAI_BASE,
- "Address family not supported"},
- #endif /*EAI_ADDRFAMILY*/
- #ifdef EAI_AGAIN
- {EAI_AGAIN + EAI_BASE,
- "Temporary failure in name resolution"},
- #endif /*EAI_AGAIN*/
- #ifdef EAI_BADFLAGS
- {EAI_BADFLAGS + EAI_BASE,
- "Invalid value for lookup flags"},
- #endif /*EAI_BADFLAGS*/
- #ifdef EAI_FAIL
- {EAI_FAIL + EAI_BASE,
- "Non-recoverable failure in name resolution"},
- #endif /*EAI_FAIL*/
- #ifdef EAI_FAMILY
- {EAI_FAMILY + EAI_BASE,
- "Address family not supported"},
- #endif /*EAI_FAMILY*/
- #ifdef EAI_MEMORY
- {EAI_MEMORY + EAI_BASE,
- "Memory allocation failure"},
- #endif /*EAI_MEMORY*/
- #ifdef EAI_NODATA
- {EAI_NODATA + EAI_BASE,
- "No address associated with nodename"},
- #endif /*EAI_NODATA*/
- #ifdef EAI_NONAME
- {EAI_NONAME + EAI_BASE,
- "Host/service name not known"},
- #endif /*EAI_NONAME*/
- #ifdef EAI_SERVICE
- {EAI_SERVICE + EAI_BASE,
- "Service name not supported for socket type"},
- #endif /*EAI_SERVICE*/
- #ifdef EAI_SOCKTYPE
- {EAI_SOCKTYPE + EAI_BASE,
- "Socket type not supported"},
- #endif /*EAI_SOCKTYPE*/
- #ifdef NCBI_OS_MSWIN
- # define DNS_BASE 0
- #else
- # define DNS_BASE 200000
- #endif /*NCBI_OS_MSWIN*/
- #ifdef HOST_NOT_FOUND
- {HOST_NOT_FOUND + DNS_BASE,
- "Host not found"},
- #endif /*HOST_NOT_FOUND*/
- #ifdef TRY_AGAIN
- {TRY_AGAIN + DNS_BASE,
- "DNS server failure"},
- #endif /*TRY_AGAIN*/
- #ifdef NO_RECOVERY
- {NO_RECOVERY + DNS_BASE,
- "Unrecoverable DNS error"},
- #endif /*NO_RECOVERY*/
- #ifdef NO_DATA
- {NO_DATA + DNS_BASE,
- "No DNS data of requested type"},
- #endif /*NO_DATA*/
- #ifdef NO_ADDRESS
- {NO_ADDRESS + DNS_BASE,
- "No address record found in DNS"},
- #endif /*NO_ADDRESS*/
- /* Last dummy entry - must present */
- {0, 0}
- };
- size_t i, n = sizeof(errmap)/sizeof(errmap[0]) - 1/*dummy entry*/;
- /* always called on error, so get error number here if not having already*/
- if ( !error )
- error = errno;
- for (i = 0; i < n; i++) {
- if (errmap[i].errnum == error)
- return errmap[i].errtxt;
- }
- return strerror(error);
- }
- /******************************************************************************
- * Data Logging
- */
- static const char* s_ID(const SOCK sock, char* buf)
- {
- const char* sname;
- if ( !sock )
- return "";
- sname = IS_LISTENING(sock) ? "LSOCK" : "SOCK";
- if (sock->sock == SOCK_INVALID)
- sprintf(buf, "%s#%u[?]: ", sname, sock->id);
- else
- sprintf(buf, "%s#%u[%u]: ", sname, sock->id, (unsigned int)sock->sock);
- return buf;
- }
- /* Put socket description to the message, then log the transferred data
- */
- static void s_DoLog
- (const SOCK sock, EIO_Event event,
- const void* data, size_t size, const struct sockaddr* sa)
- {
- char head[128];
- char tail[128];
- char _id[32];
- if ( !CORE_GetLOG() )
- return;
- assert(sock);
- switch (event) {
- case eIO_Open:
- if (sock->type == eSOCK_Datagram) {
- if ( !sa ) {
- strcpy(head, "Datagram socket created");
- *tail = 0;
- } else {
- const struct sockaddr_in* sin = (const struct sockaddr_in*) sa;
- if ( !data ) {
- strcpy(head, "Datagram socket bound to port :");
- sprintf(tail, "%hu", ntohs(sin->sin_port));
- } else {
- strcpy(head, "Datagram socket connected to ");
- HostPortToString(sin->sin_addr.s_addr,ntohs(sin->sin_port),
- tail, sizeof(tail));
- }
- }
- } else {
- if (sock->type == eSOCK_ClientSide)
- strcpy(head, "Connecting to ");
- else if ( data )
- strcpy(head, "Connected to ");
- else
- strcpy(head, "Accepted from ");
- if (sa->sa_family == AF_INET) {
- const struct sockaddr_in* sin = (const struct sockaddr_in*) sa;
- HostPortToString(sin->sin_addr.s_addr, ntohs(sin->sin_port),
- tail, sizeof(tail));
- }
- #ifdef NCBI_OS_UNIX
- else if (sa->sa_family == AF_UNIX) {
- const struct sockaddr_un* un = (const struct sockaddr_un*) sa;
- strncpy0(tail, un->sun_path, sizeof(tail) - 1);
- }
- #endif /*NCBI_OS_UNIX*/
- else
- strcpy(tail, "???");
- }
- CORE_LOGF(eLOG_Trace, ("%s%s%s", s_ID(sock, _id), head, tail));
- break;
- case eIO_Read:
- case eIO_Write:
- if (sock->type == eSOCK_Datagram) {
- const struct sockaddr_in* sin = (const struct sockaddr_in*) sa;
- assert(sa && sa->sa_family == AF_INET);
- HostPortToString(sin->sin_addr.s_addr, ntohs(sin->sin_port),
- tail, sizeof(tail));
- sprintf(tail + strlen(tail), ", msg# %u",
- (unsigned)(event == eIO_Read ? sock->n_in : sock->n_out));
- } else {
- assert(sa == 0);
- *tail = 0;
- }
- sprintf(head, "%s%s%s at offset %lu%s%s", s_ID(sock, _id),
- event == eIO_Read
- ? (sock->type != eSOCK_Datagram && !size
- ? (data ? "EOF hit" : SOCK_STRERROR(SOCK_ERRNO))
- : "Read")
- : (sock->type != eSOCK_Datagram && !size
- ? SOCK_STRERROR(SOCK_ERRNO) : "Written"),
- sock->type == eSOCK_Datagram || size ? "" :
- (event == eIO_Read ? " while reading" : " while writing"),
- (unsigned long) (event == eIO_Read
- ? sock->n_read : sock->n_written),
- sa ? (event == eIO_Read ? " from " : " to ") : "", tail);
- CORE_DATA(data, size, head);
- break;
- case eIO_Close:
- {{
- int n = sprintf(head, "%lu byte%s",
- (unsigned long) sock->n_written,
- sock->n_written == 1 ? "" : "s");
- if (sock->type == eSOCK_Datagram ||
- sock->n_out != sock->n_written) {
- sprintf(head + n, "/%lu %s%s",
- (unsigned long) sock->n_out,
- sock->type == eSOCK_Datagram ? "msg" : "total byte",
- sock->n_out == 1 ? "" : "s");
- }
- }}
- {{
- int n = sprintf(tail, "%lu byte%s",
- (unsigned long) sock->n_read,
- sock->n_read == 1 ? "" : "s");
- if (sock->type == eSOCK_Datagram ||
- sock->n_in != sock->n_read) {
- sprintf(tail + n, "/%lu %s%s",
- (unsigned long) sock->n_in,
- sock->type == eSOCK_Datagram ? "msg" : "total byte",
- sock->n_in == 1 ? "" : "s");
- }
- }}
- CORE_LOGF(eLOG_Trace, ("%s%s (out: %s, in: %s)", s_ID(sock, _id),
- sock->type == eSOCK_ServerSideKeep
- ? "Leaving" : "Closing", head,tail));
- break;
- default:
- CORE_LOGF(eLOG_Error, ("%s[SOCK::s_DoLog] Invalid event %u",
- s_ID(sock, _id), (unsigned int) event));
- assert(0);
- break;
- }
- }
- extern ESwitch SOCK_SetDataLoggingAPI(ESwitch log)
- {
- ESwitch old = s_Log;
- if (log == eDefault)
- log = eOff;
- s_Log = log;
- return old;
- }
- extern ESwitch SOCK_SetDataLogging(SOCK sock, ESwitch log)
- {
- ESwitch old = sock->log;
- sock->log = log;
- return old;
- }
- /******************************************************************************
- * API Initialization and Shutdown/Cleanup
- */
- extern void SOCK_AllowSigPipeAPI(void)
- {
- #ifdef NCBI_OS_UNIX
- s_AllowSigPipe = 1/*true - API will not mask SIGPIPE out at init*/;
- #endif /*NCBI_OS_UNIX*/
- return;
- }
- #if 0/*defined(_DEBUG) && !defined(NDEBUG)*/
- # if !defined(__GNUC__) && !defined(offsetof)
- # define offsetof(T, F) ((size_t)((char*) &(((T*) 0)->F) - (char*) 0))
- # endif
- static void s_ShowDataLayout(void)
- {
- CORE_LOGF(eLOG_Note, ("SOCK data layout:n"
- " Sizeof(SOCK_struct) = %u, offsets follown"
- "tsock: %un"
- "tid: %un"
- "thost: %un"
- "tport: %un"
- "tbitfield: 16 bitsn"
- "tr_timeout: %un"
- "tr_tv: %un"
- "tr_to: %un"
- "tw_timeout: %un"
- "tw_tv: %un"
- "tw_to: %un"
- "tc_timeout: %un"
- "tc_tv: %un"
- "tc_to: %un"
- "tr_buf: %un"
- "tw_buf: %un"
- "tw_len: %un"
- "tn_read: %un"
- "tn_written: %un"
- "tn_in: %un"
- "tn_out: %u"
- # ifdef NCBI_OS_UNIX
- "ntfile: %u"
- # endif /*NCBI_OS_UNIX*/
- , (unsigned int) sizeof(SOCK_struct),
- (unsigned int) offsetof(SOCK_struct, sock),
- (unsigned int) offsetof(SOCK_struct, id),
- (unsigned int) offsetof(SOCK_struct, host),
- (unsigned int) offsetof(SOCK_struct, port),
- (unsigned int) offsetof(SOCK_struct, r_timeout),
- (unsigned int) offsetof(SOCK_struct, r_tv),
- (unsigned int) offsetof(SOCK_struct, r_to),
- (unsigned int) offsetof(SOCK_struct, w_timeout),
- (unsigned int) offsetof(SOCK_struct, w_tv),
- (unsigned int) offsetof(SOCK_struct, w_to),
- (unsigned int) offsetof(SOCK_struct, c_timeout),
- (unsigned int) offsetof(SOCK_struct, c_tv),
- (unsigned int) offsetof(SOCK_struct, c_to),
- (unsigned int) offsetof(SOCK_struct, r_buf),
- (unsigned int) offsetof(SOCK_struct, w_buf),
- (unsigned int) offsetof(SOCK_struct, w_len),
- (unsigned int) offsetof(SOCK_struct, n_read),
- (unsigned int) offsetof(SOCK_struct, n_written),
- (unsigned int) offsetof(SOCK_struct, n_in),
- (unsigned int) offsetof(SOCK_struct, n_out)
- # ifdef NCBI_OS_UNIX
- , (unsigned int) offsetoff(SOCK_struct, file)
- # endif /*NCBI_OS_UNIX*/
- ));
- }
- #endif
- extern EIO_Status SOCK_InitializeAPI(void)
- {
- static int/*bool*/ s_AtExitSet = 0;
- CORE_LOCK_WRITE;
- if ( s_Initialized ) {
- CORE_UNLOCK;
- return eIO_Success;
- }
- #if 0/*defined(_DEBUG) && !defined(NDEBUG)*/
- s_ShowDataLayout();
- #endif
- #if defined(NCBI_OS_MSWIN)
- {{
- WSADATA wsadata;
- int x_errno = WSAStartup(MAKEWORD(1,1), &wsadata);
- if (x_errno != 0) {
- CORE_UNLOCK;
- CORE_LOG_ERRNO_EX(eLOG_Error, x_errno, SOCK_STRERROR(x_errno),
- "[SOCK::InitializeAPI] Failed WSAStartup()");
- return eIO_Unknown;
- }
- }}
- #elif defined(NCBI_OS_UNIX)
- if ( !s_AllowSigPipe ) {
- struct sigaction sa;
- if (sigaction(SIGPIPE, 0, &sa) < 0 || sa.sa_handler == SIG_DFL) {
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = SIG_IGN;
- sigaction(SIGPIPE, &sa, 0);
- }
- }
- #endif /*platform-specific init*/
- s_Initialized = 1/*true*/;
- if ( !s_AtExitSet ) {
- atexit((void (*)(void)) SOCK_ShutdownAPI);
- s_AtExitSet = 1;
- }
- CORE_UNLOCK;
- return eIO_Success;
- }
- extern EIO_Status SOCK_ShutdownAPI(void)
- {
- CORE_LOCK_WRITE;
- if ( !s_Initialized ) {
- CORE_UNLOCK;
- return eIO_Success;
- }
- s_Initialized = 0/*false*/;
- #if defined(NCBI_OS_MSWIN)
- {{
- int x_errno = WSACleanup() ? SOCK_ERRNO : 0;
- CORE_UNLOCK;
- if ( x_errno ) {
- CORE_LOG_ERRNO_EX(eLOG_Warning, x_errno, SOCK_STRERROR(x_errno),
- "[SOCK::ShutdownAPI] Failed WSACleanup()");
- return eIO_Unknown;
- }
- }}
- #else
- CORE_UNLOCK;
- #endif /*NCBI_OS_MSWIN*/
- return eIO_Success;
- }
- /******************************************************************************
- * LSOCK & SOCK AUXILIARIES
- */
- /* STimeout <--> struct timeval conversions
- */
- static STimeout *s_tv2to(const struct timeval* tv, STimeout* to)
- {
- if ( !tv )
- return 0;
- to->sec = (unsigned int) tv->tv_sec;
- to->usec = (unsigned int) tv->tv_usec;
- return to;
- }
- static struct timeval* s_to2tv(const STimeout* to, struct timeval* tv)
- {
- if ( !to )
- return 0;
- tv->tv_sec = to->usec / 1000000 + to->sec;
- tv->tv_usec = to->usec % 1000000;
- return tv;
- }
- /* Switch the specified socket I/O between blocking and non-blocking mode
- */
- static int/*bool*/ s_SetNonblock(TSOCK_Handle sock, int/*bool*/ nonblock)
- {
- #if defined(NCBI_OS_MSWIN)
- unsigned long argp = nonblock ? 1 : 0;
- return ioctlsocket(sock, FIONBIO, &argp) == 0;
- #elif defined(NCBI_OS_UNIX) || defined(NCBI_OS_MAC)
- return fcntl(sock, F_SETFL,
- nonblock ?
- fcntl(sock, F_GETFL, 0) | O_NONBLOCK :
- fcntl(sock, F_GETFL, 0) & (int) ~O_NONBLOCK) != -1;
- #else
- # error "Unsupported platform"
- #endif /*platform-specific ioctl*/
- }
- static int/*bool*/ s_SetReuseAddress(TSOCK_Handle x_sock, int/*bool*/ on_off)
- {
- #if defined(NCBI_OS_UNIX) || defined(NCBI_OS_MSWIN)
- /* setsockopt() is not implemented for MAC (in MIT socket emulation lib) */
- # ifdef NCBI_OS_MSWIN
- BOOL reuse_addr = on_off ? TRUE : FALSE;
- # else
- int reuse_addr = on_off ? 1 : 0;
- # endif /*NCBI_OS_MSWIN*/
- return !setsockopt(x_sock, SOL_SOCKET, SO_REUSEADDR,
- (char*) &reuse_addr, sizeof(reuse_addr));
- #else
- return 1;
- #endif /*NCBI_OS_UNIX || NCBI_OS_MSWIN*/
- }
- static EIO_Status s_Status(SOCK sock, EIO_Event direction)
- {
- assert(sock && sock->sock != SOCK_INVALID);
- switch ( direction ) {
- case eIO_Read:
- return sock->type != eSOCK_Datagram && sock->eof
- ? eIO_Closed : sock->r_status;
- case eIO_Write:
- return sock->w_status;
- default:
- /*should never get here*/
- assert(0);
- break;
- }
- return eIO_InvalidArg;
- }
- /* compare 2 normialized timeval timeouts: "whether v1 is less than v2" */
- static int/*bool*/ s_Less(const struct timeval* v1, const struct timeval* v2)
- {
- if (!v1)
- return 0;
- if (!v2)
- return !!v1;
- if (v1->tv_sec > v2->tv_sec)
- return 0;
- if (v1->tv_sec < v2->tv_sec)
- return 1;
- return v1->tv_usec < v2->tv_usec;
- }
- /* Select on the socket I/O (multiple sockets).
- * "Event" field is not considered for entries, whose "sock" field is 0,
- * "revent" for those entries is always "eIO_Open". For all other entries
- * only those sockets will be considered, whose "revent" field does not
- * contain "eIO_Open" value. If at least one non-"eIO_Open" status found
- * in "revent", the call terminates with "eIO_Success" status (after,
- * however checking all other entries for validity). No additional checks
- * are made for the pre-ready entries.
- *
- * This function does not check datagram sockets with the select() system call
- * at all if the number of requested sockets is more than 1 (cf. SOCK_Poll()).
- *
- * If "eIO_Write" event is inquired on a stream socket, and the socket is
- * marked for upread, then returned "revent" may also include "eIO_Read" to
- * indicate that some input is available on that socket.
- * If "eIO_Read" event is inquired on an array (n != 1) including stream
- * socket(s) and some sockets still have connection/data pending, those
- * "revent" field may then include "eIO_Write" to indicate that
- * connection can be completed/data sent.
- *
- * Return eIO_Success when at least one socket is found either ready
- * (including "eIO_Read" event on "eIO_Write" for upreadable sockets
- * and "eIO_Write" on "eIO_Read" for sockets in pending state)
- * or failing ("revent" contains "eIO_Close").
- * Return "eIO_Timeout", if timeout expired before any socket became available.
- * Any other return code indicates some failure.
- */
- static EIO_Status s_Select(size_t n,
- SSOCK_Poll polls[],
- const struct timeval* tv)
- {
- int/*bool*/ write_only = 1;
- int/*bool*/ read_only = 1;
- int/*bool*/ ready = 0;
- int/*bool*/ bad = 0;
- fd_set r_fds, w_fds, e_fds;
- int n_fds;
- struct timeval x_tv;
- size_t i;
- if ( tv )
- x_tv = *tv;
- for (;;) { /* (optionally) auto-resume if interrupted by a signal */
- struct timeval xx_tv;
- n_fds = 0;
- FD_ZERO(&r_fds);
- FD_ZERO(&w_fds);
- FD_ZERO(&e_fds);
- for (i = 0; i < n; i++) {
- if ( !polls[i].sock ) {
- polls[i].revent = eIO_Open;
- continue;
- }
- if ( polls[i].revent ) {
- ready = 1;
- continue;
- }
- if (polls[i].event &&
- (EIO_Event)(polls[i].event | eIO_ReadWrite) == eIO_ReadWrite) {
- TSOCK_Handle fd = polls[i].sock->sock;
- if (fd != SOCK_INVALID) {
- int/*bool*/ ls = IS_LISTENING(polls[i].sock);
- if (!ls && n != 1 && polls[i].sock->type == eSOCK_Datagram)
- continue;
- if (ready || bad)
- continue;
- switch (polls[i].event) {
- case eIO_Write:
- case eIO_ReadWrite:
- if (!ls) {
- if (polls[i].sock->type == eSOCK_Datagram ||
- polls[i].sock->w_status != eIO_Closed) {
- read_only = 0;
- FD_SET(fd, &w_fds);
- if (polls[i].sock->type == eSOCK_Datagram ||
- polls[i].sock->pending)
- break;
- if (polls[i].event == eIO_Write &&
- (polls[i].sock->r_on_w == eOff
- || (polls[i].sock->r_on_w == eDefault
- && s_ReadOnWrite != eOn)))
- break;
- } else if (polls[i].event == eIO_Write)
- break;
- } else if (polls[i].event == eIO_Write)
- break;
- /*FALLTHRU*/
- case eIO_Read:
- if (polls[i].sock->type != eSOCK_Datagram
- && (polls[i].sock->r_status == eIO_Closed ||
- polls[i].sock->eof))
- break;
- write_only = 0;
- FD_SET(fd, &r_fds);
- if (polls[i].sock->type == eSOCK_Datagram ||
- polls[i].event != eIO_Read ||
- polls[i].sock->w_status == eIO_Closed ||
- n == 1 || (!polls[i].sock->pending &&
- !polls[i].sock->w_len))
- break;
- read_only = 0;
- FD_SET(fd, &w_fds);
- break;
- default:
- /* should never get here */
- assert(0);
- break;
- }
- FD_SET(fd, &e_fds);
- if (n_fds < (int) fd)
- n_fds = (int) fd;
- } else {
- polls[i].revent = eIO_Close;
- ready = 1;
- }
- } else {
- polls[i].revent = eIO_Close;
- bad = 1;
- }
- }
- if ( bad )
- return eIO_InvalidArg;
- if ( ready )
- return eIO_Success;
- if (!tv || s_Less(s_SelectTimeout, &x_tv)) {
- if ( s_SelectTimeout ) {
- xx_tv = *s_SelectTimeout;
- }
- } else
- xx_tv = x_tv;
- n_fds = select(SOCK_NFDS((TSOCK_Handle) n_fds),
- write_only ? 0 : &r_fds, read_only ? 0 : &w_fds,
- &e_fds, tv || s_SelectTimeout ? &xx_tv : 0);
- /* timeout has expired */
- if (n_fds == 0) {
- if ( !tv )
- continue;
- if ( s_Less(s_SelectTimeout, &x_tv) ) {
- x_tv.tv_sec -= s_SelectTimeout->tv_sec;
- x_tv.tv_usec -= s_SelectTimeout->tv_usec;
- continue;
- }
- return eIO_Timeout;
- }
- if (n_fds > 0)
- break;
- /* n_fds < 0 */
- if (SOCK_ERRNO != SOCK_EINTR) {
- int x_errno = SOCK_ERRNO;
- char _id[32];
- CORE_LOGF_ERRNO_EX(eLOG_Trace, x_errno, SOCK_STRERROR(x_errno),
- ("%s[SOCK::s_Select] Failed select()",
- n == 1 ? s_ID(polls[0].sock, _id) : ""));
- return eIO_Unknown;
- }
- if ((n != 1 && s_InterruptOnSignal == eOn) ||
- (n == 1 && (polls[0].sock->i_on_sig == eOn
- || (polls[0].sock->i_on_sig == eDefault
- && s_InterruptOnSignal == eOn)))) {
- return eIO_Interrupt;
- }
- }
- n_fds = 0;
- for (i = 0; i < n; i++) {
- if ( polls[i].sock ) {
- TSOCK_Handle fd = polls[i].sock->sock;
- assert(polls[i].revent == eIO_Open);
- if (fd != SOCK_INVALID) {
- if (!write_only && FD_ISSET(fd, &r_fds))
- polls[i].revent = eIO_Read;
- if (!read_only && FD_ISSET(fd, &w_fds))
- polls[i].revent = (EIO_Event)(polls[i].revent | eIO_Write);
- if (!polls[i].revent && FD_ISSET(fd, &e_fds))
- polls[i].revent = eIO_Close;
- } else
- polls[i].revent = eIO_Close;
- if (polls[i].revent != eIO_Open)
- n_fds++;
- }
- }
- assert(n_fds != 0);
- /* success; can do I/O now */
- return eIO_Success;
- }
- /******************************************************************************
- * UTILITY
- */
- extern const STimeout* SOCK_SetSelectInternalRestartTimeout(const STimeout* t)
- {
- static struct timeval s_NewTmo;
- static STimeout s_OldTmo;
- const STimeout* retval = s_tv2to(s_SelectTimeout, &s_OldTmo);
- s_SelectTimeout = s_to2tv(t, &s_NewTmo);
- return retval;
- }
- /******************************************************************************
- * LISTENING SOCKET
- */
- extern EIO_Status LSOCK_Create(unsigned short port,
- unsigned short backlog,
- LSOCK* lsock)
- {
- return LSOCK_CreateEx(port, backlog, lsock, eDefault);
- }
- extern EIO_Status LSOCK_CreateEx(unsigned short port,
- unsigned short backlog,
- LSOCK* lsock,
- ESwitch log)
- {
- unsigned int x_id = ++s_ID_Counter;
- TSOCK_Handle x_lsock;
- struct sockaddr_in addr;
- *lsock = 0;
- /* initialize internals */
- verify(s_Initialized || SOCK_InitializeAPI() == eIO_Success);
- /* create new(listening) socket */
- if ((x_lsock = socket(AF_INET, SOCK_STREAM, 0)) == SOCK_INVALID) {
- int x_errno = SOCK_ERRNO;
- CORE_LOGF_ERRNO_EX(eLOG_Error, x_errno, SOCK_STRERROR(x_errno),
- ("LSOCK#%u[?]: [LSOCK::Create] "
- " Cannot create socket", x_id));
- return eIO_Unknown;
- }
- /*
- * It was confirmed(?) that at least under Solaris 2.5 this precaution:
- * 1) makes the address released immediately after the process
- * termination;
- * 2) still issue EADDINUSE error on the attempt to bind() to the
- * same address being in-use by a living process (if SOCK_STREAM).
- */
- if ( !s_SetReuseAddress(x_lsock, 1/*true*/) ) {
- int x_errno = SOCK_ERRNO;
- CORE_LOGF_ERRNO_EX(eLOG_Error, x_errno, SOCK_STRERROR(x_errno),
- ("LSOCK#%u[%u]: [LSOCK::Create] "
- " Failed setsockopt(REUSEADDR)",
- x_id, (unsigned int) x_lsock));
- SOCK_CLOSE(x_lsock);
- return eIO_Unknown;
- }
- /* bind */
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
- addr.sin_port = htons(port);
- #ifdef HAVE_SIN_LEN
- addr.sin_len = sizeof(addr);
- #endif /*HAVE_SIN_LEN*/
- if (bind(x_lsock, (struct sockaddr*) &addr, sizeof(addr)) != 0) {
- int x_errno = SOCK_ERRNO;
- CORE_LOGF_ERRNO_EX(eLOG_Error, x_errno, SOCK_STRERROR(x_errno),
- ("LSOCK#%u[%u]: [LSOCK::Create] Failed bind(:%hu)",
- x_id, (unsigned int) x_lsock, port));
- SOCK_CLOSE(x_lsock);
- return x_errno == SOCK_EADDRINUSE ? eIO_Closed : eIO_Unknown;
- }
- /* listen */
- if (listen(x_lsock, backlog) != 0) {
- int x_errno = SOCK_ERRNO;
- CORE_LOGF_ERRNO_EX(eLOG_Error, x_errno, SOCK_STRERROR(x_errno),
- ("LSOCK#%u[%u]: [LSOCK::Create] Failed listen(%hu)"
- , x_id, (unsigned int) x_lsock, backlog));
- SOCK_CLOSE(x_lsock);
- return eIO_Unknown;
- }
- /* set to non-blocking mode */
- if ( !s_SetNonblock(x_lsock, 1/*true*/) ) {
- CORE_LOGF(eLOG_Error, ("LSOCK#%u[%u]: [LSOCK::Create] "
- " Cannot set socket to non-blocking mode",
- x_id, (unsigned int) x_lsock));
- SOCK_CLOSE(x_lsock);
- return eIO_Unknown;
- }
- /* allocate memory for the internal socket structure */
- if ( !(*lsock = (LSOCK) calloc(1, sizeof(**lsock))) )
- return eIO_Unknown;
- (*lsock)->sock = x_lsock;
- (*lsock)->id = x_id;
- (*lsock)->log = log;
- (*lsock)->i_on_sig = eDefault;
- SET_LISTENING(*lsock);
- /* statistics & logging */
- if (log == eOn || (log == eDefault && s_Log == eOn)) {
- CORE_LOGF(eLOG_Trace, ("LSOCK#%u[%u]: Listening at port :%hu",
- x_id, (unsigned int) x_lsock, port));
- }
- return eIO_Success;
- }
- extern EIO_Status LSOCK_Accept(LSOCK lsock,
- const STimeout* timeout,
- SOCK* sock)
- {
- struct sockaddr_in addr;
- unsigned int x_id;
- TSOCK_Handle x_sock;
- if (lsock->sock == SOCK_INVALID) {
- CORE_LOGF(eLOG_Error, ("LSOCK#%u[?]: [LSOCK::Accept] "
- " Invalid socket", lsock->id));
- assert(0);
- return eIO_Unknown;
- }
- {{ /* wait for the connection request to come (up to timeout) */
- EIO_Status status;
- SSOCK_Poll poll;
- struct timeval tv;
- poll.sock = (SOCK) lsock;
- poll.event = eIO_Read;
- poll.revent = eIO_Open;
- if ((status = s_Select(1, &poll, s_to2tv(timeout,&tv))) != eIO_Success)
- return status;
- if (poll.revent == eIO_Close)
- return eIO_Unknown;
- assert(poll.event == eIO_Read && poll.revent == eIO_Read);
- }}
- x_id = (lsock->id * 1000 + ++s_ID_Counter) * 1000;
- {{ /* accept next connection */
- SOCK_socklen_t addrlen = (SOCK_socklen_t) sizeof(addr);
- memset(&addr, 0, sizeof(addr));
- #ifdef HAVE_SIN_LEN
- addr.sin_len = sizeof(addr);
- #endif
- if ((x_sock = accept(lsock->sock, (struct sockaddr*) &addr, &addrlen))
- == SOCK_INVALID) {
- int x_errno = SOCK_ERRNO;
- CORE_LOGF_ERRNO_EX(eLOG_Error, x_errno, SOCK_STRERROR(x_errno),
- ("LSOCK#%u[%u]: [LSOCK::Accept] "
- " Failed accept()", lsock->id,
- (unsigned int) lsock->sock));
- return eIO_Unknown;
- }
- lsock->n_accept++;
- assert(addr.sin_family == AF_INET);
- /* man accept(2) notes that non-blocking state may not be inherited */
- if ( !s_SetNonblock(x_sock, 1/*true*/) ) {
- CORE_LOGF(eLOG_Error, ("SOCK#%u[%u]: [LSOCK::Accept] Cannot"
- " set accepted socket to non-blocking mode",
- x_id, (unsigned int) x_sock));
- SOCK_CLOSE(x_sock);
- return eIO_Unknown;
- }
- if (s_ReuseAddress && !s_SetReuseAddress(x_sock, 1/*true*/)) {
- int x_errno = SOCK_ERRNO;
- CORE_LOGF_ERRNO_EX(eLOG_Warning, x_errno, SOCK_STRERROR(x_errno),
- ("SOCK#%u[%u]: [LSOCK::Accept] "
- " Failed setsockopt(REUSEADDR)",
- x_id, (unsigned int) x_sock));
- }
- }}
- /* create new SOCK structure */
- if ( !(*sock = (SOCK) calloc(1, sizeof(**sock))) ) {
- SOCK_CLOSE(x_sock);
- return eIO_Unknown;
- }
- /* success */
- (*sock)->sock = x_sock;
- (*sock)->id = x_id;
- (*sock)->host = addr.sin_addr.s_addr;
- (*sock)->port = addr.sin_port;
- (*sock)->log = lsock->log;
- (*sock)->type = eSOCK_ServerSide;
- (*sock)->r_on_w = eDefault;
- (*sock)->i_on_sig = eDefault;
- (*sock)->r_status = eIO_Success;
- (*sock)->eof = 0/*false*/;
- (*sock)->w_status = eIO_Success;
- (*sock)->pending = 0/*connected*/;
- /* all timeouts zeroed - infinite */
- BUF_SetChunkSize(&(*sock)->r_buf, SOCK_BUF_CHUNK_SIZE);
- /* w_buf is unused for accepted sockets */
- /* statistics & logging */
- if (lsock->log == eOn || (lsock->log == eDefault && s_Log == eOn))
- s_DoLog(*sock, eIO_Open, 0, 0, (struct sockaddr*) &addr);
- return eIO_Success;
- }
- extern EIO_Status LSOCK_Close(LSOCK lsock)
- {
- EIO_Status status;
- if (lsock->sock == SOCK_INVALID) {
- CORE_LOGF(eLOG_Error, ("LSOCK#%u[?]: [LSOCK::Close] "
- " Invalid socket", lsock->id));
- assert(0);
- return eIO_Unknown;
- }
- /* set the socket back to blocking mode */
- if ( !s_SetNonblock(lsock->sock, 0/*false*/) ) {
- CORE_LOGF(eLOG_Warning, ("LSOCK#%u[%u]: [LSOCK::Close] "
- " Cannot set socket back to blocking mode",
- lsock->id, (unsigned int) lsock->sock));
- }
- /* statistics & logging */
- if (lsock->log == eOn || (lsock->log == eDefault && s_Log == eOn)) {
- CORE_LOGF(eLOG_Trace, ("LSOCK#%u[%u]: Closing at port :%hu "
- "(%u accept%s total)", lsock->id,
- (unsigned int) lsock->sock, lsock->port,
- lsock->n_accept, lsock->n_accept == 1? "":"s"));
- }
- status = eIO_Success;
- for (;;) { /* close persistently - retry if interrupted by a signal */
- /* success */
- if (SOCK_CLOSE(lsock->sock) == 0)
- break;
- /* error */
- if (SOCK_ERRNO != SOCK_EINTR) {
- int x_errno = SOCK_ERRNO;
- CORE_LOGF_ERRNO_EX(eLOG_Error, x_errno, SOCK_STRERROR(x_errno),
- ("LSOCK#%u[%u]: [LSOCK::Close] Failed close()",
- lsock->id, (unsigned int) lsock->sock));
- status = eIO_Unknown;
- break;
- }
- }
- /* cleanup & return */
- lsock->sock = SOCK_INVALID;
- free(lsock);
- return status;
- }
- extern EIO_Status LSOCK_GetOSHandle(LSOCK lsock,
- void* handle,
- size_t handle_size)
- {
- if (!handle || handle_size != sizeof(lsock->sock)) {
- CORE_LOGF(eLOG_Error, ("LSOCK#%u[%u]: [LSOCK::GetOSHandle] "
- " Invalid handle %s%lu", lsock->id,
- (unsigned int) lsock->sock, handle? "size ": "",
- handle ? (unsigned long) handle_size : 0));
- assert(0);
- return eIO_InvalidArg;
- }
- memcpy(handle, &lsock->sock, handle_size);
- return lsock->sock == SOCK_INVALID ? eIO_Closed : eIO_Success;
- }
- /******************************************************************************
- * SOCKET
- */
- /* connect() could be async/interrupted by a signal or just cannot be
- * established immediately; yet, it must have been in progress
- * (asynchronous), so wait here for it to succeed (become writable).
- */
- static EIO_Status s_IsConnected(SOCK sock,
- const struct timeval* tv,
- int* x_errno,
- int/*bool*/ writeable)
- {
- EIO_Status status;
- #if defined(NCBI_OS_UNIX) || defined(NCBI_OS_MSWIN)
- SOCK_socklen_t x_len = (SOCK_socklen_t) sizeof(*x_errno);
- #endif /*NCBI_OS_UNIX || NCBI_OS_MSWIN*/
- SSOCK_Poll poll;
- *x_errno = 0;
- if (sock->w_status == eIO_Closed)
- return eIO_Closed;
- if ( !writeable ) {
- poll.sock = sock;
- poll.event = eIO_Write;
- poll.revent = eIO_Open;
- status = s_Select(1, &poll, tv);
- if (status == eIO_Timeout)
- return status;
- } else {
- status = eIO_Success;
- poll.revent = eIO_Write;
- }
- #if defined(NCBI_OS_UNIX) || defined(NCBI_OS_MSWIN)
- if (status == eIO_Success &&
- (getsockopt(sock->sock, SOL_SOCKET, SO_ERROR, (void*) x_errno, &x_len)
- || *x_errno != 0)) {
- status = eIO_Unknown;
- }
- #endif /*NCBI_OS_UNIX || NCBI_OS_MSWIN*/
- if (status != eIO_Success || poll.revent != eIO_Write) {
- if ( !*x_errno )
- *x_errno = SOCK_ERRNO;
- if (*x_errno == SOCK_ECONNREFUSED)
- sock->r_status = sock->w_status = status = eIO_Closed;
- else if (status == eIO_Success)
- status = eIO_Unknown;
- } else if (s_ReuseAddress && !s_SetReuseAddress(sock->sock, 1/*true*/)) {
- int x_errno = SOCK_ERRNO;
- char _id[32];
- CORE_LOGF_ERRNO_EX(eLOG_Warning, x_errno, SOCK_STRERROR(x_errno),
- ("%s[SOCK::s_IsConnected] Failed "
- "setsockopt(REUSEADDR)", s_ID(sock, _id)));
- }
- return status;
- }
- /* Connect the (pre-allocated) socket to the specified "host:port" peer.
- * HINT: if "host" is NULL then assume(!) that the "sock" already exists,
- * and connect to the same host; the same is for zero "port".
- * NOTE: Client-side stream sockets only.
- */
- static EIO_Status s_Connect(SOCK sock,
- const char* host,
- unsigned short port,
- const STimeout* timeout)
- {
- char _id[32];
- int x_errno;
- TSOCK_Handle x_sock;
- unsigned int x_host;
- unsigned short x_port;
- struct sockaddr_in peer;
- int n;
- assert(sock->type == eSOCK_ClientSide);
- #ifdef NCBI_OS_UNIX
- assert(!sock->file[0]);
- #endif /*NCBI_OS_UNIX*/
- /* initialize internals */
- verify(s_Initialized || SOCK_InitializeAPI() == eIO_Success);
- /* get address of the remote host (assume the same host if it is NULL) */
- x_host = host && *host ? SOCK_gethostbyname(host) : sock->host;
- if ( !x_host ) {
- CORE_LOGF(eLOG_Error, ("%s[SOCK::s_Connect] Failed "
- "SOCK_gethostbyname("%.64s")",
- s_ID(sock, _id), host));
- return eIO_Unknown;
- }
- /* set the port to connect to (assume the same port if "port" is zero) */
- x_port = (unsigned short) (port ? htons(port) : sock->port);
- /* create new socket */
- if ((x_sock = socket(AF_INET, SOCK_STREAM, 0)) == SOCK_INVALID) {
- int x_errno = SOCK_ERRNO;
- CORE_LOGF_ERRNO_EX(eLOG_Error, x_errno, SOCK_STRERROR(x_errno),
- ("%s[SOCK::s_Connect] Cannot create socket",
- s_ID(sock, _id)));
- return eIO_Unknown;
- }
- sock->sock = x_sock;
- /* set the socket I/O to non-blocking mode */
- if ( !s_SetNonblock(x_sock, 1/*true*/) ) {
- CORE_LOGF(eLOG_Error, ("%s[SOCK::s_Connect] Cannot set socket to "
- "non-blocking mode", s_ID(sock, _id)));
- sock->sock = SOCK_INVALID;
- SOCK_CLOSE(x_sock);
- return eIO_Unknown;
- }
- /* fill in the server "addr" to connect to */
- memset(&peer, 0, sizeof(peer));
- peer.sin_family = AF_INET;
- peer.sin_addr.s_addr = x_host;
- peer.sin_port = x_port;
- #ifdef HAVE_SIN_LEN
- peer.sin_len = sizeof(peer);
- #endif /*HAVE_SIN_LEN*/
- /* statistics & logging */
- if (sock->log == eOn || (sock->log == eDefault && s_Log == eOn))
- s_DoLog(sock, eIO_Open, 0, 0, (struct sockaddr*) &peer);
- /* establish connection to the peer */
- sock->r_status = eIO_Success;
- sock->eof = 0/*false*/;
- sock->w_status = eIO_Success;
- assert(sock->w_len == 0);
- for (n = 0; ; n = 1) {
- if (connect(x_sock, (struct sockaddr*) &peer, sizeof(peer)) == 0) {
- x_errno = 0;
- break;
- }
- x_errno = SOCK_ERRNO;
- if (x_errno != SOCK_EINTR || sock->i_on_sig == eOn ||
- (sock->i_on_sig == eDefault && s_InterruptOnSignal))
- break;
- }
- if (x_errno) {
- if ((n != 0 || x_errno != SOCK_EINPROGRESS) &&
- (n == 0 || x_errno != SOCK_EALREADY) &&
- x_errno != SOCK_EWOULDBLOCK) {
- if (x_errno != SOCK_EINTR) {
- char addr[80];
- HostPortToString(x_host, ntohs(x_port), addr, sizeof(addr));
- CORE_LOGF_ERRNO_EX(eLOG_Error, x_errno, SOCK_STRERROR(x_errno),
- ("%s[SOCK::s_Connect] Failed connect() "
- "to %s", s_ID(sock, _id), addr));
- }
- sock->sock = SOCK_INVALID;
- SOCK_CLOSE(x_sock);
- /* unrecoverable error */
- return x_errno == SOCK_EINTR ? eIO_Interrupt : eIO_Unknown;
- }
- if (!timeout || timeout->sec || timeout->usec) {
- EIO_Status status;
- struct timeval tv;
- status = s_IsConnected(sock, s_to2tv(timeout, &tv), &x_errno, 0);
- if (status != eIO_Success) {
- char addr[80];
- HostPortToString(x_host, ntohs(x_port), addr, sizeof(addr));
- CORE_LOGF_ERRNO_EX(eLOG_Error, x_errno, SOCK_STRERROR(x_errno),
- ("%s[SOCK::s_Connect] Failed pending "
- "connect() to %s (%s)", s_ID(sock, _id),
- addr, IO_StatusStr(status)));
- sock->sock = SOCK_INVALID;
- SOCK_CLOSE(x_sock);
- return status;
- }
- sock->pending = 0/*connected*/;
- } else
- sock->pending = 1/*not yet connected*/;
- } else
- sock->pending = 0/*connected*/;
- /* success: do not change any timeouts */
- sock->host = x_host;
- sock->port = x_port;
- sock->w_len = BUF_Size(sock->w_buf);
- return eIO_Success;
- }
- /* To allow emulating "peek" using the NCBI data buffering.
- * (MSG_PEEK is not implemented on Mac, and it is poorly implemented
- * on Win32, so we had to implement this feature by ourselves.)
- * NOTE: This call is for stream sockets only.
- */
- static int s_Recv(SOCK sock,
- void* buffer,
- size_t size,
- int/*bool*/ peek)
- {
- char* x_buffer = (char*) buffer;
- char xx_buffer[4096];
- size_t n_read;
- assert(sock->type != eSOCK_Datagram && !sock->pending);
- if ( !size ) {
- /* internal upread use only */
- assert(sock->r_status != eIO_Closed && !sock->eof && peek && !buffer);
- n_read = 0;
- } else {
- /* read (or peek) from the internal buffer */
- n_read = peek ?
- BUF_Peek(sock->r_buf, x_buffer, size) :
- BUF_Read(sock->r_buf, x_buffer, size);
- if ((n_read && (n_read == size || !peek)) ||
- sock->r_status == eIO_Closed || sock->eof) {
- return (int) n_read;
- }
- }
- /* read (not just peek) from the socket */
- do {
- size_t n_todo;
- int x_read;
- if ( !size ) {
- /* internal upread call -- read out as much as possible */
- n_todo = sizeof(xx_buffer);
- x_buffer = xx_buffer;
- } else if ( !buffer ) {
- /* read to the temporary buffer (to store or discard later) */
- n_todo = size - n_read;
- if (n_todo > sizeof(xx_buffer))
- n_todo = sizeof(xx_buffer);
- x_buffer = xx_buffer;
- } else {
- /* read to the data buffer provided by user */
- n_todo = size - n_read;
- x_buffer += n_read;
- }
- /* recv */
- x_read = recv(sock->sock, x_buffer, n_todo, 0);
- /* success */
- if (x_read >= 0 ||
- (x_read < 0 && (SOCK_ERRNO == SOCK_ENOTCONN ||
- SOCK_ERRNO == SOCK_ECONNRESET ||
- SOCK_ERRNO == SOCK_ECONNABORTED ||
- SOCK_ERRNO == SOCK_ENETRESET))) {
- /* statistics & logging */
- if (sock->log == eOn || (sock->log == eDefault && s_Log == eOn)){
- s_DoLog(sock, eIO_Read, x_read >= 0 ? x_buffer : 0,
- (size_t)(x_read < 0 ? 0 : x_read), 0);
- }
- if (x_read <= 0) {
- /* catch EOF/failure */
- sock->eof = 1/*true*/;
- if (x_read == 0)
- sock->r_status = eIO_Success;
- else
- sock->r_status = sock->w_status = eIO_Closed;
- break;
- }
- } else {
- /* some error */
- int x_errno = SOCK_ERRNO;
- if (x_errno != SOCK_EWOULDBLOCK &&
- x_errno != SOCK_EAGAIN &&
- x_errno != SOCK_EINTR) {
- /* catch unknown ERROR */
- sock->r_status = eIO_Unknown;
- CORE_LOGF_ERRNO_EX(eLOG_Trace, x_errno, SOCK_STRERROR(x_errno),
- ("%s[SOCK::s_Recv] "
- " Failed recv()", s_ID(sock, xx_buffer)));
- }
- return n_read ? (int) n_read : -1;
- }
- assert(x_read > 0);
- /* if "peek" -- store the new read data in the internal input buffer */
- if (peek && !BUF_Write(&sock->r_buf, x_buffer, (size_t) x_read)) {
- CORE_LOGF_ERRNO(eLOG_Error, errno,
- ("%s[SOCK::s_Recv] Cannot store data in "
- "peek buffer", s_ID(sock, xx_buffer)));
- sock->eof = 1/*failure*/;
- sock->r_status = eIO_Closed;
- break;
- }
- /* successful read */
- sock->r_status = eIO_Success;
- sock->n_read += x_read;
- n_read += x_read;
- } while (!size || (!buffer && n_read < size));
- return (int) n_read;
- }
- static EIO_Status s_WritePending(SOCK, const struct timeval*, int);
- /* s_Select() with stall protection: try pull incoming data from sockets.
- * This method returns array of polls, "revent"s of which are always
- * compatible with requested "event"s. That is, it always strips additional
- * events that s_Select() may have set to indicate additional I/O events
- * some sockets are ready for. Return eIO_Timeout if no compatible events
- * were found (all sockets are not ready for inquired respective I/O) within
- * the specified timeout (and no other socket error was flagged).
- * Return eIO_Success if at least one socket is ready. Return the number
- * of sockets that are ready via pointer argument "n_ready" (may be NULL).
- * Return other error code to indicate error condition.
- */
- static EIO_Status s_SelectStallsafe(size_t n,
- SSOCK_Poll polls[],
- const struct timeval* tv,
- size_t* n_ready)
- {
- int/*bool*/ pending;
- EIO_Status status;
- size_t i, j;
- if ((status = s_Select(n, polls, tv)) != eIO_Success) {
- if ( n_ready )
- *n_ready = 0;
- return status;
- }
- j = 0;
- pending = 0;
- for (i = 0; i < n; i++) {
- if (polls[i].revent == eIO_Close)
- break;
- if (polls[i].revent & polls[i].event)
- break;
- if (polls[i].revent != eIO_Open && !pending) {
- pending = 1;
- j = i;
- }
- }
- if (i >= n && pending) {
- /* all sockets are not ready for the requested events */
- for (i = j; i < n; i++) {
- /* try to push pending writes */
- if (polls[i].event == eIO_Read && polls[i].revent == eIO_Write) {
- assert(n != 1);
- assert(polls[i].sock->pending || polls[i].sock->w_len);
- status = s_WritePending(polls[i].sock, tv, 1/*writeable*/);
- if (status != eIO_Success && status != eIO_Timeout) {
- polls[i].revent = eIO_Close;
- break;
- }
- continue;
- }
- /* try to find an immediately readable socket */
- if (polls[i].event != eIO_Write)
- continue;
- while (polls[i].revent == eIO_Read) {
- assert(polls[i].sock &&
- polls[i].sock->sock != SOCK_INVALID &&
- polls[i].sock->type != eSOCK_Datagram &&
- polls[i].sock->w_status != eIO_Closed &&
- polls[i].sock->r_status != eIO_Closed &&
- !polls[i].sock->eof &&
- !polls[i].sock->pending &&
- (polls[i].sock->r_on_w == eOn
- || (polls[i].sock->r_on_w == eDefault
- && s_ReadOnWrite == eOn)));
- /* try upread as much as possible data into internal buffer */
- s_Recv(polls[i].sock, 0, 0/*infinite*/, 1/*peek*/);
- /* then poll if writeable */
- polls[i].revent = eIO_Open;
- if ((status = s_Select(1, &polls[i], tv)) != eIO_Success)
- break;
- }
- if (status != eIO_Success || polls[i].revent == eIO_Write)
- break; /*error or can write now!*/
- }
- }
- j = 0;
- for (i = 0; i < n; i++) {
- if (polls[i].revent != eIO_Close) {
- polls[i].revent = (EIO_Event)(polls[i].revent & polls[i].event);
- if (polls[i].revent != eIO_Open)
- j++;
- } else
- j++;
- }
- if ( n_ready )
- *n_ready = j;
- return j ? eIO_Success : (status == eIO_Success ? eIO_Timeout : status);
- }
- static EIO_Status s_WipeRBuf(SOCK sock)
- {
- EIO_Status status;
- size_t size = BUF_Size(sock->r_buf);
- if (size && BUF_Read(sock->r_buf, 0, size) != size) {
- char _id[32];
- CORE_LOGF(eLOG_Error, ("%s[SOCK::s_WipeRBuf] "
- " Cannot drop aux. data buf", s_ID(sock, _id)));
- assert(0);
- status = eIO_Unknown;
- } else
- status = eIO_Success;
- return status;
- }
- /* For datagram sockets only! */
- static EIO_Status s_WipeWBuf(SOCK sock)
- {
- EIO_Status status;
- size_t size = BUF_Size(sock->w_buf);
- assert(sock->type == eSOCK_Datagram);
- if (size && BUF_Read(sock->w_buf, 0, size) != size) {
- char _id[32];
- CORE_LOGF(eLOG_Error, ("%s[SOCK::s_WipeWBuf] "
- " Cannot drop aux. data buf", s_ID(sock, _id)));
- assert(0);
- status = eIO_Unknown;
- } else
- status = eIO_Success;
- sock->eof = 0;
- return status;
- }
- /* Write data to the socket "as is" (as many bytes at once as possible).
- * Return eIO_Success if at least some bytes were written successfully.
- * Otherwise (no bytes written) return an error code to indicate the problem.
- * NOTE: This call is for stream sockets only.
- */
- static EIO_Status s_Send(SOCK sock,
- const void* buf,
- size_t size,
- size_t* n_written)
- {
- char _id[32];
- assert(size > 0 && sock->type != eSOCK_Datagram && *n_written == 0);
- for (;;) { /* optionally retry if interrupted by a signal */
- /* try to write */
- int x_written = send(sock->sock, (void*) buf, size, 0);
- int x_errno;
- if (x_written > 0) {
- /* statistics & logging */
- if (sock->log == eOn || (sock->log == eDefault && s_Log == eOn))
- s_DoLog(sock, eIO_Write, buf, (size_t) x_written, 0);
- sock->n_written += x_written;
- *n_written = x_written;
- sock->w_status = eIO_Success;
- break/*done*/;
- }
- x_errno = SOCK_ERRNO;
- /* don't want to handle all possible errors... let them be "unknown" */
- sock->w_status = eIO_Unknown;
- /* blocked -- retry if unblocked before the timeout expires */
- /* (use stall protection if specified) */
- if (x_errno == SOCK_EWOULDBLOCK || x_errno == SOCK_EAGAIN) {
- EIO_Status status;
- SSOCK_Poll poll;
- poll.sock = sock;
- poll.event = eIO_Write;
- poll.revent = eIO_Open;
- /* stall protection: try pull incoming data from the socket */
- status = s_SelectStallsafe(1, &poll, sock->w_timeout, 0);
- if (status != eIO_Success)
- return status;
- if (poll.revent == eIO_Close)
- return eIO_Unknown;
- assert(poll.revent == eIO_Write);
- continue;
- }
- if (x_errno != SOCK_EINTR) {
- /* forcibly closed by peer or shut down? */
- if (x_errno != SOCK_EPIPE && x_errno != SOCK_ENOTCONN &&
- x_errno != SOCK_ECONNRESET && x_errno != SOCK_ECONNABORTED &&
- x_errno != SOCK_ENETRESET) {
- CORE_LOGF_ERRNO_EX(eLOG_Trace, x_errno, SOCK_STRERROR(x_errno),
- ("%s[SOCK::s_Send] "
- " Failed send()", s_ID(sock, _id)));
- break;
- }
- sock->w_status = eIO_Closed;
- if (x_errno != SOCK_EPIPE)
- sock->r_status = eIO_Closed;
- if (sock->log == eOn || (sock->log == eDefault && s_Log == eOn))
- s_DoLog(sock, eIO_Write, 0, 0, 0);
- break;
- }
- if (sock->i_on_sig == eOn ||
- (sock->i_on_sig == eDefault && s_InterruptOnSignal == eOn)) {
- sock->w_status = eIO_Interrupt;
- break;
- }
- }
- return sock->w_status;
- }
- /* Wrapper for s_Send() that slices the output buffer for some brain-dead
- * systems (e.g. old Macs) that cannot handle large data chunks in "send()".
- * Return eIO_Success if some data were successfully sent; other
- * error code if no data were sent at all.
- */
- #ifdef SOCK_WRITE_SLICE
- static EIO_Status s_WriteSliced(SOCK sock,
- const void* buf,
- size_t size,
- size_t* n_written)
- {
- /* split output buffer by slices (of size <= SOCK_WRITE_SLICE)
- * before writing to the socket
- */
- EIO_Status status;
- assert(size > 0 && *n_written == 0);
- do {
- size_t n_io = size > SOCK_WRITE_SLICE ? SOCK_WRITE_SLICE : size;
- size_t n_io_done = 0;
- status = s_Send(sock, (char*) buf + *n_written, n_io, &n_io_done);
- if (status != eIO_Success)
- break;
- *n_written += n_io_done;
- if (n_io != n_io_done)
- break;
- size -= n_io_done;
- } while ( size );
- return status;
- }
- #else
- # define s_WriteSliced s_Send
- #endif /*SOCK_WRITE_SLICE*/
- static EIO_Status s_WritePending(SOCK sock,
- const struct timeval* tv,
- int/*bool*/ writeable)
- {
- const struct timeval* x_tv;
- EIO_Status status;
- int x_errno;
- size_t off;
- assert(sock->type != eSOCK_Datagram && sock->sock != SOCK_INVALID);
- if ( sock->pending ) {
- status = s_IsConnected(sock, tv, &x_errno, writeable);
- if (status != eIO_Success) {
- if (status != eIO_Timeout) {
- char addr[80];
- char _id[32];
- #ifdef NCBI_OS_UNIX
- if ( sock->file[0] ) {
- size_t len = strlen(sock->file);
- int/*bool*/ trunc = len > sizeof(addr) - 3 ? 1 : 0;
- sprintf(addr, ""%s%.*s"", trunc ? "..." : "",
- (int)(trunc ? sizeof(addr) - 6 : len),
- &sock->file[trunc ? len - sizeof(addr) + 6 : 0]);
- } else
- #endif /*NCBI_OS_UNIX*/
- HostPortToString(sock->host, ntohs(sock->port),
- addr, sizeof(addr));
- CORE_LOGF_ERRNO_EX(eLOG_Error, x_errno, SOCK_STRERROR(x_errno),
- ("%s[SOCK::s_WritePending] Failed pending "
- "connect() to %s", s_ID(sock, _id), addr));
- sock->w_status = status;
- }
- return status;
- }
- sock->pending = 0/*connected*/;
- }
- if (sock->w_len == 0 || sock->w_status == eIO_Closed)
- return eIO_Success;
- x_tv = sock->w_timeout;
- sock->w_timeout = tv;
- off = BUF_Size(sock->w_buf) - sock->w_len;
- do {
- char buf[4096];
- size_t n_written = 0;
- size_t n_write = BUF_PeekAt(sock->w_buf, off, buf, sizeof(buf));
- status = s_WriteSliced(sock, buf, n_write, &n_written);
- if (status != eIO_Success)
- break;
- sock->w_len -= n_written;
- off += n_written;
- } while ( sock->w_len );
- sock->w_timeout = x_tv;
- assert((sock->w_len != 0) == (status != eIO_Success));
- return status;
- }
- /* Read/Peek data from the socket. Always return eIO_Success if some data
- * were read (regardless of socket conditions that may include EOF/error).
- * Return other (error) code only if no data at all could be obtained.
- */
- static EIO_Status s_Read(SOCK sock,
- void* buf,
- size_t size,
- size_t* n_read,
- int/*bool*/ peek)
- {
- EIO_Status status;
- *n_read = 0;
- if (sock->type == eSOCK_Datagram) {
- *n_read = peek ?
- BUF_Peek(sock->r_buf, buf, size) :
- BUF_Read(sock->r_buf, buf, size);
- sock->r_status = *n_read || !size ? eIO_Success : eIO_Closed;
- return sock->r_status;
- }
- status = s_WritePending(sock, sock->r_timeout, 0);
- if (sock->pending || !size)
- return sock->pending ? status : s_Status(sock, eIO_Read);
- for (;;) { /* retry if either blocked or interrupted (optional) */
- /* try to read */
- int x_read = s_Recv(sock, buf, size, peek);
- int x_errno;
- if (x_read > 0) {
- assert((size_t) x_read <= size);
- *n_read = x_read;
- return eIO_Success;
- }
- if (sock->r_status == eIO_Unknown)
- break;
- if (sock->r_status == eIO_Closed || sock->eof) {
- if ( !sock->eof ) {
- char _id[32];
- CORE_LOGF(eLOG_Trace, ("%s[SOCK::s_Read] Socket has already "
- "been shut down for reading",
- s_ID(sock, _id)));
- }
- return eIO_Closed;
- }
- x_errno = SOCK_ERRNO;
- /* blocked -- wait for data to come; exit if timeout/error */
- if (x_errno == SOCK_EWOULDBLOCK || x_errno == SOCK_EAGAIN) {
- const struct timeval* tv = sock->r_timeout;
- SSOCK_Poll poll;
- if (tv && !tv->tv_sec && !tv->tv_usec)
- return eIO_Timeout;
- poll.sock = sock;
- poll.event = eIO_Read;
- poll.revent = eIO_Open;
- if ((status = s_Select(1, &poll, tv)) != eIO_Success)
- return status;
- if (poll.revent == eIO_Close)
- break;
- assert(poll.event == eIO_Read && poll.revent == eIO_Read);
- continue;
- }
- if (x_errno != SOCK_EINTR)
- break;
- if (sock->i_on_sig == eOn ||
- (sock->i_on_sig == eDefault && s_InterruptOnSignal == eOn)) {
- sock->r_status = eIO_Interrupt;
- break;
- }
- }
- /* don't want to handle all possible errors... let them be "unknown" */
- return eIO_Unknown;
- }
- /* Write to the socket. Return eIO_Success if some data were written.
- * Return other (error) code only if no data at all could be written.
- */
- static EIO_Status s_Write(SOCK sock,
- const void* buf,
- size_t size,
- size_t* n_written)
- {
- EIO_Status status;
- *n_written = 0;
- if (sock->type == eSOCK_Datagram) {
- if ( sock->eof )
- s_WipeWBuf(sock);
- if ( BUF_Write(&sock->w_buf, buf, size) ) {
- sock->w_status = eIO_Success;
- *n_written = size;
- } else
- sock->w_status = eIO_Unknown;
- return sock->w_status;
- }
- if (sock->w_status == eIO_Closed) {
- if (size != 0) {
- char _id[32];
- CORE_LOGF(eLOG_Trace, ("%s[SOCK::s_Write] Socket has already "
- "been shut down for writing",
- s_ID(sock, _id)));
- }
- return eIO_Closed;
- }
- if ((status = s_WritePending(sock, sock->w_timeout, 0)) != eIO_Success) {
- if (status == eIO_Timeout || status == eIO_Closed)
- return status;
- return size ? status : eIO_Success;
- }
- assert(sock->w_len == 0);
- return size ? s_WriteSliced(sock, buf, size, n_written) : eIO_Success;
- }
- /* For non-datagram sockets only */
- static EIO_Status s_Shutdown(SOCK sock,
- EIO_Event how,
- const struct timeval* tv)
- {
- EIO_Status status;
- char _id[32];
- int x_how;
- assert(sock->type != eSOCK_Datagram);
- switch ( how ) {
- case eIO_Read:
- if ( sock->eof ) {
- /* hit EOF (and may be not yet shut down) -- so, flag it as been
- * shut down, but do not call the actual system shutdown(),
- * as it can cause smart OS'es like Linux to complain
- */
- sock->eof = 0/*false*/;
- sock->r_status = eIO_Closed;
- }
- if (sock->r_status == eIO_Closed)
- return eIO_Success; /* has been shut down already */
- x_how = SOCK_SHUTDOWN_RD;
- sock->r_status = eIO_Closed;
- #ifdef NCBI_OS_MSWIN
- /* see comments at the end of eIO_Write case */
- return eIO_Success;
- #endif /*NCBI_OS_MSWIN*/
- break;
- case eIO_Write:
- if (sock->w_status == eIO_Closed)
- return eIO_Success; /* has been shut down already */
- if ((status = s_WritePending(sock, tv, 0)) != eIO_Success
- && (!tv || tv->tv_sec || tv->tv_usec)) {
- CORE_LOGF(eLOG_Warning, ("%s[SOCK::s_Shutdown] Shutting down for "
- "write with some output pending (%s)",
- s_ID(sock, _id), IO_StatusStr(status)));
- }
- x_how = SOCK_SHUTDOWN_WR;
- sock->w_status = eIO_Closed;
- #ifdef NCBI_OS_MSWIN
- /* on MS-Win, socket shutdown on writing apparently messes up (?!) *
- * with the later reading, esp. when reading a lot of data... */
- return eIO_Success;
- #endif /*NCBI_OS_MSWIN*/
- break;
- case eIO_ReadWrite:
- #ifdef NCBI_OS_MSWIN
- if (sock->r_status == eIO_Closed && sock->w_status == eIO_Closed)
- return eIO_Success;
- if (sock->w_status != eIO_Closed &&
- (status = s_WritePending(sock, tv, 0)) != eIO_Success
- && (!tv || tv->tv_sec || tv->tv_usec)) {
- CORE_LOGF(eLOG_Warning, ("%s[SOCK::s_Shutdown] Shutting down for "
- "R/W with some output pending (%s)",
- s_ID(sock, _id), IO_StatusStr(status)));