1 /* $Id: ncbi_socket.c 620599 2020-11-24 19:06:50Z lavr $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author:  Anton Lavrentiev, Denis Vakatov
27  *
28  * File Description:
29  *   Plain portable TCP/IP socket API for:  UNIX, MS-Win, MacOS
30  *     [UNIX ]   -DNCBI_OS_UNIX     -lresolv -lsocket -lnsl
31  *     [MSWIN]   -DNCBI_OS_MSWIN    ws2_32.lib
32  *
33  */
34 
35 /* Uncomment these(or specify "-DHAVE_GETADDRINFO -DHAVE_GETNAMEINFO") only if:
36  * 0) you are compiling this outside of the NCBI C or C++ Toolkits
37  *    (USE_NCBICONF is not #define'd), and
38  * 1) your platform has "getaddrinfo()" and "getnameinfo()", and
39  * 2) you are going to use this API code in multi-thread application, and
40  * 3) "gethostbyname()" gets called somewhere else in your code, and
41  * 4) you are aware that GLIBC implementation is rather heavy (creates tons of
42  *    test sockets on the fly), yet you prefer to use that API nonetheless
43  */
44 
45 /* #define HAVE_GETADDRINFO 1 */
46 /* #define HAVE_GETNAMEINFO 1 */
47 
48 /* Uncomment this (or specify "-DHAVE_GETHOSTBY***_R=") only if:
49  * 0) you are compiling this outside of the NCBI C or C++ Toolkits
50  *    (USE_NCBICONF is not #define'd), and
51  * 1) your platform has "gethostbyname_r()" but not "getnameinfo()", and
52  * 2) you are going to use this API code in multi-thread application, and
53  * 3) "gethostbyname()" gets called somewhere else in your code
54  */
55 
56 /*   Solaris: */
57 /* #define HAVE_GETHOSTBYNAME_R 5 */
58 /* #define HAVE_GETHOSTBYADDR_R 7 */
59 
60 /*   Linux, IRIX: */
61 /* #define HAVE_GETHOSTBYNAME_R 6 */
62 /* #define HAVE_GETHOSTBYADDR_R 8 */
63 
64 /* Uncomment this (or specify "-DHAVE_SIN_LEN") only if:
65  * 0) you are compiling this outside of the NCBI C or C++ Toolkits
66  *    (USE_NCBICONF is not #define'd), and
67  * 1) on your platform, struct sockaddr_in contains a field called "sin_len"
68  *    (and sockaddr_un::sun_len is then assumed to be also present).
69  */
70 
71 /* #define HAVE_SIN_LEN 1 */
72 
73 /* NCBI core headers
74  */
75 #include "ncbi_ansi_ext.h"
76 #include "ncbi_connssl.h"
77 #include "ncbi_once.h"
78 #include <connect/ncbi_connutil.h>
79 #include <connect/ncbi_socket_unix.h>
80 
81 /* Remaining platform-specific system headers
82  */
83 #ifdef NCBI_OS_UNIX
84 #  include <fcntl.h>
85 #  include <netdb.h>
86 #  include <netinet/in.h>
87 #  include <netinet/tcp.h>
88 #  ifdef NCBI_OS_LINUX
89 #    ifndef   IP_MTU
90 #      define IP_MTU  14
91 #    endif /*!IP_MTU*/
92 #  endif /*NCBI_OS_LINUX*/
93 #  if !defined(NCBI_OS_BEOS)
94 #    include <arpa/inet.h>
95 #  endif /*NCBI_OS_BEOS*/
96 #  include <signal.h>
97 #  include <sys/param.h>
98 #  ifdef HAVE_POLL_H
99 #    include <sys/poll.h>
100 #    ifndef   POLLRDHUP
101 #      define POLLRDHUP  POLLHUP
102 #    endif /*!POLLRDHUP*/
103 #  endif /*HAVE_POLL_H*/
104 #  include <sys/stat.h>
105 #  include <sys/un.h>
106 #  include <unistd.h>
107 #endif /*NCBI_OS_UNIX*/
108 
109 /* Portable standard C headers
110  */
111 #include <ctype.h>
112 #include <stdlib.h>
113 
114 #define NCBI_USE_ERRCODE_X   Connect_Socket
115 
116 
117 #ifndef   INADDR_LOOPBACK
118 #  define INADDR_LOOPBACK  0x7F000001
119 #endif /*!INADDR_LOOPBACK*/
120 #ifndef   IN_LOOPBACKNET
121 #  define IN_LOOPBACKNET   127
122 #endif /*!IN_LOOPBACKNET*/
123 #ifdef IN_CLASSA_MAX
124 #  if IN_CLASSA_MAX <= IN_LOOPBACKNET
125 #    error "IN_LOOPBACKNET is out of range"
126 #  endif /*IN_CLASSA_MAX<=IN_LOOPBACKNET*/
127 #endif /*IN_CLASSA_MAX*/
128 
129 #ifdef NCBI_COMPILER_MSVC
130 #  define sys_gethostname(a, b)  gethostname(a, (int)(b))
131 #else
132 #  define sys_gethostname        gethostname
133 #endif
134 
135 
136 #ifdef NCBI_MONKEY
137 /* A hack - we assume that SOCK variable is named "sock" in the code.
138    If the desired behavior is timeout, "sock" will be replaced with a
139    connection to a non-responsive server */
140 #  define send(a,b,c,d)                                                 \
141     (g_MONKEY_Send    ? g_MONKEY_Send(a,b,c,d,&sock) : send(a,b,c,d))
142 #  define recv(a,b,c,d)                                                 \
143     (g_MONKEY_Recv    ? g_MONKEY_Recv(a,b,c,d,&sock) : recv(a,b,c,d))
144 #  define connect(a,b,c)                                                \
145     (g_MONKEY_Connect ? g_MONKEY_Connect(a,b,c)      : connect(a,b,c))
146 #endif /*NCBI_MONKEY*/
147 
148 
149 /******************************************************************************
150  *  TYPEDEFS & MACROS
151  */
152 
153 
154 /* Minimal size of the data buffer chunk in the socket internal buffer(s) */
155 #define SOCK_BUF_CHUNK_SIZE   16384
156 
157 /* Macros for platform-dependent constants, error codes and functions
158  */
159 #if   defined(NCBI_OS_MSWIN)
160 
161 #  define SOCK_GHBX_MT_SAFE   1  /* for gethostby...() */
162 #  define SOCK_SEND_SLICE     (4 << 10)  /* 4K */
163 #  define SOCK_INVALID        INVALID_SOCKET
164 #  define SOCK_ERRNO          WSAGetLastError()
165 #  define SOCK_NFDS(s)        0
166 #  define SOCK_CLOSE(s)       closesocket(s)
167 #  define SOCK_EVENTS         (FD_CLOSE|FD_CONNECT|FD_OOB|FD_WRITE|FD_READ)
168 #  define WIN_INT_CAST        (int)
169 #  ifndef   WSA_INVALID_EVENT
170 #    define WSA_INVALID_EVENT ((WSAEVENT) 0)
171 #  endif /*!WSA_INVALID_EVENT*/
172 /* NCBI_OS_MSWIN */
173 
174 #elif defined(NCBI_OS_UNIX)
175 
176 #  ifdef NCBI_OS_CYGWIN
177      /* These do not work correctly as of cygwin 2.11.1 */
178 #    ifdef   SOCK_NONBLOCK
179 #      undef SOCK_NONBLOCK
180 #    endif /*SOCK_NONBLOCK*/
181 #    ifdef   SOCK_CLOEXEC
182 #      undef SOCK_CLOEXEC
183 #    endif /*SOCK_CLOEXEC*/
184 #  endif /*NCBI_OS_CYGWIN*/
185 
186 #  define SOCK_INVALID        (-1)
187 #  define SOCK_ERRNO          errno
188 #  define SOCK_NFDS(s)        ((s) + 1)
189 #  define WIN_INT_CAST        /* no cast */
190 #  ifdef NCBI_OS_BEOS
191 #    define SOCK_CLOSE(s)     closesocket(s)
192 #  else
193 #    define SOCK_CLOSE(s)     close(s)
194 #  endif /*NCBI_OS_BEOS*/
195 #  ifndef   INADDR_NONE
196 #    define INADDR_NONE       ((unsigned int)(~0UL))
197 #  endif  /*INADDR_NONE*/
198 /* NCBI_OS_UNIX */
199 
200 #  if defined(TCP_NOPUSH)  &&  !defined(TCP_CORK)
201 #    define TCP_CORK          TCP_NOPUSH  /* BSDism */
202 #  endif /*TCP_NOPUSH && !TCP_CORK*/
203 
204 #endif /*NCBI_OS*/
205 
206 #ifdef   sun
207 #  undef sun
208 #endif /*sun*/
209 
210 #ifndef   abs
211 #  define abs(a)              ((a) < 0 ? -(a) : (a))
212 #endif /*!abs*/
213 
214 #define MAXIDLEN              80
215 
216 #define SOCK_STRERROR(error)  s_StrError(0, (error))
217 
218 #define SOCK_LOOPBACK         (assert(INADDR_LOOPBACK), htonl(INADDR_LOOPBACK))
219 
220 #define SOCK_GET_TIMEOUT(s, t)      ((s)->t##_tv_set ? &(s)->t##_tv : 0)
221 
222 #define SOCK_SET_TIMEOUT(s, t, v)  (((s)->t##_tv_set = (v) ? 1 : 0)     \
223                                     ? (void)((s)->t##_tv = *(v)) : (void) 0)
224 
225 
226 #if defined(HAVE_SOCKLEN_T)  ||  defined(_SOCKLEN_T)
227 typedef socklen_t  TSOCK_socklen_t;
228 #else
229 typedef int        TSOCK_socklen_t;
230 #endif /*HAVE_SOCKLEN_T || _SOCKLEN_T*/
231 
232 
233 
234 /******************************************************************************
235  *  INTERNAL GLOBALS
236  */
237 
238 
239 const char g_kNcbiSockNameAbbr[] = "SOCK";
240 
241 
242 
243 /******************************************************************************
244  *  STATIC GLOBALS
245  */
246 
247 
248 /* Flag to indicate whether the API has been [de]initialized */
249 static volatile int/*bool*/ s_Initialized = 0/*-1=deinit;0=uninit;1=init*/;
250 
251 /* Which wait API to use, UNIX only */
252 static ESOCK_IOWaitSysAPI s_IOWaitSysAPI = eSOCK_IOWaitSysAPIAuto;
253 
254 /* Through ID counter */
255 static volatile unsigned int s_ID_Counter = 0;
256 
257 /* Read-while-writing switch */
258 static ESwitch s_ReadOnWrite = eOff;        /* no read-on-write by default   */
259 
260 /* Reuse address flag for newly created stream sockets */
261 static ESwitch s_ReuseAddress = eOff;       /* off by default                */
262 
263 /* I/O restart on signals */
264 static ESwitch s_InterruptOnSignal = eOff;  /* restart I/O by default        */
265 
266 /* Data/event logging */
267 static ESwitch s_Log = eOff;                /* no logging by default         */
268 
269 /* Select restart timeout */
270 static const struct timeval* s_SelectTimeout = 0; /* =0 (disabled) by default*/
271 
272 /* Flag to indicate whether API should mask SIGPIPE (during initialization)  */
273 #ifdef NCBI_OS_UNIX
274 static int/*bool*/ s_AllowSigPipe = 0/*false - mask SIGPIPE out*/;
275 #endif /*NCBI_OS_UNIX*/
276 
277 /* SSL support */
278 static SOCKSSL            s_SSL;
279 static FSSLSetup volatile s_SSLSetup;
280 
281 /* External error reporting */
282 static FSOCK_ErrHook volatile s_ErrHook;
283 static void*         volatile s_ErrData;
284 
285 
286 
287 /******************************************************************************
288  *  ERROR REPORTING
289  */
290 
291 
292 #define NCBI_INCLUDE_STRERROR_C
293 #include "ncbi_strerror.c"
294 
295 
s_StrError(SOCK sock,int error)296 static const char* s_StrError(SOCK sock, int error)
297 {
298     if (!error)
299         return 0;
300 
301     if (sock  &&  error < 0) {
302         FSSLError sslerror = sock->sslctx  &&  s_SSL ? s_SSL->Error : 0;
303         if (sslerror) {
304             char errbuf[256];
305             const char* strerr = sslerror(sock->sslctx->sess, error,
306                                           errbuf, sizeof(errbuf));
307             if (strerr  &&  *strerr)
308                 return ERR_STRDUP(strerr);
309         }
310     }
311     return s_StrErrorInternal(error);
312 }
313 
314 
315 #ifdef NCBI_OS_MSWIN
s_WinStrerror(DWORD error)316 static const char* s_WinStrerror(DWORD error)
317 {
318     TCHAR* str;
319     DWORD  rv;
320 
321     if (!error)
322         return 0;
323     str = NULL;
324     rv  = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
325                         FORMAT_MESSAGE_FROM_SYSTEM     |
326                         FORMAT_MESSAGE_MAX_WIDTH_MASK  |
327                         FORMAT_MESSAGE_IGNORE_INSERTS,
328                         NULL, error,
329                         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
330                         (LPTSTR) &str, 0, NULL);
331     if (!rv  &&  str) {
332         LocalFree((HLOCAL) str);
333         return 0;
334     }
335     return UTIL_TcharToUtf8OnHeap(str);
336 }
337 #endif /*NCBI_OS_MSWIN*/
338 
339 
s_ErrorCallback(const SSOCK_ErrInfo * info)340 static void s_ErrorCallback(const SSOCK_ErrInfo* info)
341 {
342     FSOCK_ErrHook hook;
343     void*         data;
344 
345     CORE_LOCK_READ;
346     hook = s_ErrHook;
347     data = s_ErrData;
348     CORE_UNLOCK;
349     if (hook)
350         hook(info, data);
351 }
352 
353 
SOCK_SetErrHookAPI(FSOCK_ErrHook hook,void * data)354 extern void SOCK_SetErrHookAPI(FSOCK_ErrHook hook, void* data)
355 {
356     CORE_LOCK_WRITE;
357     s_ErrData = data;
358     s_ErrHook = hook;
359     CORE_UNLOCK;
360 }
361 
362 
363 
364 /******************************************************************************
365  *  DATA LOGGING
366  */
367 
368 
s_CP(unsigned int host,unsigned short port,const char * path,char * buf,size_t bufsize)369 static const char* s_CP(unsigned int host, unsigned short port,
370                         const char* path, char* buf, size_t bufsize)
371 {
372     if (path[0])
373         return path;
374     if (!(host | port))
375         return "";
376     SOCK_HostPortToString(host, port, buf, bufsize);
377     return buf;
378 }
379 
380 
s_ID(const SOCK sock,char buf[MAXIDLEN])381 static const char* s_ID(const SOCK sock, char buf[MAXIDLEN])
382 {
383     const char* sname;
384     const char* cp;
385     char addr[40];
386     char fd[20];
387     size_t len;
388     int n;
389 
390     if (!sock)
391         return "";
392     switch (sock->type) {
393     case eTrigger:
394         cp = "";
395         sname = "TRIGGER";
396         break;
397     case eSocket:
398         cp = s_CP(sock->host, sock->port,
399 #ifdef NCBI_OS_UNIX
400                   sock->path,
401 #else
402                   "",
403 #endif /*NCBI_OS_UNIX*/
404                   addr, sizeof(addr));
405 #ifdef NCBI_OS_UNIX
406         if (sock->path[0])
407             sname = sock->sslctx ? "SUSOCK" : "USOCK";
408         else
409 #endif /*NCBI_OS_UNIX*/
410             sname = sock->sslctx ? "SSOCK"  : g_kNcbiSockNameAbbr;
411         break;
412     case eDatagram:
413         sname = "DSOCK";
414         addr[0] = '\0';
415         n = sock->myport ? sprintf(addr, "(:%hu)", sock->myport) : 0;
416         if (sock->host  ||  sock->port) {
417             SOCK_HostPortToString(sock->host, sock->port,
418                                   addr + n, sizeof(addr) - (size_t) n);
419         }
420         cp = addr;
421         break;
422     case eListening:
423 #ifdef NCBI_OS_UNIX
424         if (!sock->myport)
425             cp = ((LSOCK) sock)->path;
426         else
427 #endif /*NCBI_OS_UNIX*/
428         {
429             sprintf(addr, ":%hu", sock->myport);
430             cp = addr;
431         }
432         sname = "LSOCK";
433         break;
434     default:
435         cp = "";
436         sname = "?";
437         assert(0);
438         break;
439     }
440 
441     if (sock->sock != SOCK_INVALID)
442         sprintf(fd, "%u", (unsigned int) sock->sock);
443     else
444         strcpy(fd, "?");
445     len = cp  &&  *cp ? strlen(cp) : 0;
446     n = (int)(len > sizeof(addr) - 1 ? sizeof(addr) - 1 : len);
447     sprintf(buf, "%s#%u[%s]%s%s%.*s: ", sname, sock->id, fd,
448             &"@"[!n], (size_t) n < len ? "..." : "", n, cp + len - n);
449     return buf;
450 }
451 
452 
s_GetLocalPort(TSOCK_Handle fd)453 static unsigned short s_GetLocalPort(TSOCK_Handle fd)
454 {
455     struct sockaddr_in sin;
456     TSOCK_socklen_t sinlen = (TSOCK_socklen_t) sizeof(sin);
457     memset(&sin, 0, sizeof(sin));
458 #ifdef HAVE_SIN_LEN
459     sin.sin_len = sinlen;
460 #endif /*HAVE_SIN_LEN*/
461     if (getsockname(fd, (struct sockaddr*) &sin, &sinlen) == 0
462         &&  sin.sin_family == AF_INET) {
463         return ntohs(sin.sin_port);
464     }
465     return 0;
466 }
467 
468 
469 /* Put socket description to the message, then log the transferred data
470  */
s_DoLog(ELOG_Level level,const SOCK sock,EIO_Event event,const void * data,size_t size,const void * ptr)471 static void s_DoLog(ELOG_Level  level, const SOCK sock, EIO_Event   event,
472                     const void* data,  size_t     size, const void* ptr)
473 {
474     const struct sockaddr_in* sin;
475     const char* what, *strerr;
476     char _id[MAXIDLEN];
477     char head[128];
478     char tail[128];
479     int n;
480 
481     if (!CORE_GetLOG())
482         return;
483 
484     assert(sock  &&  (sock->type & eSocket));
485     switch (event) {
486     case eIO_Open:
487         if (sock->type != eDatagram) {
488             unsigned short port;
489             if (sock->side == eSOCK_Client) {
490                 what = ptr ? (const char*) ptr : "Connecting";
491                 strcpy(head, *what ? what : "Re-using");
492                 port = sock->myport;
493             } else if (!ptr) {
494                 strcpy(head, "Accepted");
495                 port = 0;
496             } else {
497                 strcpy(head, "Created");
498                 port = sock->myport;
499             }
500             if (!port) {
501 #ifdef NCBI_OS_UNIX
502                 if (!sock->path[0])
503 #endif /*NCBI_OS_UNIX*/
504                     port = s_GetLocalPort(sock->sock);
505             }
506             if (port) {
507                 sprintf(tail, " @:%hu", port);
508                 if (!sock->myport) {
509                     /* here: not LSOCK_Accept()'d network sockets only */
510                     assert(sock->side == eSOCK_Client  ||  ptr);
511                     sock->myport = port;  /*cache it*/
512                 }
513             } else
514                 *tail = '\0';
515         } else if (!(sin = (const struct sockaddr_in*) ptr)) {
516             strcpy(head, "Created");
517             *tail = '\0';
518         } else if (!data) {
519             strcpy(head, "Bound @");
520             sprintf(tail, "(:%hu)", ntohs(sin->sin_port));
521         } else if (sin->sin_family == AF_INET) {
522             strcpy(head, "Associated ");
523             SOCK_HostPortToString(sin->sin_addr.s_addr,
524                                   ntohs(sin->sin_port),
525                                   tail, sizeof(tail));
526         } else {
527             strcpy(head, "Disassociated");
528             *tail = '\0';
529         }
530         CORE_LOGF_X(112, level,
531                     ("%s%s%s", s_ID(sock, _id), head, tail));
532         break;
533 
534     case eIO_Read:
535     case eIO_Write:
536         strerr = 0;
537         what = (event == eIO_Read
538                 ? (sock->type == eDatagram  ||  size  ||
539                    (data  &&  !(strerr = s_StrError(sock, *((int*) data))))
540                    ? "Read"
541                    : data ? strerr : "EOF hit")
542                 : (sock->type == eDatagram  ||  size  ||
543                    !(strerr = s_StrError(sock, *((int*) data)))
544                    ? "Written"
545                    :        strerr));
546 
547         n = (int) strlen(what);
548         while (n  &&  isspace((unsigned char) what[n - 1]))
549             --n;
550         if (n > 1  &&  what[n - 1] == '.')
551             --n;
552         if (sock->type == eDatagram) {
553             sin = (const struct sockaddr_in*) ptr;
554             assert(sin  &&  sin->sin_family == AF_INET);
555             SOCK_HostPortToString(sin->sin_addr.s_addr,
556                                   ntohs(sin->sin_port),
557                                   head, sizeof(head));
558             sprintf(tail, ", msg# %" NCBI_BIGCOUNT_FORMAT_SPEC,
559                     event == eIO_Read ? sock->n_in : sock->n_out);
560         } else if (!ptr  ||  !*((char*) ptr)) {
561             sprintf(head, " at offset %" NCBI_BIGCOUNT_FORMAT_SPEC,
562                     event == eIO_Read ? sock->n_read : sock->n_written);
563             strcpy(tail, ptr ? " [OOB]" : "");
564         } else {
565             strncpy0(head, (const char*) ptr, sizeof(head));
566             *tail = '\0';
567         }
568 
569         CORE_DATAF_X(109, level, data, size,
570                      ("%s%.*s%s%s%s", s_ID(sock, _id), n, what,
571                       sock->type == eDatagram
572                       ? (event == eIO_Read ? " from " : " to ")
573                       : size  ||  !data ? "" : strerr
574                       ? (event == eIO_Read
575                          ? " while reading" : " while writing")
576                       : " 0 bytes",
577                       head, tail));
578 
579         UTIL_ReleaseBuffer(strerr);
580         break;
581 
582     case eIO_Close:
583         n = sprintf(head, "%" NCBI_BIGCOUNT_FORMAT_SPEC " byte%s",
584                     sock->n_written, &"s"[sock->n_written == 1]);
585         if (sock->type == eDatagram  ||
586             sock->n_out != sock->n_written) {
587             sprintf(head + n, "/%" NCBI_BIGCOUNT_FORMAT_SPEC " %s%s",
588                     sock->n_out,
589                     sock->type == eDatagram ? "msg" : "total byte",
590                     &"s"[sock->n_out == 1]);
591         }
592         n = sprintf(tail, "%" NCBI_BIGCOUNT_FORMAT_SPEC " byte%s",
593                     sock->n_read, &"s"[sock->n_read == 1]);
594         if (sock->type == eDatagram  ||
595             sock->n_in != sock->n_read) {
596             sprintf(tail + n, "/%" NCBI_BIGCOUNT_FORMAT_SPEC " %s%s",
597                     sock->n_in,
598                     sock->type == eDatagram ? "msg" : "total byte",
599                     &"s"[sock->n_in == 1]);
600         }
601         CORE_LOGF_X(113, level,
602                     ("%s%s (out: %s, in: %s)", s_ID(sock, _id),
603                      ptr ? (const char*) ptr :
604                      sock->keep ? "Leaving" : "Closing",
605                      head, tail));
606         break;
607 
608     default:
609         CORE_LOGF_X(1, eLOG_Error,
610                     ("%s[SOCK::DoLog] "
611                      " Invalid event #%u",
612                      s_ID(sock, _id), (unsigned int) event));
613         assert(0);
614         break;
615     }
616 }
617 
618 
619 
620 /******************************************************************************
621  *  STimeout <--> struct timeval CONVERSIONS
622  */
623 
624 
625 #ifdef __GNUC__
626 inline
627 #endif /*__GNUC__*/
s_tv2to(const struct timeval * tv,STimeout * to)628 static STimeout*       s_tv2to(const struct timeval* tv, STimeout* to)
629 {
630     assert(tv);
631 
632     /* NB: internally tv always kept normalized */
633     to->sec  = (unsigned int) tv->tv_sec;
634     to->usec = (unsigned int) tv->tv_usec;
635     return to;
636 }
637 
638 #ifdef __GNUC__
639 inline
640 #endif /*__GNUC__*/
s_to2tv(const STimeout * to,struct timeval * tv)641 static struct timeval* s_to2tv(const STimeout* to,       struct timeval* tv)
642 {
643     if (!to)
644         return 0;
645 
646     tv->tv_sec  = to->usec / 1000000 + to->sec;
647     tv->tv_usec = to->usec % 1000000;
648     return tv;
649 }
650 
651 
652 
653 /******************************************************************************
654  *  API INITIALIZATION, SHUTDOWN/CLEANUP, and UTILITY
655  */
656 
657 
658 #if defined(_DEBUG)  &&  !defined(NDEBUG)
659 #  if !defined(__GNUC__)  &&  !defined(offsetof)
660 #    define offsetof(T, F)  ((size_t)((char*) &(((T*) 0)->F) - (char*) 0))
661 #  endif
662 #endif /*_DEBUG && !NDEBUG*/
663 
664 #if defined(_DEBUG)  &&  !defined(NDEBUG)
665 
666 #  ifndef   SOCK_HAVE_SHOWDATALAYOUT
667 #    define SOCK_HAVE_SHOWDATALAYOUT  1
668 #  endif /* SOCK_HAVE_SHOWDATALAYOUT */
669 
670 #endif /*__GNUC__ && _DEBUG && !NDEBUG*/
671 
672 #ifdef SOCK_HAVE_SHOWDATALAYOUT
673 
674 #  define   extentof(T, F)  (sizeof(((T*) 0)->F))
675 
676 #  define   infof(T, F)     (unsigned int) offsetof(T, F), \
677                             (unsigned int) extentof(T, F)
678 
x_ShowDataLayout(void)679 static void x_ShowDataLayout(void)
680 {
681     static const char kLayoutFormat[] = {
682         "SOCK data layout:\n"
683         "    Sizeof(TRIGGER_struct) = %u\n"
684         "    Sizeof(LSOCK_struct) = %u\n"
685         "    Sizeof(SOCK_struct) = %u, offsets (sizes) follow\n"
686         "\tsock:      %3u (%u)\n"
687         "\tid:        %3u (%u)\n"
688         "\tisset:     %3u (%u)\n"
689         "\thost:      %3u (%u)\n"
690         "\tport:      %3u (%u)\n"
691         "\tmyport:    %3u (%u)\n"
692         "\terr:       %3u (%u)\n"
693         "\tbitfield:      (4)\n"
694 #  ifdef NCBI_OS_MSWIN
695         "\tevent:     %3u (%u)\n"
696 #  endif /*NCBI_OS_MSWIN*/
697         "\tsslctx:    %3u (%u)\n"
698         "\tr_tv:      %3u (%u)\n"
699         "\tw_tv:      %3u (%u)\n"
700         "\tc_tv:      %3u (%u)\n"
701         "\tr_to:      %3u (%u)\n"
702         "\tw_to:      %3u (%u)\n"
703         "\tc_to:      %3u (%u)\n"
704         "\tr_buf:     %3u (%u)\n"
705         "\tw_buf:     %3u (%u)\n"
706         "\tr_len:     %3u (%u)\n"
707         "\tw_len:     %3u (%u)\n"
708         "\tn_read:    %3u (%u)\n"
709         "\tn_written: %3u (%u)\n"
710         "\tn_in:      %3u (%u)\n"
711         "\tn_out:     %3u (%u)"
712 #  ifdef NCBI_OS_UNIX
713         "\n\tpath:      %3u (%u)"
714 #  endif /*NCBI_OS_UNIX*/
715     };
716 #  ifdef NCBI_OS_MSWIN
717 #    define SOCK_SHOWDATALAYOUT_PARAMS              \
718         infof(SOCK_struct,    sock),                \
719         infof(SOCK_struct,    id),                  \
720         infof(TRIGGER_struct, isset),               \
721         infof(SOCK_struct,    host),                \
722         infof(SOCK_struct,    port),                \
723         infof(SOCK_struct,    myport),              \
724         infof(SOCK_struct,    err),                 \
725         infof(SOCK_struct,    event),               \
726         infof(SOCK_struct,    sslctx),              \
727         infof(SOCK_struct,    r_tv),                \
728         infof(SOCK_struct,    w_tv),                \
729         infof(SOCK_struct,    c_tv),                \
730         infof(SOCK_struct,    r_to),                \
731         infof(SOCK_struct,    w_to),                \
732         infof(SOCK_struct,    c_to),                \
733         infof(SOCK_struct,    r_buf),               \
734         infof(SOCK_struct,    w_buf),               \
735         infof(SOCK_struct,    r_len),               \
736         infof(SOCK_struct,    w_len),               \
737         infof(SOCK_struct,    n_read),              \
738         infof(SOCK_struct,    n_written),           \
739         infof(SOCK_struct,    n_in),                \
740         infof(SOCK_struct,    n_out)
741 #  else
742 #    define SOCK_SHOWDATALAYOUT_PARAMS              \
743         infof(SOCK_struct,    sock),                \
744         infof(SOCK_struct,    id),                  \
745         infof(TRIGGER_struct, isset),               \
746         infof(SOCK_struct,    host),                \
747         infof(SOCK_struct,    port),                \
748         infof(SOCK_struct,    myport),              \
749         infof(SOCK_struct,    err),                 \
750         infof(SOCK_struct,    sslctx),              \
751         infof(SOCK_struct,    r_tv),                \
752         infof(SOCK_struct,    w_tv),                \
753         infof(SOCK_struct,    c_tv),                \
754         infof(SOCK_struct,    r_to),                \
755         infof(SOCK_struct,    w_to),                \
756         infof(SOCK_struct,    c_to),                \
757         infof(SOCK_struct,    r_buf),               \
758         infof(SOCK_struct,    w_buf),               \
759         infof(SOCK_struct,    r_len),               \
760         infof(SOCK_struct,    w_len),               \
761         infof(SOCK_struct,    n_read),              \
762         infof(SOCK_struct,    n_written),           \
763         infof(SOCK_struct,    n_in),                \
764         infof(SOCK_struct,    n_out),               \
765         infof(SOCK_struct,    path)
766 #  endif /*NCBI_OS_MSWIN*/
767     CORE_LOGF_X(2, eLOG_Trace,
768                 (kLayoutFormat,
769                  (unsigned int) sizeof(TRIGGER_struct),
770                  (unsigned int) sizeof(LSOCK_struct),
771                  (unsigned int) sizeof(SOCK_struct),
772                  SOCK_SHOWDATALAYOUT_PARAMS));
773 #  undef SOCK_SHOWDATALAYOUT_PARAMS
774 }
775 
776 #endif /*SOCK_HAVE_SHOWDATALAYOUT*/
777 
778 
s_Init(void)779 static EIO_Status s_Init(void)
780 {
781     CORE_TRACE("[SOCK::InitializeAPI]  Begin");
782 
783     CORE_LOCK_WRITE;
784 
785     if (s_Initialized) {
786         CORE_UNLOCK;
787         CORE_TRACE("[SOCK::InitializeAPI]  Noop");
788         return s_Initialized < 0 ? eIO_NotSupported : eIO_Success;
789     }
790 
791 #ifdef SOCK_HAVE_SHOWDATALAYOUT
792     if (s_Log == eOn)
793         x_ShowDataLayout();
794 #endif /*SOCK_HAVE_SHOWDATALAYOUT*/
795 
796 #if defined(_DEBUG)  &&  !defined(NDEBUG)
797     /* Layout / alignment sanity check */
798     assert(sizeof(TRIGGER_Handle)        == sizeof(TSOCK_Handle));
799     assert(offsetof(TRIGGER_struct, err) == offsetof(SOCK_struct,  err));
800     assert(offsetof(TRIGGER_struct, err) == offsetof(LSOCK_struct, err));
801     assert(offsetof(SOCK_struct, sslctx) == offsetof(LSOCK_struct, context));
802 #  ifdef NCBI_OS_MSWIN
803     assert(offsetof(LSOCK_struct, event) == offsetof(SOCK_struct,  event));
804     assert(WSA_INVALID_EVENT == 0);
805 #  endif /*NCBI_OS_MSWIN*/
806 #endif /*_DEBUG && !NDEBUG*/
807 
808 #if   defined(NCBI_OS_MSWIN)
809     {{
810         WSADATA wsadata;
811         int error = WSAStartup(MAKEWORD(1,1), &wsadata);
812         if (error) {
813             const char* strerr;
814 
815             CORE_UNLOCK;
816             strerr = SOCK_STRERROR(error);
817             CORE_LOG_ERRNO_EXX(3, eLOG_Error,
818                                error, strerr ? strerr : "",
819                                "[SOCK::InitializeAPI] "
820                                " Failed WSAStartup()");
821             UTIL_ReleaseBuffer(strerr);
822             return eIO_NotSupported;
823         }
824     }}
825 #elif defined(NCBI_OS_UNIX)
826     if (!s_AllowSigPipe) {
827         struct sigaction sa;
828         if (sigaction(SIGPIPE, 0, &sa) != 0  ||  sa.sa_handler == SIG_DFL) {
829             memset(&sa, 0, sizeof(sa));
830             sa.sa_handler = SIG_IGN;
831             sigaction(SIGPIPE, &sa, 0);
832         }
833     }
834 #endif /*NCBI_OS*/
835 
836 #ifndef NCBI_OS_MSWIN
837     {{
838         static void* /*bool*/ s_AtExitSet = 0/*false*/;
839         if (CORE_Once(&s_AtExitSet))
840             atexit((void (*)(void)) SOCK_ShutdownAPI);
841     }}
842 #endif /*NCBI_OS_MSWIN*/
843 
844     s_Initialized = 1/*inited*/;
845 
846     CORE_UNLOCK;
847     CORE_TRACE("[SOCK::InitializeAPI]  End");
848     return eIO_Success;
849 }
850 
851 
852 #ifdef __cplusplus
853 extern "C" {
854 #endif /*__cplusplus*/
855 static EIO_Status s_Recv(SOCK,       void*, size_t, size_t*, int);
856 static EIO_Status s_Send(SOCK, const void*, size_t, size_t*, int);
857 #ifdef __cplusplus
858 }
859 #endif /*__cplusplus*/
860 
861 
s_InitAPI_(int secure)862 static EIO_Status s_InitAPI_(int secure)
863 {
864     static const struct SOCKSSL_struct kNoSSL = { "", 0 };
865     EIO_Status status;
866 
867     if (!s_Initialized  &&  (status = s_Init()) != eIO_Success)
868         return status;
869 
870     assert(s_Initialized);
871 
872     if (s_Initialized < 0)
873         return eIO_NotSupported;
874 
875     if (!secure)
876         return eIO_Success;
877 
878     if (s_SSL)
879         return s_SSL == &kNoSSL ? eIO_NotSupported : eIO_Success;
880 
881     if (s_SSLSetup) {
882         const char* what = 0;
883         CORE_LOCK_WRITE;
884         if (!s_SSL) {
885             SOCKSSL ssl;
886             if (!s_SSLSetup  ||  !(ssl = s_SSLSetup())) {
887                 what   = (const char*)(-1L);
888                 s_SSL  = &kNoSSL;
889                 status = eIO_NotSupported;
890             } else {
891                 what   = ssl->Name;
892                 s_SSL  = ((status = ssl->Init(s_Recv, s_Send)) == eIO_Success
893                           ? ssl : &kNoSSL);
894             }
895         } else
896             status = s_SSL == &kNoSSL ? eIO_NotSupported : eIO_Success;
897         CORE_UNLOCK;
898         if (status != eIO_Success  &&  what) {
899             const char* provider;
900             char buf[40];
901             if (what == (const char*)(-1L)) {
902                 sprintf(buf, "%p()", s_SSLSetup);
903                 provider = buf;
904             } else
905                 provider = *what ? what : "???";
906             CORE_LOGF(eLOG_Critical,
907                       ("Failed to %s SSL provider %s: %s",
908                        what == (const char*)(-1L) ? "setup" : "initialize",
909                        provider, IO_StatusStr(status)));
910         }
911     } else {
912         static void* /*bool*/ s_Once = 0/*false*/;
913         if (CORE_Once(&s_Once)) {
914             CORE_LOG(eLOG_Critical, "Secure Socket Layer (SSL) has not"
915                      " been properly initialized in the NCBI Toolkit. "
916                      " Have you forgotten to call SOCK_SetupSSL[Ex]()?");
917         }
918         status = eIO_NotSupported;
919     }
920 
921     return status;
922 }
923 
924 
925 #ifdef __GNUC__
926 inline
927 #endif /*__GNUC__*/
s_InitAPI(int secure)928 static EIO_Status s_InitAPI(int secure)
929 {
930     EIO_Status status = s_InitAPI_(secure);
931     if (s_ErrHook  &&  status != eIO_Success) {
932         SSOCK_ErrInfo info;
933         memset(&info, 0, sizeof(info));
934         info.type = eSOCK_ErrInit;
935         info.status = status;
936         s_ErrorCallback(&info);
937     }
938     return status;
939 }
940 
941 
SOCK_InitializeAPI(void)942 extern EIO_Status SOCK_InitializeAPI(void)
943 {
944     EIO_Status status = s_Init();
945     if (s_ErrHook  &&  status != eIO_Success) {
946         SSOCK_ErrInfo info;
947         memset(&info, 0, sizeof(info));
948         info.type = eSOCK_ErrInit;
949         info.status = status;
950         s_ErrorCallback(&info);
951     }
952     return status;
953 }
954 
955 
956 /* Must be called under a lock */
957 #ifdef __GNUC__
958 inline
959 #endif /*__GNUC__*/
x_ShutdownSSL(void)960 static void x_ShutdownSSL(void)
961 {
962     if (s_Initialized > 0) {
963         FSSLExit sslexit = s_SSLSetup  &&  s_SSL ? s_SSL->Exit : 0;
964         s_SSLSetup = 0;
965         s_SSL      = 0;
966         if (sslexit)
967             sslexit();
968     }
969 }
970 
971 
SOCK_ShutdownAPI(void)972 extern EIO_Status SOCK_ShutdownAPI(void)
973 {
974     if (s_Initialized < 0)
975         return eIO_Success;
976 
977     CORE_TRACE("[SOCK::ShutdownAPI]  Begin");
978 
979     CORE_LOCK_WRITE;
980 
981     if (s_Initialized <= 0) {
982         CORE_UNLOCK;
983         return eIO_Success;
984     }
985     s_Initialized = -1/*deinited*/;
986 
987     x_ShutdownSSL();
988 
989 #ifdef NCBI_OS_MSWIN
990     {{
991         int error = WSACleanup() ? SOCK_ERRNO : 0;
992         CORE_UNLOCK;
993         if (error) {
994             const char* strerr = SOCK_STRERROR(error);
995             CORE_LOG_ERRNO_EXX(4, eLOG_Warning,
996                                error, strerr ? strerr : "",
997                                "[SOCK::ShutdownAPI] "
998                                " Failed WSACleanup()");
999             UTIL_ReleaseBuffer(strerr);
1000             return eIO_NotSupported;
1001         }
1002     }}
1003 #else
1004     CORE_UNLOCK;
1005 #endif /*NCBI_OS_MSWIN*/
1006 
1007     CORE_TRACE("[SOCK::ShutdownAPI]  End");
1008     return eIO_Success;
1009 }
1010 
1011 
SOCK_AllowSigPipeAPI(void)1012 extern void SOCK_AllowSigPipeAPI(void)
1013 {
1014 #ifdef NCBI_OS_UNIX
1015     s_AllowSigPipe = 1/*true - API will not mask SIGPIPE out at init*/;
1016 #endif /*NCBI_OS_UNIX*/
1017     return;
1018 }
1019 
1020 
SOCK_OSHandleSize(void)1021 extern size_t SOCK_OSHandleSize(void)
1022 {
1023     return sizeof(TSOCK_Handle);
1024 }
1025 
1026 
SOCK_SetSelectInternalRestartTimeout(const STimeout * t)1027 extern const STimeout* SOCK_SetSelectInternalRestartTimeout(const STimeout* t)
1028 {
1029     static struct timeval s_New;
1030     static STimeout       s_Old;
1031     const  STimeout*      retval;
1032     retval          = s_SelectTimeout ? s_tv2to(s_SelectTimeout, &s_Old) : 0;
1033     s_SelectTimeout =                   s_to2tv(t,               &s_New);
1034     return retval;
1035 }
1036 
1037 
SOCK_SetIOWaitSysAPI(ESOCK_IOWaitSysAPI api)1038 extern ESOCK_IOWaitSysAPI SOCK_SetIOWaitSysAPI(ESOCK_IOWaitSysAPI api)
1039 {
1040     ESOCK_IOWaitSysAPI retval = s_IOWaitSysAPI;
1041 #if !defined(NCBI_OS_UNIX)  ||  !defined(HAVE_POLL_H)
1042     if (api == eSOCK_IOWaitSysAPIPoll) {
1043         CORE_LOG_X(149, eLOG_Critical, "[SOCK::SetIOWaitSysAPI] "
1044                    " Poll API requested but not supported on this platform");
1045     } else
1046 #endif /*!NCBI_OS_UNIX || !HAVE_POLL_H*/
1047         s_IOWaitSysAPI = api;
1048     return retval;
1049 }
1050 
1051 
1052 
1053 /******************************************************************************
1054  *  gethost[by]...() WRAPPERS
1055  */
1056 
1057 
s_gethostname(char * name,size_t namesize,ESwitch log)1058 static int s_gethostname(char* name, size_t namesize, ESwitch log)
1059 {
1060     int/*bool*/ failed;
1061 
1062     CORE_TRACE("[SOCK::gethostname]");
1063 
1064     name[0] = name[namesize - 1] = '\0';
1065     if (sys_gethostname(name, namesize) != 0) {
1066         if (log) {
1067             int error = SOCK_ERRNO;
1068             const char* strerr = SOCK_STRERROR(error);
1069             CORE_LOG_ERRNO_EXX(103, eLOG_Error,
1070                                error, strerr ? strerr : "",
1071                                "[SOCK_gethostname] "
1072                                " Failed gethostname()");
1073             UTIL_ReleaseBuffer(strerr);
1074         }
1075         failed = 1/*true*/;
1076     } else if (name[namesize - 1]) {
1077         if (log) {
1078             CORE_LOGF_X(104, eLOG_Error,
1079                         ("[SOCK_gethostname] "
1080                          " Buffer too small (%lu)", (unsigned long) namesize));
1081         }
1082         failed = 1/*true*/;
1083     } else
1084         failed = 0/*false*/;
1085 
1086     CORE_TRACEF(("[SOCK::gethostname] "
1087                  " \"%.*s\"%s", (int) namesize, name,
1088                  failed ? " (failed)" : ""));
1089     if (failed)
1090         *name = '\0';
1091     return *name ? 0/*success*/ : -1/*failure*/;
1092 }
1093 
1094 
1095 #ifdef __GNUC__
1096 inline
1097 #endif /*__GNUC__*/
x_IsAPIPA(unsigned int addr)1098 static int/*bool*/ x_IsAPIPA(unsigned int addr)
1099 {
1100     return !((addr & 0xFFFF0000) ^ 0xA9FE0000); /* 169.254/16 per IANA */
1101 }
1102 
1103 
x_ChooseIP(char ** addrs)1104 static const char* x_ChooseIP(char** addrs)
1105 {
1106     int n;
1107     for (n = 0;  addrs[n];  ++n) {
1108         unsigned int ip;
1109         memcpy(&ip, addrs[n], sizeof(ip));
1110         if (!SOCK_IsLoopbackAddress(ip)  &&  !x_IsAPIPA(ntohl(ip)))
1111             return addrs[n];
1112     }
1113     return addrs[0];
1114 }
1115 
1116 
s_gethostbyname_(const char * hostname,int not_ip,int self,ESwitch log)1117 static unsigned int s_gethostbyname_(const char* hostname,
1118                                      int/*bool*/ not_ip,
1119                                      int/*bool*/ self,
1120                                      ESwitch     log)
1121 {
1122     char buf[CONN_HOST_LEN + 1];
1123     unsigned int host;
1124 
1125     assert(!hostname  ||  *hostname);
1126     if (!hostname) {
1127         if (s_gethostname(buf, sizeof(buf), log) != 0)
1128             return 0;
1129 #if 0/*def NCBI_OS_DARWIN*/
1130         {{
1131             char* p;
1132             if ((p = strchr(buf, '.')) != 0)
1133                 *p = '\0';
1134         }}
1135 #endif /*NCBI_OS_DARWIN*/
1136         not_ip = 1/*true*/;
1137         hostname = buf;
1138         assert(*buf);
1139     }
1140 
1141     CORE_TRACEF(("[SOCK::gethostbyname]  \"%s\"", hostname));
1142 
1143 #ifdef NCBI_OS_DARWIN
1144     if (strspn(hostname, ".0123456789") == strlen(hostname)) {
1145         /* Darwin's inet_addr() does not care for integer overflows :-/ */
1146         if (!SOCK_isip(hostname)) {
1147             host = 0;
1148             goto out;
1149         }
1150     }
1151 #endif /*NCBI_OS_DARWIN*/
1152 
1153     if (not_ip  ||  (host = inet_addr(hostname)) == htonl(INADDR_NONE)) {
1154         int error;
1155 #if defined(HAVE_GETADDRINFO)  &&  !defined(__GLIBC__)
1156         struct addrinfo hints, *out = 0;
1157         memset(&hints, 0, sizeof(hints));
1158         hints.ai_family = AF_INET; /* currently, we only handle IPv4 */
1159         if ((error = getaddrinfo(hostname, 0, &hints, &out)) == 0  &&  out) {
1160             if (self  &&  out->ai_next) {
1161                 struct addrinfo* tmp = out;
1162                 char* addrs[128];
1163                 size_t n;
1164                 for (n = 0;  n < sizeof(addrs) / sizeof(addrs[0]) - 1;  ++n) {
1165                     struct sockaddr_in* sin
1166                         = (struct sockaddr_in*) tmp->ai_addr;
1167                     assert(sin->sin_family == AF_INET);
1168                     addrs[n] = (char*) &sin->sin_addr;
1169                     if (!(tmp = tmp->ai_next))
1170                         break;
1171                 }
1172                 addrs[n] = 0;
1173                 memcpy(&host, x_ChooseIP(addrs), sizeof(host));
1174             } else {
1175                 struct sockaddr_in* sin = (struct sockaddr_in*) out->ai_addr;
1176                 assert(sin->sin_family == AF_INET);
1177                 host = sin->sin_addr.s_addr;
1178             }
1179         } else {
1180             if (log) {
1181                 const char* strerr;
1182                 if (error == EAI_SYSTEM)
1183                     error  = SOCK_ERRNO;
1184                 else if (error)
1185                     error += EAI_BASE;
1186                 else
1187                     error  = EFAULT;
1188                 strerr = SOCK_STRERROR(error);
1189                 CORE_LOGF_ERRNO_EXX(105, eLOG_Warning,
1190                                     error, strerr ? strerr : "",
1191                                     ("[SOCK_gethostbyname] "
1192                                      " Failed getaddrinfo(\"%.*s\")",
1193                                      CONN_HOST_LEN, hostname));
1194                 UTIL_ReleaseBuffer(strerr);
1195             }
1196             host = 0;
1197         }
1198         if (out)
1199             freeaddrinfo(out);
1200 #else /* use some variant of gethostbyname */
1201         struct hostent* he;
1202 #  ifdef HAVE_GETHOSTBYNAME_R
1203         static const char suffix[] = "_r";
1204         struct hostent x_he;
1205         char x_buf[1024];
1206 
1207         error = 0;
1208 #    if   HAVE_GETHOSTBYNAME_R == 5
1209         he = gethostbyname_r(hostname, &x_he, x_buf, sizeof(x_buf), &error);
1210 #    elif HAVE_GETHOSTBYNAME_R == 6
1211         if (gethostbyname_r(hostname, &x_he, x_buf, sizeof(x_buf),
1212                             &he, &error) != 0) {
1213             /*NB: retval == errno on error*/
1214             assert(he == 0);
1215             he = 0;
1216         }
1217 #    else
1218 #      error "Unknown HAVE_GETHOSTBYNAME_R value"
1219 #    endif /*HAVE_GETHOSTNBYNAME_R == N*/
1220         if (!he) {
1221             if (!error)
1222                 error = SOCK_ERRNO;
1223             else
1224                 error += DNS_BASE;
1225         }
1226 #  else
1227         static const char suffix[] = "";
1228 
1229 #    ifndef SOCK_GHBX_MT_SAFE
1230         CORE_LOCK_WRITE;
1231 #    endif /*!SOCK_GHBX_MT_SAFE*/
1232 
1233         he = gethostbyname(hostname);
1234         error = he ? 0 : h_errno + DNS_BASE;
1235 #  endif /*HAVE_GETHOSTBYNAME_R*/
1236 
1237         if (!he || he->h_addrtype != AF_INET || he->h_length != sizeof(host)) {
1238             if (he)
1239                 error = EINVAL;
1240             host = 0;
1241         } else
1242             memcpy(&host, self ? x_ChooseIP(he->h_addr_list) : he->h_addr, sizeof(host));
1243 
1244 #  ifndef HAVE_GETHOSTBYNAME_R
1245 #    ifndef SOCK_GHBX_MT_SAFE
1246         CORE_UNLOCK;
1247 #    endif /*!SOCK_GHBX_MT_SAFE*/
1248 #  endif /*HAVE_GETHOSTBYNAME_R*/
1249 
1250         if (!host) {
1251 #  ifdef NETDB_INTERNAL
1252             if (error == NETDB_INTERNAL + DNS_BASE)
1253                 error  = SOCK_ERRNO;
1254 #  endif /*NETDB_INTERNAL*/
1255             if (error == ERANGE)
1256                 log = eOn;
1257             if (log) {
1258                 const char* strerr = SOCK_STRERROR(error);
1259                 CORE_LOGF_ERRNO_EXX(106, eLOG_Warning,
1260                                     error, strerr ? strerr : "",
1261                                     ("[SOCK_gethostbyname] "
1262                                      " Failed gethostbyname%s(\"%.*s\")",
1263                                      suffix, CONN_HOST_LEN, hostname));
1264                 UTIL_ReleaseBuffer(strerr);
1265             }
1266         }
1267 #endif /*HAVE_GETADDRINFO && !__GLIBC__*/
1268     }
1269 
1270 #ifdef NCBI_OS_DARWIN
1271  out:
1272 #endif /*NCBI_OS_DARWIN*/
1273 #if defined(_DEBUG)  &&  !defined(NDEBUG)
1274     if (!SOCK_isipEx(hostname, 1/*full-quad*/)  ||  !host) {
1275         char addr[40];
1276         CORE_TRACEF(("[SOCK::gethostbyname]  \"%s\" @ %s", hostname,
1277                      SOCK_ntoa(host, addr, sizeof(addr)) == 0
1278                      ? addr : sprintf(addr, "0x%08X",
1279                                       (unsigned int) ntohl(host))
1280                      ? addr : "(unknown)"));
1281     }
1282 #endif /*_DEBUG && !NDEBUG*/
1283     return host;
1284 }
1285 
1286 
1287 /* a non-standard helper */
s_getlocalhostaddress(ESwitch reget,ESwitch log)1288 static unsigned int s_getlocalhostaddress(ESwitch reget, ESwitch log)
1289 {
1290     static void* /*bool*/ s_Once = 0/*false*/;
1291     /* cached IP address of the local host */
1292     static unsigned int s_LocalHostAddress = 0;
1293     if (reget == eOn  ||  (!s_LocalHostAddress  &&  reget != eOff))
1294         s_LocalHostAddress = s_gethostbyname_(0, 0, 1/*self*/, log);
1295     if (s_LocalHostAddress)
1296         return s_LocalHostAddress;
1297     if (reget != eOff  &&  CORE_Once(&s_Once)) {
1298         CORE_LOGF_X(9, reget == eDefault ? eLOG_Warning : eLOG_Error,
1299                     ("[SOCK::GetLocalHostAddress] "
1300                      " Cannot obtain local host address%s",
1301                      reget == eDefault ? ", using loopback instead" : ""));
1302     }
1303     return reget == eDefault ? SOCK_LOOPBACK : 0;
1304 }
1305 
1306 
s_gethostbyname(const char * hostname,int not_ip,ESwitch log)1307 static unsigned int s_gethostbyname(const char* hostname,
1308                                     int/*bool*/ not_ip,
1309                                     ESwitch     log)
1310 {
1311     static void* /*bool*/ s_Once = 0/*false*/;
1312     unsigned int retval;
1313 
1314     if (hostname &&  !*hostname)
1315         hostname = 0;
1316     if (!(retval = s_gethostbyname_(hostname, not_ip, 0, log))) {
1317         if (s_ErrHook) {
1318             SSOCK_ErrInfo info;
1319             memset(&info, 0, sizeof(info));
1320             info.type = eSOCK_ErrDns;
1321             info.host = hostname;
1322             s_ErrorCallback(&info);
1323         }
1324     } else if (!s_Once  &&  !hostname
1325                &&  SOCK_IsLoopbackAddress(retval)  &&  CORE_Once(&s_Once)) {
1326         char addr[40 + 1];
1327         *addr = " "[SOCK_ntoa(retval, addr + 1, sizeof(addr) - 1) ? 1 : 0];
1328         CORE_LOGF_X(155, eLOG_Warning,
1329                     ("[SOCK::gethostbyname] "
1330                      " Got loopback address%s for local host name", addr));
1331     }
1332 
1333     return retval;
1334 }
1335 
1336 
s_gethostbyaddr_(unsigned int host,char * name,size_t namesize,ESwitch log)1337 static char* s_gethostbyaddr_(unsigned int host, char* name,
1338                               size_t namesize, ESwitch log)
1339 {
1340     char addr[40];
1341 
1342     if (!host)
1343         host = s_getlocalhostaddress(eDefault, log);
1344 
1345     CORE_TRACEF(("[SOCK::gethostbyaddr]  %s",
1346                  SOCK_ntoa(host, addr, sizeof(addr)) == 0
1347                  ? addr : sprintf(addr, "0x%08X", (unsigned int) ntohl(host))
1348                  ? addr : "(unknown)"));
1349 
1350     if (host) {
1351         int error;
1352 #if defined(HAVE_GETNAMEINFO)  &&  !defined(__GLIBC__)
1353         struct sockaddr_in sin;
1354 
1355         memset(&sin, 0, sizeof(sin));
1356 #  ifdef HAVE_SIN_LEN
1357         sin.sin_len = (TSOCK_socklen_t) sizeof(sin);
1358 #  endif /*HAVE_SIN_LEN*/
1359         sin.sin_family      = AF_INET; /* we only handle IPv4 currently */
1360         sin.sin_addr.s_addr = host;
1361         if ((error = getnameinfo((struct sockaddr*) &sin, sizeof(sin),
1362                                  name, namesize, 0, 0, 0)) != 0  ||  !*name) {
1363             if (SOCK_ntoa(host, name, namesize) != 0) {
1364                 if (error == EAI_SYSTEM)
1365                     error  = SOCK_ERRNO;
1366                 else if (error)
1367                     error += EAI_BASE;
1368                 else {
1369 #  ifdef ENOSPC
1370                     error  = ENOSPC;
1371 #  else
1372                     error  = ERANGE;
1373 #  endif /*ENOSPC*/
1374                     log = eOn;
1375                 }
1376                 name[0] = '\0';
1377                 name = 0;
1378             }
1379             if (!name  &&  log) {
1380                 const char* strerr = SOCK_STRERROR(error);
1381                 if (SOCK_ntoa(host, addr, sizeof(addr)) != 0)
1382                     sprintf(addr, "0x%08X", (unsigned int) ntohl(host));
1383                 CORE_LOGF_ERRNO_EXX(107, eLOG_Warning,
1384                                     error, strerr ? strerr : "",
1385                                     ("[SOCK_gethostbyaddr] "
1386                                      " Failed getnameinfo(%s)",
1387                                      addr));
1388                 UTIL_ReleaseBuffer(strerr);
1389             }
1390         }
1391 #else /* use some variant of gethostbyaddr */
1392         struct hostent* he;
1393 #  ifdef HAVE_GETHOSTBYADDR_R
1394         static const char suffix[] = "_r";
1395         struct hostent x_he;
1396         char x_buf[1024];
1397 
1398         error = 0;
1399 #    if   HAVE_GETHOSTBYADDR_R == 7
1400         he = gethostbyaddr_r((char*) &host, sizeof(host), AF_INET, &x_he,
1401                              x_buf, sizeof(x_buf), &error);
1402 #    elif HAVE_GETHOSTBYADDR_R == 8
1403         if (gethostbyaddr_r((char*) &host, sizeof(host), AF_INET, &x_he,
1404                             x_buf, sizeof(x_buf), &he, &error) != 0) {
1405             /*NB: retval == errno on error*/
1406             assert(he == 0);
1407             he = 0;
1408         }
1409 #    else
1410 #      error "Unknown HAVE_GETHOSTBYADDR_R value"
1411 #    endif /*HAVE_GETHOSTBYADDR_R == N*/
1412         if (!he) {
1413             if (!error)
1414                 error = SOCK_ERRNO;
1415             else
1416                 error += DNS_BASE;
1417         }
1418 #  else /*HAVE_GETHOSTBYADDR_R*/
1419         static const char suffix[] = "";
1420 
1421 #    ifndef SOCK_GHBX_MT_SAFE
1422         CORE_LOCK_WRITE;
1423 #    endif /*!SOCK_GHBX_MT_SAFE*/
1424 
1425         he = gethostbyaddr((char*) &host, sizeof(host), AF_INET);
1426         error = he ? 0 : h_errno + DNS_BASE;
1427 #  endif /*HAVE_GETHOSTBYADDR_R*/
1428 
1429         if (!he  ||  strlen(he->h_name) >= namesize) {
1430             if (he  ||  SOCK_ntoa(host, name, namesize) != 0) {
1431 #ifdef ENOSPC
1432                 error = ENOSPC;
1433 #else
1434                 error = ERANGE;
1435 #endif /*ENOSPC*/
1436                 log = eOn;
1437                 name[0] = '\0';
1438                 name = 0;
1439             }
1440         } else
1441             strcpy(name, he->h_name);
1442 
1443 #  ifndef HAVE_GETHOSTBYADDR_R
1444 #    ifndef SOCK_GHBX_MT_SAFE
1445         CORE_UNLOCK;
1446 #    endif /*!SOCK_GHBX_MT_SAFE*/
1447 #  endif /*HAVE_GETHOSTBYADDR_R*/
1448 
1449         if (!name) {
1450 #  ifdef NETDB_INTERNAL
1451             if (error == NETDB_INTERNAL + DNS_BASE)
1452                 error  = SOCK_ERRNO;
1453 #  endif /*NETDB_INTERNAL*/
1454             if (error == ERANGE)
1455                 log = eOn;
1456             if (log) {
1457                 const char* strerr = SOCK_STRERROR(error);
1458                 if (SOCK_ntoa(host, addr, sizeof(addr)) != 0)
1459                     sprintf(addr, "0x%08X", (unsigned int) ntohl(host));
1460                 CORE_LOGF_ERRNO_EXX(108, eLOG_Warning,
1461                                     error, strerr ? strerr : "",
1462                                     ("[SOCK_gethostbyaddr] "
1463                                      " Failed gethostbyaddr%s(%s)",
1464                                      suffix, addr));
1465                 UTIL_ReleaseBuffer(strerr);
1466             }
1467         }
1468 #endif /*HAVE_GETNAMEINFO && !__GLIBC__*/
1469     } else {
1470         name[0] = '\0';
1471         name = 0;
1472     }
1473 
1474     CORE_TRACEF(("[SOCK::gethostbyaddr]  %s @ %s%s%s",
1475                  SOCK_ntoa(host, addr, sizeof(addr)) == 0
1476                  ? addr : sprintf(addr, "0x%08X", (unsigned int) ntohl(host))
1477                  ? addr : "(unknown)",
1478                  &"\""[!name], name ? name : "(unknown)", &"\""[!name]));
1479     return name;
1480 }
1481 
1482 
s_gethostbyaddr(unsigned int host,char * name,size_t namesize,ESwitch log)1483 static const char* s_gethostbyaddr(unsigned int host, char* name,
1484                                    size_t namesize, ESwitch log)
1485 {
1486     static void* /*bool*/ s_Once = 0/*false*/;
1487     const char* retval = s_gethostbyaddr_(host, name, namesize, log);
1488     if (!s_Once  &&  retval
1489         &&  ((SOCK_IsLoopbackAddress(host)
1490               &&  strncasecmp(retval, "localhost", 9) != 0)  ||
1491              (!host
1492               &&  strncasecmp(retval, "localhost", 9) == 0))
1493         &&  CORE_Once(&s_Once)) {
1494         CORE_LOGF_X(10, eLOG_Warning,
1495                     ("[SOCK::gethostbyaddr] "
1496                      " Got \"%.*s\" for %s address", CONN_HOST_LEN,
1497                      retval, host ? "loopback" : "local host"));
1498     }
1499     return retval;
1500 }
1501 
1502 
1503 
1504 /******************************************************************************
1505  *  STATIC SOCKET HELPERS
1506  */
1507 
1508 
1509 /* Switch the specified socket I/O between blocking and non-blocking mode
1510  */
1511 #ifdef __GNUC__
1512 inline
1513 #endif /*__GNUC__*/
s_SetNonblock(TSOCK_Handle sock,int nonblock)1514 static int/*bool*/ s_SetNonblock(TSOCK_Handle sock, int/*bool*/ nonblock)
1515 {
1516 #if   defined(NCBI_OS_MSWIN)
1517     unsigned long arg = nonblock ? 1 : 0;
1518     return ioctlsocket(sock, FIONBIO, &arg) == 0;
1519 #elif defined(NCBI_OS_UNIX)
1520     int flags = fcntl(sock, F_GETFL, 0);
1521     if (flags == -1)
1522         return 0/*false*/;
1523     if (!nonblock == !(flags & O_NONBLOCK))
1524         return 1/*true*/;
1525     return fcntl(sock, F_SETFL, nonblock
1526                  ? flags |        O_NONBLOCK
1527                  : flags & (int) ~O_NONBLOCK) == 0;
1528 #else
1529 #  error "Unsupported platform"
1530 #endif /*NCBI_OS*/
1531 }
1532 
1533 
1534 /* Set close-on-exec flag
1535  */
1536 #ifdef __GNUC__
1537 inline
1538 #endif /*__GNUC__*/
s_SetCloexec(TSOCK_Handle x_sock,int cloexec)1539 static int/*bool*/ s_SetCloexec(TSOCK_Handle x_sock, int/*bool*/ cloexec)
1540 {
1541 #if   defined(NCBI_OS_UNIX)
1542     int flags = fcntl(x_sock, F_GETFD, 0);
1543     if (flags == -1)
1544         return 0/*false*/;
1545     if (!cloexec == !(flags & FD_CLOEXEC))
1546         return 1/*true*/;
1547     return fcntl(x_sock, F_SETFD, cloexec
1548                  ? flags |        FD_CLOEXEC
1549                  : flags & (int) ~FD_CLOEXEC) == 0;
1550 #elif defined(NCBI_OS_MSWIN)
1551     return SetHandleInformation((HANDLE)x_sock, HANDLE_FLAG_INHERIT, !cloexec);
1552 #else
1553 #  error "Unsupported platform"
1554 #endif /*NCBI_OS*/
1555 }
1556 
1557 
1558 /*ARGSUSED*/
1559 #ifdef __GNUC__
1560 inline
1561 #endif /*__GNUC__*/
s_SetReuseAddress(TSOCK_Handle x_sock,int on_off)1562 static int/*bool*/ s_SetReuseAddress(TSOCK_Handle x_sock, int/*bool*/ on_off)
1563 {
1564 #if defined(NCBI_OS_UNIX)  ||  defined(NCBI_OS_MSWIN)
1565 #  ifdef NCBI_OS_MSWIN
1566     BOOL reuse_addr = on_off ? TRUE : FALSE;
1567 #  else
1568     int  reuse_addr = on_off ? 1    : 0;
1569 #  endif /*NCBI_OS_MSWIN*/
1570     return setsockopt(x_sock, SOL_SOCKET, SO_REUSEADDR,
1571                       (char*) &reuse_addr, sizeof(reuse_addr)) == 0;
1572 #else
1573     /* setsockopt() is not implemented for MAC (in MIT socket emulation lib) */
1574     return 1;
1575 #endif /*NCBI_OS_UNIX || NCBI_OS_MSWIN*/
1576 }
1577 
1578 
1579 #ifdef SO_KEEPALIVE
1580 #ifdef __GNUC__
1581 inline
1582 #endif /*__GNUC__*/
s_SetKeepAlive(TSOCK_Handle x_sock,int on_off)1583 static int/*bool*/ s_SetKeepAlive(TSOCK_Handle x_sock, int/*bool*/ on_off)
1584 {
1585 #  ifdef NCBI_OS_MSWIN
1586     BOOL oobinline = on_off ? TRUE      : FALSE;
1587 #  else
1588     int  oobinline = on_off ? 1/*true*/ : 0/*false*/;
1589 #  endif /*NCBI_OS_MSWIN*/
1590     return setsockopt(x_sock, SOL_SOCKET, SO_KEEPALIVE,
1591                       (char*) &oobinline, sizeof(oobinline)) == 0;
1592 }
1593 #endif /*SO_KEEPALIVE*/
1594 
1595 
1596 #ifdef SO_OOBINLINE
1597 #ifdef __GNUC__
1598 inline
1599 #endif /*__GNUC__*/
s_SetOobInline(TSOCK_Handle x_sock,int on_off)1600 static int/*bool*/ s_SetOobInline(TSOCK_Handle x_sock, int/*bool*/ on_off)
1601 {
1602 #  ifdef NCBI_OS_MSWIN
1603     BOOL oobinline = on_off ? TRUE      : FALSE;
1604 #  else
1605     int  oobinline = on_off ? 1/*true*/ : 0/*false*/;
1606 #  endif /*NCBI_OS_MSWIN*/
1607     return setsockopt(x_sock, SOL_SOCKET, SO_OOBINLINE,
1608                       (char*) &oobinline, sizeof(oobinline)) == 0;
1609 }
1610 #endif /*SO_OOBINLINE*/
1611 
1612 
1613 #if !defined(NCBI_OS_MSWIN)  &&  defined(FD_SETSIZE)
x_TryLowerSockFileno(SOCK sock)1614 static int/*bool*/ x_TryLowerSockFileno(SOCK sock)
1615 {
1616 #  ifdef STDERR_FILENO
1617 #    define SOCK_DUPOVER  STDERR_FILENO
1618 #  else
1619 #    define SOCK_DUPOVER  2
1620 #  endif /*STDERR_FILENO*/
1621     int fd = fcntl(sock->sock, F_DUPFD, SOCK_DUPOVER + 1);
1622     if (fd >= 0) {
1623         if (fd < FD_SETSIZE) {
1624             char _id[MAXIDLEN];
1625             int cloexec = fcntl(sock->sock, F_GETFD, 0);
1626             if (cloexec > 0  &&  (cloexec & FD_CLOEXEC))
1627                 fcntl(fd, F_SETFD, cloexec);
1628             CORE_LOGF_X(111, eLOG_Trace,
1629                         ("%s[SOCK::Select] "
1630                          " File descriptor has been lowered to %d",
1631                          s_ID(sock, _id), fd));
1632             close(sock->sock);
1633             sock->sock = fd;
1634             return 1/*success*/;
1635         }
1636         close(fd);
1637         errno = 0;
1638     }
1639     return 0/*failure*/;
1640 }
1641 #endif /*!NCBI_MSWIN && FD_SETSIZE*/
1642 
1643 
1644 #ifdef __GNUC__
1645 inline
1646 #endif /*__GNUC__*/
1647 /* compare 2 normalized timeval timeouts: "whether *v1 is less than *v2" */
s_IsSmallerTimeout(const struct timeval * v1,const struct timeval * v2)1648 static int/*bool*/ s_IsSmallerTimeout(const struct timeval* v1,
1649                                       const struct timeval* v2)
1650 {
1651     if (!v1/*inf*/)
1652         return 0;
1653     if (!v2/*inf*/)
1654         return 1;
1655     if (v1->tv_sec > v2->tv_sec)
1656         return 0;
1657     if (v1->tv_sec < v2->tv_sec)
1658         return 1;
1659     return v1->tv_usec < v2->tv_usec;
1660 }
1661 
1662 
1663 #if !defined(NCBI_OS_MSWIN)  ||  !defined(NCBI_CXX_TOOLKIT)
1664 
1665 
s_Select_(size_t n,SSOCK_Poll polls[],const struct timeval * tv,int asis)1666 static EIO_Status s_Select_(size_t                n,
1667                             SSOCK_Poll            polls[],
1668                             const struct timeval* tv,
1669                             int/*bool*/           asis)
1670 {
1671     char           _id[MAXIDLEN];
1672     int/*bool*/    write_only;
1673     int/*bool*/    read_only;
1674     int            ready;
1675     fd_set         rfds;
1676     fd_set         wfds;
1677     fd_set         efds;
1678     int            nfds;
1679     struct timeval x_tv;
1680     size_t         i;
1681 
1682 #  ifdef NCBI_OS_MSWIN
1683     if (!n) {
1684         DWORD ms =
1685             tv ? tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000 : INFINITE;
1686         Sleep(ms);
1687         return eIO_Timeout;
1688     }
1689 #  endif /*NCBI_OS_MSWIN*/
1690 
1691     if (tv)
1692         x_tv = *tv;
1693     else /* won't be used but keeps compilers happy */
1694         memset(&x_tv, 0, sizeof(x_tv));
1695 
1696     for (;;) { /* optionally auto-resume if interrupted / sliced */
1697         int/*bool*/    bad   = 0/*false*/;
1698 #  ifdef NCBI_OS_MSWIN
1699         unsigned int   count = 0;
1700 #  endif /*NCBI_OS_MSWIN*/
1701         struct timeval xx_tv;
1702 
1703         write_only = 1/*true*/;
1704         read_only = 1/*true*/;
1705         ready = 0/*false*/;
1706         FD_ZERO(&efds);
1707         nfds = 0;
1708 
1709         for (i = 0;  i < n;  ++i) {
1710             EIO_Event    event;
1711             SOCK         sock;
1712             ESOCK_Type   type;
1713             TSOCK_Handle fd;
1714 
1715             if (!(sock = polls[i].sock)) {
1716                 assert(!polls[i].revent/*eIO_Open*/);
1717                 continue;
1718             }
1719 
1720             event = polls[i].event;
1721             if ((event | eIO_ReadWrite) != eIO_ReadWrite) {
1722                 polls[i].revent = eIO_Close;
1723                 if (!bad) {
1724                     ready = 0/*false*/;
1725                     bad   = 1/*true*/;
1726                 }
1727                 continue;
1728             }
1729             if (!event) {
1730                 assert(!polls[i].revent/*eIO_Open*/);
1731                 continue;
1732             }
1733             if (bad)
1734                 continue;
1735 
1736             if ((fd = sock->sock) == SOCK_INVALID) {
1737                 polls[i].revent = eIO_Close;
1738                 ready = 1/*true*/;
1739                 continue;
1740             }
1741             if (polls[i].revent) {
1742                 ready = 1/*true*/;
1743                 if (polls[i].revent == eIO_Close)
1744                     continue;
1745                 assert((polls[i].revent | eIO_ReadWrite) == eIO_ReadWrite);
1746                 event = (EIO_Event)(event & ~polls[i].revent);
1747             }
1748 
1749 #  if !defined(NCBI_OS_MSWIN)  &&  defined(FD_SETSIZE)
1750             if (fd >= FD_SETSIZE) {
1751                 if (!x_TryLowerSockFileno(sock)) {
1752                     /* NB: only once here, as this sets "bad" to "1" */
1753                     CORE_LOGF_ERRNO_X(145, eLOG_Error, errno,
1754                                       ("%s[SOCK::Select] "
1755                                        " Socket file descriptor must "
1756                                        " be less than %d",
1757                                        s_ID(sock, _id), FD_SETSIZE));
1758                     polls[i].revent = eIO_Close;
1759                     ready = bad = 1/*true*/;
1760                     continue;
1761                 }
1762                 fd = sock->sock;
1763                 assert(fd < FD_SETSIZE);
1764             }
1765 #  endif /*!NCBI_OS_MSWIN && FD_SETSIZE*/
1766 
1767             type = (ESOCK_Type) sock->type;
1768             switch (type & eSocket ? event : event & eIO_Read) {
1769             case eIO_Write:
1770             case eIO_ReadWrite:
1771                 assert(type & eSocket);
1772                 if (type == eDatagram  ||  sock->w_status != eIO_Closed) {
1773                     if (read_only) {
1774                         FD_ZERO(&wfds);
1775                         read_only = 0/*false*/;
1776                     }
1777                     FD_SET(fd, &wfds);
1778                 }
1779                 if (event == eIO_Write  &&
1780                     (type == eDatagram  ||  asis
1781                      ||  (sock->r_on_w == eOff
1782                           ||  (sock->r_on_w == eDefault
1783                                &&  s_ReadOnWrite != eOn)))) {
1784                     break;
1785                 }
1786                 /*FALLTHRU*/
1787 
1788             case eIO_Read:
1789                 if (type != eSocket
1790                     ||  (sock->r_status != eIO_Closed  &&  !sock->eof)) {
1791                     if (write_only) {
1792                         FD_ZERO(&rfds);
1793                         write_only = 0/*false*/;
1794                     }
1795                     FD_SET(fd, &rfds);
1796                 }
1797                 if (type != eSocket  ||  asis  ||  event != eIO_Read
1798                     ||  sock->w_status == eIO_Closed
1799                     ||  !(sock->pending | sock->w_len)) {
1800                     break;
1801                 }
1802                 if (read_only) {
1803                     FD_ZERO(&wfds);
1804                     read_only = 0/*false*/;
1805                 }
1806                 FD_SET(fd, &wfds);
1807                 break;
1808 
1809             default:
1810                 /*fully pre-ready*/
1811                 break;
1812             }
1813 
1814             FD_SET(fd, &efds);
1815             if (nfds < (int) fd)
1816                 nfds = (int) fd;
1817 
1818 #  ifdef NCBI_OS_MSWIN
1819             /* check whether FD_SETSIZE has been exceeded */
1820             if (!FD_ISSET(fd, &efds)) {
1821                 /* NB: only once here, as this sets "bad" to "1" */
1822                 CORE_LOGF_X(145, eLOG_Error,
1823                             ("[SOCK::Select] "
1824                              " Too many sockets in select(),"
1825                              " must be fewer than %u", count));
1826                 polls[i].revent = eIO_Close;
1827                 ready = bad = 1/*true*/;
1828                 continue;
1829             }
1830             ++count;
1831 #  endif /*NCBI_OS_MSWIN*/
1832         }
1833         assert(i >= n);
1834 
1835         if (bad) {
1836             if (ready) {
1837                 errno = SOCK_ETOOMANY;
1838                 return eIO_Unknown;
1839             } else {
1840                 errno = EINVAL;
1841                 return eIO_InvalidArg;
1842             }
1843         }
1844 
1845         if (ready)
1846             memset(&xx_tv, 0, sizeof(xx_tv));
1847         else if (tv  &&  s_IsSmallerTimeout(&x_tv, s_SelectTimeout))
1848             xx_tv = x_tv;
1849         else if (s_SelectTimeout)
1850             xx_tv = *s_SelectTimeout;
1851         /* else infinite (0) timeout will be used */
1852 
1853         nfds = select(SOCK_NFDS((TSOCK_Handle) nfds),
1854                       write_only ? 0 : &rfds,
1855                       read_only  ? 0 : &wfds, &efds,
1856                       ready  ||  tv  ||  s_SelectTimeout ? &xx_tv : 0);
1857 
1858         if (nfds > 0)
1859             break;
1860 
1861         if (!nfds) {
1862             /* timeout has expired */
1863             if (!ready) {
1864                 if (!tv)
1865                     continue;
1866                 if (s_IsSmallerTimeout(s_SelectTimeout, &x_tv)) {
1867                     x_tv.tv_sec -= s_SelectTimeout->tv_sec;
1868                     if (x_tv.tv_usec < s_SelectTimeout->tv_usec) {
1869                         x_tv.tv_sec--;
1870                         x_tv.tv_usec += 1000000;
1871                     }
1872                     x_tv.tv_usec -= s_SelectTimeout->tv_usec;
1873                     continue;
1874                 }
1875                 return eIO_Timeout;
1876             }
1877             /* NB: ready */
1878         } else { /* nfds < 0 */
1879             int error = SOCK_ERRNO;
1880             if (error != SOCK_EINTR) {
1881                 const char* strerr = SOCK_STRERROR(error);
1882                 CORE_LOGF_ERRNO_EXX(5, eLOG_Warning,
1883                                     error, strerr ? strerr : "",
1884                                     ("%s[SOCK::Select] "
1885                                      " Failed select()",
1886                                      n == 1 ? s_ID(polls[0].sock, _id) : ""));
1887                 UTIL_ReleaseBuffer(strerr);
1888                 if (!ready)
1889                     return eIO_Unknown;
1890             } else if ((n != 1  &&  s_InterruptOnSignal == eOn)  ||
1891                        (n == 1  &&  (polls[0].sock->i_on_sig == eOn
1892                                      ||  (polls[0].sock->i_on_sig == eDefault
1893                                           &&  s_InterruptOnSignal == eOn)))) {
1894                 return eIO_Interrupt;
1895             } else
1896                 continue;
1897             assert(error != SOCK_EINTR  &&  ready);
1898         }
1899         break;
1900     }
1901 
1902     if (nfds > 0) {
1903         /* NB: some fd bits could have been counted multiple times if reside
1904            in different fd_set's (such as ready for both R and W), recount. */
1905         for (ready = 0, i = 0;  i < n;  ++i) {
1906             SOCK sock = polls[i].sock;
1907             if (sock  &&  polls[i].event) {
1908                 TSOCK_Handle fd;
1909                 if (polls[i].revent == eIO_Close) {
1910                     ++ready;
1911                     continue;
1912                 }
1913                 if ((fd = sock->sock) == SOCK_INVALID) {
1914                     polls[i].revent = eIO_Close;
1915                     ++ready;
1916                     continue;
1917                 }
1918 #  if !defined(NCBI_OS_MSWIN)  &&  defined(FD_SETSIZE)
1919                 assert(fd < FD_SETSIZE);
1920 #  endif /*!NCBI_OS_MSWIN && FD_SETSIZE*/
1921                 if (!write_only  &&  FD_ISSET(fd, &rfds)) {
1922                     polls[i].revent = (EIO_Event)(polls[i].revent | eIO_Read);
1923 #  ifdef NCBI_OS_MSWIN
1924                     sock->readable = 1/*true*/;
1925 #  endif /*NCBI_OS_MSWIN*/
1926                 }
1927                 if (!read_only   &&  FD_ISSET(fd, &wfds)) {
1928                     polls[i].revent = (EIO_Event)(polls[i].revent | eIO_Write);
1929 #  ifdef NCBI_OS_MSWIN
1930                     sock->writable = 1/*true*/;
1931 #  endif /*NCBI_OS_MSWIN*/
1932                 }
1933                 assert((polls[i].revent | eIO_ReadWrite) == eIO_ReadWrite);
1934                 if (polls[i].revent == eIO_Open) {
1935                     if (!FD_ISSET(fd, &efds))
1936                         continue;
1937                     polls[i].revent = eIO_Close;
1938                 } else if (sock->type == eTrigger)
1939                     polls[i].revent = polls[i].event;
1940                 assert(polls[i].revent != eIO_Open);
1941                 ++ready;
1942             } else
1943                 assert(polls[i].revent == eIO_Open);
1944         }
1945     }
1946 
1947     assert(ready);
1948     /* can do I/O now */
1949     return eIO_Success;
1950 }
1951 
1952 
1953 #  if defined(NCBI_OS_UNIX) && !defined(NCBI_OS_DARWIN) && defined(HAVE_POLL_H)
1954 
1955 
1956 #    define NPOLLS  ((3 * sizeof(fd_set)) / sizeof(struct pollfd))
1957 
1958 
x_CountPolls(size_t n,SSOCK_Poll polls[])1959 static size_t x_CountPolls(size_t n, SSOCK_Poll polls[])
1960 {
1961     int/*bool*/ bigfd = 0/*false*/;
1962     int/*bool*/ good  = 1/*true*/;
1963     size_t      count = 0;
1964     size_t      i;
1965 
1966     for (i = 0;  i < n;  ++i) {
1967         if (!polls[i].sock) {
1968             assert(!polls[i].revent/*eIO_Open*/);
1969             continue;
1970         }
1971         if ((polls[i].event | eIO_ReadWrite) != eIO_ReadWrite) {
1972             good = 0/*false*/;
1973             continue;
1974         }
1975         if (!polls[i].event) {
1976             assert(!polls[i].revent/*eIO_Open*/);
1977             continue;
1978         }
1979         if (polls[i].sock->sock == SOCK_INVALID
1980             ||  polls[i].revent == eIO_Close) {
1981             /* pre-ready */
1982             continue;
1983         }
1984 #    ifdef FD_SETSIZE
1985         if (polls[i].sock->sock >= FD_SETSIZE
1986             &&  (s_IOWaitSysAPI == eSOCK_IOWaitSysAPIPoll
1987                  ||  !x_TryLowerSockFileno(polls[i].sock))) {
1988             bigfd = 1/*true*/;
1989         }
1990 #    endif /*FD_SETSIZE*/
1991         ++count;
1992     }
1993     return good  &&  (s_IOWaitSysAPI != eSOCK_IOWaitSysAPIAuto
1994                       ||  count <= NPOLLS  ||  bigfd) ? count : 0;
1995 }
1996 
1997 
s_Poll_(size_t n,SSOCK_Poll polls[],const struct timeval * tv,int asis)1998 static EIO_Status s_Poll_(size_t                n,
1999                           SSOCK_Poll            polls[],
2000                           const struct timeval* tv,
2001                           int/*bool*/           asis)
2002 {
2003     struct pollfd  xx_polls[NPOLLS];
2004     char           _id[MAXIDLEN];
2005     struct pollfd* x_polls;
2006     EIO_Status     status;
2007     nfds_t         ready;
2008     nfds_t         count;
2009     int            wait;
2010     size_t         m, i;
2011 
2012     if (s_IOWaitSysAPI != eSOCK_IOWaitSysAPIAuto)
2013         m = n;
2014     else
2015 #    ifdef FD_SETSIZE
2016     if (n > FD_SETSIZE)
2017         m = n;
2018     else
2019 #    endif /*FD_SETSIZE*/
2020     if (!(m = x_CountPolls(n, polls)))
2021         return s_Select_(n, polls, tv, asis);
2022 
2023     if (m <= NPOLLS)
2024         x_polls = xx_polls;
2025     else if (!(x_polls = (struct pollfd*) malloc(m * sizeof(*x_polls)))) {
2026         CORE_LOGF_ERRNO_X(146, eLOG_Critical, errno,
2027                           ("%s[SOCK::Select] "
2028                            " Cannot allocate poll vector(%lu)",
2029                            n == 1 ? s_ID(polls[0].sock, _id) : "",
2030                            (unsigned long) m));
2031         return eIO_Unknown;
2032     }
2033 
2034     status = eIO_Success;
2035     wait = tv ? (int)(tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000) : -1;
2036     for (;;) { /* optionally auto-resume if interrupted / sliced */
2037         int/*bool*/ bad = 0/*false*/;
2038         int         x_ready;
2039         int         slice;
2040 
2041         ready = count = 0;
2042         for (i = 0;  i < n;  ++i) {
2043             short        bitset;
2044             EIO_Event    event;
2045             SOCK         sock;
2046             ESOCK_Type   type;
2047             TSOCK_Handle fd;
2048 
2049             if (!(sock = polls[i].sock)) {
2050                 assert(!polls[i].revent/*eIO_Open*/);
2051                 continue;
2052             }
2053 
2054             event = polls[i].event;
2055             if ((event | eIO_ReadWrite) != eIO_ReadWrite) {
2056                 polls[i].revent = eIO_Close;
2057                 bad = 1/*true*/;
2058                 continue;
2059             }
2060             if (!event) {
2061                 assert(!polls[i].revent/*eIO_Open*/);
2062                 continue;
2063             }
2064             if (bad)
2065                 continue;
2066 
2067             if ((fd = sock->sock) == SOCK_INVALID) {
2068                 polls[i].revent = eIO_Close;
2069                 ++ready;
2070                 continue;
2071             }
2072             if (polls[i].revent) {
2073                 ++ready;
2074                 if (polls[i].revent == eIO_Close)
2075                     continue;
2076                 assert((polls[i].revent | eIO_ReadWrite) == eIO_ReadWrite);
2077                 event = (EIO_Event)(event & ~polls[i].revent);
2078             }
2079 
2080             bitset = 0;
2081             type = (ESOCK_Type) sock->type;
2082             switch (type & eSocket ? event : event & eIO_Read) {
2083             case eIO_Write:
2084             case eIO_ReadWrite:
2085                 assert(type & eSocket);
2086                 if (type == eDatagram  ||  sock->w_status != eIO_Closed)
2087                     bitset |= POLLOUT;
2088                 if (event == eIO_Write  &&
2089                     (type == eDatagram  ||  asis
2090                      ||  (sock->r_on_w == eOff
2091                           ||  (sock->r_on_w == eDefault
2092                                &&  s_ReadOnWrite != eOn)))) {
2093                     break;
2094                 }
2095                 /*FALLTHRU*/
2096 
2097             case eIO_Read:
2098                 if (type != eSocket
2099                     ||  (sock->r_status != eIO_Closed  &&  !sock->eof))
2100                     bitset |= POLLIN;
2101                 if (type != eSocket  ||  asis  ||  event != eIO_Read
2102                     ||  sock->w_status == eIO_Closed
2103                     ||  !(sock->pending | sock->w_len)) {
2104                     break;
2105                 }
2106                 bitset |= POLLOUT;
2107                 break;
2108 
2109             default:
2110                 /*fully pre-ready*/
2111                 continue;
2112             }
2113 
2114             if (!bitset)
2115                 continue;
2116             assert(count < (nfds_t) m);
2117             x_polls[count].fd      = fd;
2118             x_polls[count].events  = bitset;
2119             x_polls[count].revents = 0;
2120             ++count;
2121         }
2122         assert(i >= n);
2123 
2124         if (bad) {
2125             status = eIO_InvalidArg;
2126             errno = EINVAL;
2127             break;
2128         }
2129 
2130         if (s_SelectTimeout) {
2131             slice = (int)((s_SelectTimeout->tv_sec         * 1000 +
2132                           (s_SelectTimeout->tv_usec + 500) / 1000));
2133             if (wait != -1  &&  wait < slice)
2134                 slice = wait;
2135         } else
2136             slice = wait;
2137 
2138         if (count  ||  !ready) {
2139             x_ready = poll(x_polls, count, !ready ? slice : 0);
2140 
2141             if (x_ready > 0) {
2142 #    ifdef NCBI_OS_DARWIN
2143                 /* Mac OS X sometimes misreports, weird! */
2144                 if (x_ready > (int) count)
2145                     x_ready = (int) count;  /* this is *not* a workaround!!! */
2146 #    endif /*NCBI_OS_DARWIN*/
2147                 assert(status == eIO_Success);
2148                 ready = (nfds_t) x_ready;
2149                 assert(ready <= count);
2150                 break;
2151             }
2152         } else
2153             x_ready = 0;
2154 
2155         if (!x_ready) {
2156             /* timeout has expired */
2157             if (!ready) {
2158                 if (!tv)
2159                     continue;
2160                 if (wait  > slice) {
2161                     wait -= slice;
2162                     continue;
2163                 }
2164                 status = eIO_Timeout;
2165                 break;
2166             }
2167             /* NB: ready */
2168         } else { /* x_ready < 0 */
2169             if ((x_ready = SOCK_ERRNO) != SOCK_EINTR) {
2170                 const char* strerr = SOCK_STRERROR(x_ready);
2171                 CORE_LOGF_ERRNO_EXX(147, ready ? eLOG_Warning : eLOG_Error,
2172                                     x_ready, strerr ? strerr : "",
2173                                     ("%s[SOCK::Select] "
2174                                      " Failed poll()",
2175                                      n == 1 ? s_ID(polls[0].sock, _id) : ""));
2176                 UTIL_ReleaseBuffer(strerr);
2177                 if (!ready) {
2178                     status = eIO_Unknown;
2179                     break;
2180                 }
2181             } else if ((n != 1  &&  s_InterruptOnSignal == eOn)  ||
2182                        (n == 1  &&  (polls[0].sock->i_on_sig == eOn
2183                                      ||  (polls[0].sock->i_on_sig == eDefault
2184                                           &&  s_InterruptOnSignal == eOn)))) {
2185                 status = eIO_Interrupt;
2186                 break;
2187             } else
2188                 continue;
2189             assert(x_ready != SOCK_EINTR  &&  ready);
2190         }
2191 
2192         assert(status == eIO_Success  &&  ready);
2193         n = 0/*no post processing*/;
2194         break;
2195     }
2196 
2197     assert(status != eIO_Success  ||  ready > 0);
2198     if (status == eIO_Success  &&  n) {
2199         nfds_t x_ready = 0;
2200         nfds_t scanned = 0;
2201         for (m = 0, i = 0;  i < n;  ++i) {
2202             SOCK sock = polls[i].sock;
2203             if (sock  &&  polls[i].event) {
2204                 TSOCK_Handle fd;
2205                 short events, revents;
2206                 if (polls[i].revent == eIO_Close) {
2207                     ++x_ready;
2208                     continue;
2209                 }
2210                 if ((fd = sock->sock) == SOCK_INVALID) {
2211                     polls[i].revent = eIO_Close;
2212                     ++x_ready;
2213                     continue;
2214                 }
2215                 events = revents = 0;
2216                 if (scanned < ready) {
2217                     nfds_t x_scanned = 0;
2218                     nfds_t j;
2219                     assert((nfds_t) m < count);
2220                     for (j = (nfds_t) m;  j < count;  ++j) {
2221                         if (x_polls[j].revents)
2222                             ++x_scanned;
2223                         if (x_polls[j].fd == fd) {
2224                             events   = x_polls[j].events;
2225                             revents  = x_polls[j].revents;
2226                             scanned += x_scanned;
2227                             m        = (size_t) ++j;
2228                             break;
2229                         }
2230                     }
2231                     assert(events  ||  ((nfds_t) m < count  &&  count <= j));
2232                 }
2233                 if ((events & POLLIN)
2234                     &&  (revents & (POLLIN | POLLRDHUP | POLLPRI))) {
2235                     polls[i].revent = (EIO_Event)(polls[i].revent | eIO_Read);
2236                 }
2237                 if ((events & POLLOUT)
2238                     &&  (revents & (POLLOUT | POLLHUP))) {
2239                     polls[i].revent = (EIO_Event)(polls[i].revent | eIO_Write);
2240                 }
2241                 assert((polls[i].revent | eIO_ReadWrite) == eIO_ReadWrite);
2242                 if (polls[i].revent == eIO_Open) {
2243                     if (!(revents & (POLLERR | POLLNVAL)))
2244                         continue;
2245                     polls[i].revent = eIO_Close;
2246                 } else if (sock->type == eTrigger)
2247                     polls[i].revent = polls[i].event;
2248                 assert(polls[i].revent != eIO_Open);
2249                 ++x_ready;
2250             } else
2251                 assert(polls[i].revent == eIO_Open);
2252         }
2253         assert(scanned <= ready);
2254         assert(x_ready >= ready);
2255     }
2256 
2257     if (x_polls != xx_polls)
2258         free(x_polls);
2259     return status;
2260 }
2261 
2262 
2263 #  endif /*NCBI_OS_UNIX && !NCBI_OS_DARWIN && HAVE_POLL_H*/
2264 
2265 
2266 #endif /*!NCBI_OS_MSWIN || !NCBI_CXX_TOOLKIT*/
2267 
2268 
2269 /* Select on the socket I/O (multiple sockets).
2270  *
2271  * "Event" field is not considered for entries, whose "sock" field is 0,
2272  * "revent" for those entries is always set "eIO_Open".  For all other entries
2273  * "revent" will be checked, and if set, it will be "subtracted" from the
2274  * requested "event" (or the entry won't be considered at all if "revent" is
2275  * already set to "eIO_Close").  If at least one non-"eIO_Open" status found
2276  * in "revent", the call terminates with "eIO_Success" (after, however, having
2277  * checked all other entries for validity, and) after having polled
2278  * (with timeout 0) on all remaining entries and events.
2279  *
2280  * This function always checks datagram and listening sockets, and triggers
2281  * exactly as they are requested (according to "event").  For stream sockets,
2282  * the call behaves differently only if the last parameter is passed as zero:
2283  *
2284  * If "eIO_Write" event is inquired on a stream socket, and the socket is
2285  * marked for upread, then returned "revent" may also include "eIO_Read" to
2286  * indicate that some input is available on that socket.
2287  *
2288  * If "eIO_Read" event is inquired on a stream socket, and the socket still
2289  * has its connection/data pending, the "revent" field may then include
2290  * "eIO_Write" to indicate that connection can be completed/data sent.
2291  *
2292  * Return "eIO_Success" when at least one socket is found either ready
2293  * (including "eIO_Read" event on "eIO_Write" for upreadable sockets, and/or
2294  * "eIO_Write" on "eIO_Read" for sockets in pending state when "asis!=0")
2295  * or failing ("revent" contains "eIO_Close").
2296  *
2297  * Return "eIO_Timeout", if timeout expired before any socket was capable
2298  * of doing any IO.  Any other return code indicates some usage failure.
2299  */
s_Select(size_t n,SSOCK_Poll polls[],const struct timeval * tv,int asis)2300 static EIO_Status s_Select(size_t                n,
2301                            SSOCK_Poll            polls[],
2302                            const struct timeval* tv,
2303                            int/*bool*/           asis)
2304 {
2305 #if defined(NCBI_OS_MSWIN)  &&  defined(NCBI_CXX_TOOLKIT)
2306     DWORD  wait = tv ? tv->tv_sec * 1000 + (tv->tv_usec + 500)/1000 : INFINITE;
2307     HANDLE what[MAXIMUM_WAIT_OBJECTS];
2308     long   want[MAXIMUM_WAIT_OBJECTS];
2309     char  _id[MAXIDLEN];
2310 
2311     for (;;) { /* timeslice loop */
2312         int/*bool*/ done  = 0/*false*/;
2313         int/*bool*/ ready = 0/*false*/;
2314         DWORD       count = 0;
2315         DWORD       slice;
2316         size_t      i;
2317 
2318         for (i = 0;  i < n;  ++i) {
2319             long      bitset;
2320             EIO_Event event;
2321             SOCK      sock;
2322             HANDLE    ev;
2323 
2324             if (!(sock = polls[i].sock)) {
2325                 assert(!polls[i].revent/*eIO_Open*/);
2326                 continue;
2327             }
2328 
2329             event = polls[i].event;
2330             if ((event | eIO_ReadWrite) != eIO_ReadWrite) {
2331                 polls[i].revent = eIO_Close;
2332                 if (!done) {
2333                     ready = 0/*false*/;
2334                     done  = 1/*true*/;
2335                 }
2336                 continue;
2337             }
2338             if (!event) {
2339                 assert(!polls[i].revent/*eIO_Open*/);
2340                 continue;
2341             }
2342             if (done)
2343                 continue;
2344 
2345             if (sock->sock == SOCK_INVALID) {
2346                 polls[i].revent = eIO_Close;
2347                 ready = 1/*true*/;
2348                 continue;
2349             }
2350             if (polls[i].revent) {
2351                 ready = 1/*true*/;
2352                 if (polls[i].revent == eIO_Close)
2353                     continue;
2354                 assert((polls[i].revent | eIO_ReadWrite) == eIO_ReadWrite);
2355                 event = (EIO_Event)(event & ~polls[i].revent);
2356             }
2357 
2358             bitset = 0;
2359             if (sock->type != eTrigger) {
2360                 ESOCK_Type type = (ESOCK_Type) sock->type;
2361                 EIO_Event  readable = sock->readable ? eIO_Read  : eIO_Open;
2362                 EIO_Event  writable = sock->writable ? eIO_Write : eIO_Open;
2363                 switch (type & eSocket ? event : event & eIO_Read) {
2364                 case eIO_Write:
2365                 case eIO_ReadWrite:
2366                     if (type == eDatagram  ||  sock->w_status != eIO_Closed) {
2367                         if (writable) {
2368                             polls[i].revent |= eIO_Write;
2369                             ready = 1/*true*/;
2370                         }
2371                         if (!sock->connected)
2372                             bitset |= FD_CONNECT/*C*/;
2373                         bitset     |= FD_WRITE/*W*/;
2374                     }
2375                     if (event == eIO_Write  &&
2376                         (type == eDatagram  ||  asis
2377                          ||  (sock->r_on_w == eOff
2378                               ||  (sock->r_on_w == eDefault
2379                                    &&  s_ReadOnWrite != eOn)))) {
2380                         break;
2381                     }
2382                     /*FALLTHRU*/
2383 
2384                 case eIO_Read:
2385                     if (type != eSocket
2386                         ||  (sock->r_status != eIO_Closed  &&  !sock->eof)) {
2387                         if (readable) {
2388                             polls[i].revent |= eIO_Read;
2389                             ready = 1/*true*/;
2390                         }
2391                         if (type & eSocket) {
2392                             if (type == eSocket)
2393                                 bitset |= FD_OOB/*O*/;
2394                             bitset     |= FD_READ/*R*/;
2395                         } else
2396                             bitset     |= FD_ACCEPT/*A*/;
2397                     }
2398                     if (type != eSocket  ||  asis  ||  event != eIO_Read
2399                         ||  sock->w_status == eIO_Closed
2400                         ||  !(sock->pending | sock->w_len)) {
2401                         break;
2402                     }
2403                     if (writable) {
2404                         polls[i].revent |= eIO_Write;
2405                         ready = 1/*true*/;
2406                     }
2407                     bitset |= FD_WRITE/*W*/;
2408                     break;
2409 
2410                 default:
2411                     /*fully pre-ready*/
2412                     continue;
2413                 }
2414 
2415                 if (!bitset)
2416                     continue;
2417                 ev = sock->event;
2418             } else
2419                 ev = ((TRIGGER) sock)->fd;
2420 
2421             if (count >= sizeof(what) / sizeof(what[0])) {
2422                 /* NB: only once here, as this sets "done" to "1" */
2423                 CORE_LOGF_X(145, eLOG_Error,
2424                             ("[SOCK::Select] "
2425                              " Too many objects, must be fewer than %u",
2426                              (unsigned int) count));
2427                 polls[i].revent = eIO_Close;
2428                 ready = done = 1/*true*/;
2429                 continue;
2430             }
2431             want[count] = bitset;
2432             what[count] = ev;
2433             ++count;
2434         }
2435         assert(i >= n);
2436 
2437         if (done) {
2438             if (ready) {
2439                 errno = SOCK_ETOOMANY;
2440                 return eIO_Unknown;
2441             } else {
2442                 errno = EINVAL;
2443                 return eIO_InvalidArg;
2444             }
2445         }
2446 
2447         if (s_SelectTimeout) {
2448             slice = (s_SelectTimeout->tv_sec         * 1000 +
2449                     (s_SelectTimeout->tv_usec + 500) / 1000);
2450             if (wait != INFINITE  &&  wait < slice)
2451                 slice = wait;
2452         } else
2453             slice = wait;
2454 
2455         if (count) {
2456             DWORD m = 0, r;
2457             i = 0;
2458             do {
2459                 size_t j;
2460                 DWORD  c = count - m;
2461                 r = WaitForMultipleObjects(c,
2462                                            what + m,
2463                                            FALSE/*any*/,
2464                                            ready ? 0 : slice);
2465                 if (r == WAIT_FAILED) {
2466                     DWORD err = GetLastError();
2467                     const char* strerr = s_WinStrerror(err);
2468                     CORE_LOGF_ERRNO_EXX(133, eLOG_Error,
2469                                         err, strerr ? strerr : "",
2470                                         ("[SOCK::Select] "
2471                                          " Failed WaitForMultipleObjects(%u)",
2472                                          (unsigned int) c));
2473                     UTIL_ReleaseBufferOnHeap(strerr);
2474                     break;
2475                 }
2476                 if (r == WAIT_TIMEOUT)
2477                     break;
2478                 if (r < WAIT_OBJECT_0  ||  WAIT_OBJECT_0 + c <= r) {
2479                     CORE_LOGF_X(134, !ready ? eLOG_Error : eLOG_Warning,
2480                                 ("[SOCK::Select] "
2481                                  " WaitForMultipleObjects(%u) returned %d",
2482                                  (unsigned int) c, (int)(r - WAIT_OBJECT_0)));
2483                     r = WAIT_FAILED;
2484                     break;
2485                 }
2486                 m += r - WAIT_OBJECT_0;
2487                 assert(!done);
2488 
2489                 /* something must be ready */
2490                 for (j = i;  j < n;  ++j) {
2491                     SOCK sock = polls[j].sock;
2492                     WSANETWORKEVENTS e;
2493                     long bitset;
2494                     if (!sock  ||  !polls[j].event)
2495                         continue;
2496                     if (polls[j].revent == eIO_Close) {
2497                         ready = 1/*true*/;
2498                         continue;
2499                     }
2500                     if (sock->type == eTrigger) {
2501                         if (what[m] != ((TRIGGER) sock)->fd)
2502                             continue;
2503                         polls[j].revent = polls[j].event;
2504                         assert(polls[j].revent != eIO_Open);
2505                         done = 1/*true*/;
2506                         break;
2507                     }
2508                     if (sock->sock == SOCK_INVALID) {
2509                         polls[j].revent = eIO_Close;
2510                         ready = 1/*true*/;
2511                         continue;
2512                     }
2513                     if (what[m] != sock->event)
2514                         continue;
2515                     /* reset well before a re-enabling WSA API call occurs */
2516                     if (!WSAResetEvent(what[m])) {
2517                         sock->r_status = sock->w_status = eIO_Closed;
2518                         polls[j].revent = eIO_Close;
2519                         done = 1/*true*/;
2520                         break;
2521                     }
2522                     if (WSAEnumNetworkEvents(sock->sock, what[m], &e) != 0) {
2523                         int error = SOCK_ERRNO;
2524                         const char* strerr = SOCK_STRERROR(error);
2525                         CORE_LOGF_ERRNO_EXX(136, eLOG_Error,
2526                                             error, strerr ? strerr : "",
2527                                             ("%s[SOCK::Select] "
2528                                              " Failed WSAEnumNetworkEvents",
2529                                              s_ID(sock, _id)));
2530                         UTIL_ReleaseBuffer(strerr);
2531                         polls[j].revent = eIO_Close;
2532                         done = 1/*true*/;
2533                         break;
2534                     }
2535                     /* NB: the bits are XCAOWR */
2536                     if (!(bitset = e.lNetworkEvents)) {
2537                         if (ready  ||  !slice) {
2538                             m = count - 1;
2539                             assert(!done);
2540                             break;
2541                         }
2542                         if (sock->type == eListening
2543                             &&  (sock->log == eOn  ||
2544                                  (sock->log == eDefault  &&  s_Log == eOn))) {
2545                             LSOCK lsock = (LSOCK) sock;
2546                             ELOG_Level level;
2547                             if (lsock->away < 10) {
2548                                 lsock->away++;
2549                                 level = eLOG_Warning;
2550                             } else
2551                                 level = eLOG_Trace;
2552                             CORE_LOGF_X(141, level,
2553                                         ("%s[SOCK::Select] "
2554                                          " Run-away connection detected",
2555                                          s_ID(sock, _id)));
2556                         }
2557                         break;
2558                     }
2559                     if (bitset & FD_CLOSE/*X*/) {
2560                         if (sock->type != eSocket) {
2561                             polls[j].revent = eIO_Close;
2562                             done = 1/*true*/;
2563                             break;
2564                         }
2565                         bitset |= FD_READ/*at least SHUT_WR @ remote end*/;
2566                         sock->readable = sock->closing = 1/*true*/;
2567                     } else {
2568                         if (bitset & (FD_CONNECT | FD_WRITE)) {
2569                             assert(sock->type & eSocket);
2570                             sock->writable = 1/*true*/;
2571                         }
2572                         if (bitset & (FD_ACCEPT | FD_OOB | FD_READ))
2573                             sock->readable = 1/*true*/;
2574                     }
2575                     bitset &= want[m];
2576                     if ((bitset & (FD_CONNECT | FD_WRITE))
2577                         &&  sock->writable) {
2578                         assert(sock->type & eSocket);
2579                         polls[j].revent=(EIO_Event)(polls[j].revent|eIO_Write);
2580                         done = 1/*true*/;
2581                     }
2582                     if ((bitset & (FD_ACCEPT | FD_OOB | FD_READ))
2583                         &&  sock->readable) {
2584                         polls[j].revent=(EIO_Event)(polls[j].revent|eIO_Read);
2585                         done = 1/*true*/;
2586                     }
2587                     assert((polls[j].revent | eIO_ReadWrite) == eIO_ReadWrite);
2588                     if (!polls[j].revent) {
2589                         int k;
2590                         if ((e.lNetworkEvents & FD_CLOSE)
2591                             &&  !e.iErrorCode[FD_CLOSE_BIT]) {
2592                             polls[j].revent = polls[j].event;
2593                             done = 1/*true*/;
2594                         } else for (k = 0;  k < FD_MAX_EVENTS;  ++k) {
2595                             if (!(e.lNetworkEvents & (1 << k)))
2596                                 continue;
2597                             if (e.iErrorCode[k]) {
2598                                 polls[j].revent = eIO_Close;
2599                                 errno = e.iErrorCode[k];
2600                                 done = 1/*true*/;
2601                                 break;
2602                             }
2603                         }
2604                     } else
2605                         done = 1/*true*/;
2606                     break;
2607                 }
2608                 if (done) {
2609                     ready = 1/*true*/;
2610                     done = 0/*false*/;
2611                     i = ++j;
2612                 }
2613                 if (ready  ||  !slice)
2614                     ++m;
2615             } while (m < count);
2616 
2617             if (ready)
2618                 break;
2619 
2620             if (r == WAIT_FAILED)
2621                 return eIO_Unknown;
2622             /* treat this as a timed out slice */
2623         } else if (ready) {
2624             break;
2625         } else
2626             Sleep(slice);
2627 
2628         if (wait != INFINITE) {
2629             if (wait  > slice) {
2630                 wait -= slice;
2631                 continue;
2632             }
2633             return eIO_Timeout;
2634         }
2635     }
2636 
2637     /* can do I/O now */
2638     return eIO_Success;
2639 
2640 #else /*!NCBI_OS_MSWIN || !NCBI_CXX_TOOLKIT*/
2641 
2642 #  if defined(NCBI_OS_UNIX) && !defined(NCBI_OS_DARWIN) && defined(HAVE_POLL_H)
2643     if (s_IOWaitSysAPI != eSOCK_IOWaitSysAPISelect)
2644         return s_Poll_(n, polls, tv, asis);
2645 #  endif /*NCBI_OS_UNIX && !NCBI_OS_DARWIN && HAVE_POLL_H*/
2646 
2647     return s_Select_(n, polls, tv, asis);
2648 
2649 #endif /*NCBI_OS_MSWIN && NCBI_CXX_TOOLKIT*/
2650 }
2651 
2652 
2653 #if defined(NCBI_COMPILER_GCC)  ||  defined(NCBI_COMPILER_ANY_CLANG)
2654 #  pragma GCC diagnostic push                       /* NCBI_FAKE_WARNING */
2655 #  pragma GCC diagnostic ignored "-Wuninitialized"  /* NCBI_FAKE_WARNING */
x_tvcpy(struct timeval * dst,struct timeval * src)2656 static inline void x_tvcpy(struct timeval* dst, struct timeval* src)
2657 {
2658     memcpy(dst, src, sizeof(*dst));
2659 }
2660 #  pragma GCC diagnostic warning "-Wuninitialized"  /* NCBI_FAKE_WARNING */
2661 #  pragma GCC diagnostic pop                        /* NCBI_FAKE_WARNING */
2662 #else
2663 #  define x_tvcpy(d, s)  (void) memcpy((d), (s), sizeof(*(d)))
2664 #endif /*NCBI_COMPILER_GCC*/
2665 
2666 
2667 /* connect() could be async/interrupted by a signal or just cannot establish
2668  * the connection immediately;  yet, it must have been in progress
2669  * (asynchronous), so wait here for it to succeed (become writeable).
2670  */
s_IsConnected_(SOCK sock,const struct timeval * tv,const char ** what,int * error,int writeable)2671 static EIO_Status s_IsConnected_(SOCK                  sock,
2672                                  const struct timeval* tv,
2673                                  const char**          what,
2674                                  int*                  error,
2675                                  int/*bool*/           writeable)
2676 {
2677     char _id[MAXIDLEN];
2678     EIO_Status status;
2679     SSOCK_Poll poll;
2680 
2681     *what = 0;
2682     *error = 0;
2683     if (sock->w_status == eIO_Closed)
2684         return eIO_Closed;
2685 
2686     errno = 0;
2687     if (!writeable) {
2688         poll.sock   = sock;
2689         poll.event  = eIO_Write;
2690         poll.revent = eIO_Open;
2691         status = s_Select(1, &poll, tv, 1/*asis*/);
2692         assert(poll.event == eIO_Write);
2693         if (status == eIO_Timeout)
2694             return status;
2695     } else {
2696         status      = eIO_Success;
2697         poll.revent = eIO_Write;
2698     }
2699 
2700 #if defined(NCBI_OS_UNIX)  ||  defined(NCBI_OS_MSWIN)
2701     if (!sock->connected  &&  status == eIO_Success) {
2702         TSOCK_socklen_t len = (TSOCK_socklen_t) sizeof(*error);
2703         /* Note WSA resets SOCK_ERROR to 0 after this call, if successful */
2704         if (getsockopt(sock->sock, SOL_SOCKET, SO_ERROR, (void*) error, &len)
2705             != 0  ||  *error != 0) {
2706             status = eIO_Unknown;
2707             /* if left zero, *error will be assigned errno just a bit later */
2708         }
2709     }
2710 #endif /*NCBI_OS_UNIX || NCBI_OS_MSWIN*/
2711 
2712     if (status != eIO_Success  ||  poll.revent != eIO_Write) {
2713         if (!*error) {
2714             *error = SOCK_ERRNO;
2715 #  ifdef NCBI_OS_MSWIN
2716             if (!*error)
2717                 *error = errno;
2718 #  endif /*NCBI_OS_MSWIN*/
2719         }
2720         if (*error == SOCK_ECONNREFUSED  ||  *error == SOCK_ETIMEDOUT)
2721             sock->r_status = sock->w_status = status = eIO_Closed;
2722         else if (status == eIO_Success)
2723             status = eIO_Unknown;
2724         return status;
2725     }
2726 
2727     if (!sock->connected) {
2728         if (sock->log == eOn  ||  (sock->log == eDefault  &&  s_Log == eOn)) {
2729 #if defined(_DEBUG)  &&  !defined(NDEBUG)
2730             char mtu[128];
2731 #  if defined(SOL_IP)  &&  defined(IP_MTU)
2732             if (sock->port) {
2733                 int             m    = 0;
2734                 TSOCK_socklen_t mlen = (TSOCK_socklen_t) sizeof(m);
2735                 if (getsockopt(sock->sock, SOL_IP, IP_MTU, &m, &mlen) != 0) {
2736                     const char* strerr = SOCK_STRERROR(SOCK_ERRNO);
2737                     sprintf(mtu, ", MTU ?? (%.80s)", strerr ? strerr : "??");
2738                     UTIL_ReleaseBuffer(strerr);
2739                 } else
2740                     sprintf(mtu, ", MTU = %d", m);
2741             } else
2742 #  endif /*SOL_IP && IP_MTU*/
2743                 *mtu = '\0';
2744 #else
2745             static const char* mtu = "";
2746 #endif /*_DEBUG && !NDEBUG*/
2747             CORE_LOGF(eLOG_Trace,
2748                       ("%sConnection established%s", s_ID(sock, _id), mtu));
2749         }
2750         if (s_ReuseAddress == eOn
2751 #ifdef NCBI_OS_UNIX
2752             &&  !sock->path[0]
2753 #endif /*NCBI_OS_UNIX*/
2754             &&  !s_SetReuseAddress(sock->sock, 1/*true*/)) {
2755             int x_error = SOCK_ERRNO;
2756             const char* strerr = SOCK_STRERROR(x_error);
2757             CORE_LOGF_ERRNO_EXX(6, eLOG_Trace,
2758                                 x_error, strerr ? strerr : "",
2759                                 ("%s[SOCK::IsConnected] "
2760                                  " Failed setsockopt(REUSEADDR)",
2761                                  s_ID(sock, _id)));
2762             UTIL_ReleaseBuffer(strerr);
2763         }
2764         sock->connected = 1/*true*/;
2765     }
2766 
2767     if (sock->pending) {
2768         if (sock->sslctx) {
2769             FSSLOpen sslopen = s_SSL ? s_SSL->Open : 0;
2770             if (sslopen) {
2771                 int/*bool*/ want_desc
2772                     = (sock->log == eOn
2773                        ||  (sock->log == eDefault  &&  s_Log == eOn)
2774                        ? 1/*true*/ : 0/*false*/);
2775                 const unsigned int rtv_set = sock->r_tv_set;
2776                 const unsigned int wtv_set = sock->w_tv_set;
2777                 struct timeval rtv;
2778                 struct timeval wtv;
2779                 char* desc;
2780                 if (rtv_set)
2781                     rtv = sock->r_tv;
2782                 if (wtv_set)
2783                     wtv = sock->w_tv;
2784                 SOCK_SET_TIMEOUT(sock, r, tv);
2785                 SOCK_SET_TIMEOUT(sock, w, tv);
2786                 status = sslopen(sock->sslctx->sess, error,
2787                                  want_desc ? &desc : 0);
2788                 if ((sock->w_tv_set = wtv_set & 1) != 0)
2789                     x_tvcpy(&sock->w_tv, &wtv);
2790                 if ((sock->r_tv_set = rtv_set & 1) != 0)
2791                     x_tvcpy(&sock->r_tv, &rtv);
2792                 if (status == eIO_Success) {
2793                     sock->pending = 0/*false*/;
2794                     if (want_desc) {
2795                         CORE_LOGF(eLOG_Trace,
2796                                   ("%sSSL session created%s%s%s%s%s",
2797                                    s_ID(sock, _id),
2798                                    sock->sslctx->host? " \""              : "",
2799                                    sock->sslctx->host? sock->sslctx->host : "",
2800                                    &"\""[!sock->sslctx->host],
2801                                    &" "[!desc], desc ? desc : ""));
2802                         if (desc)
2803                             free(desc);
2804                     }
2805                 } else
2806                     *what = "SSL hello";
2807             } else
2808                 status = eIO_NotSupported;
2809         } else
2810             sock->pending = 0/*false*/;
2811     }
2812 
2813     return status;
2814 }
2815 
2816 
s_WaitConnected(SOCK sock,const struct timeval * tv)2817 static EIO_Status s_WaitConnected(SOCK sock, const struct timeval* tv)
2818 {
2819     const char* what;
2820     int         unused;
2821     EIO_Status  status = s_IsConnected_(sock, tv, &what, &unused, 0);
2822     if (s_ErrHook  &&  status != eIO_Success  &&  status != eIO_Timeout) {
2823         SSOCK_ErrInfo info;
2824         char          addr[40];
2825         memset(&info, 0, sizeof(info));
2826         info.type = eSOCK_ErrIO;
2827         info.sock = sock;
2828         if (sock->port) {
2829             SOCK_ntoa(sock->host, addr, sizeof(addr));
2830             info.host =       addr;
2831             info.port = sock->port;
2832         }
2833 #ifdef NCBI_OS_UNIX
2834         else
2835             info.host = sock->path;
2836 #endif /*NCBI_OS_UNIX*/
2837         info.event = eIO_Open;
2838         info.status = status;
2839         s_ErrorCallback(&info);
2840     }
2841     return status;
2842 }
2843 
2844 
2845 /* Read as many as "size" bytes of data from the socket.  Return eIO_Success
2846  * if at least one byte has been read or EOF has been reached (0 bytes read).
2847  * Otherwise (nothing read), return an error code to indicate the problem.
2848  * NOTE:  This call is for stream sockets only.  Also, it can return the
2849  * above mentioned EOF indicator only once, with all successive calls to
2850  * return an error (usually, eIO_Closed).
2851  */
s_Recv(SOCK sock,void * buf,size_t size,size_t * n_read,int flag)2852 static EIO_Status s_Recv(SOCK    sock,
2853                          void*   buf,
2854                          size_t  size,
2855                          size_t* n_read,
2856                          int     flag)
2857 {
2858     int/*bool*/ readable;
2859     char _id[MAXIDLEN];
2860 
2861     assert(sock->type == eSocket  &&  buf  &&  size > 0  &&  !*n_read);
2862 
2863     if (sock->r_status == eIO_Closed)
2864         return eIO_Unknown;
2865     if (sock->eof)
2866         return eIO_Closed;
2867 
2868     /* read from the socket */
2869     readable = 0/*false*/;
2870     for (;;) { /* optionally auto-resume if interrupted */
2871         int error;
2872         ssize_t x_read = recv(sock->sock, buf, WIN_INT_CAST size, 0/*flags*/);
2873 #ifdef NCBI_OS_MSWIN
2874         /* recv() resets IO event recording */
2875         sock->readable = sock->closing;
2876 #endif /*NCBI_OS_MSWIN*/
2877 
2878         /* success/EOF? */
2879         if (x_read >= 0  ||
2880             (x_read < 0  &&  ((error = SOCK_ERRNO) == SOCK_ENOTCONN    ||  /*NCBI_FAKE_WARNING*/
2881                               error                == SOCK_ETIMEDOUT   ||
2882                               error                == SOCK_ENETRESET   ||
2883                               error                == SOCK_ECONNRESET  ||
2884                               error                == SOCK_ECONNABORTED))) {
2885             /* statistics & logging */
2886             if ((x_read < 0  &&  sock->log != eOff)  ||
2887                 ((sock->log == eOn || (sock->log == eDefault && s_Log == eOn))
2888                  &&  (!sock->sslctx  ||  flag > 0))) {
2889                 s_DoLog(x_read < 0
2890                         ? (sock->n_read  &&  sock->n_written
2891                            ? eLOG_Error : eLOG_Trace)
2892                         : eLOG_Note, sock, eIO_Read,
2893                         x_read < 0 ? (void*) &error :
2894                         x_read > 0 ? buf            : 0,
2895                         (size_t)(x_read < 0 ? 0 : x_read), 0);
2896             }
2897 
2898             if (x_read > 0) {
2899                 assert((size_t) x_read <= size);
2900                 sock->n_read += (TNCBI_BigCount) x_read;
2901                 *n_read       = (size_t)         x_read;
2902             } else {
2903                 /* catch EOF/failure */
2904                 sock->eof = 1/*true*/;
2905                 if (x_read) {
2906                     sock->r_status = sock->w_status = eIO_Closed;
2907                     return eIO_Unknown/*error*/;
2908                 }
2909 #ifdef NCBI_OS_MSWIN
2910                 sock->closing = 1/*true*/;
2911 #endif /*NCBI_OS_MSWIN*/
2912             }
2913             sock->r_status = eIO_Success;
2914             break/*success*/;
2915         }
2916 
2917         if (error == SOCK_EWOULDBLOCK  ||  error == SOCK_EAGAIN) {
2918             /* blocked -- wait for data to come;  return if timeout/error */
2919             EIO_Status status;
2920             SSOCK_Poll poll;
2921 
2922             if (sock->r_tv_set  &&  !(sock->r_tv.tv_sec | sock->r_tv.tv_usec)){
2923                 sock->r_status = eIO_Timeout;
2924                 break/*timeout*/;
2925             }
2926             if (readable) {
2927                 CORE_TRACEF(("%s[SOCK::Recv] "
2928                              " Spurious false indication of data ready",
2929                              s_ID(sock, _id)));
2930             }
2931             poll.sock   = sock;
2932             poll.event  = eIO_Read;
2933             poll.revent = eIO_Open;
2934             status = s_Select(1, &poll, SOCK_GET_TIMEOUT(sock, r), 1/*asis*/);
2935             assert(poll.event == eIO_Read);
2936             if (status == eIO_Timeout) {
2937                 sock->r_status = eIO_Timeout;
2938                 break/*timeout*/;
2939             }
2940             if (status != eIO_Success)
2941                 return status;
2942             if (poll.revent == eIO_Close)
2943                 return eIO_Unknown;
2944             assert(poll.revent == eIO_Read);
2945             readable = 1/*true*/;
2946             continue/*read again*/;
2947         }
2948 
2949         if (error != SOCK_EINTR) {
2950             const char* strerr = SOCK_STRERROR(error);
2951             CORE_LOGF_ERRNO_EXX(7, eLOG_Trace,
2952                                 error, strerr ? strerr : "",
2953                                 ("%s[SOCK::Recv] "
2954                                  " Failed recv()",
2955                                  s_ID(sock, _id)));
2956             UTIL_ReleaseBuffer(strerr);
2957             /* don't want to handle all possible errors...
2958                let them be "unknown" */
2959             sock->r_status = eIO_Unknown;
2960             break/*unknown*/;
2961         }
2962 
2963         if (sock->i_on_sig == eOn
2964             ||  (sock->i_on_sig == eDefault  &&  s_InterruptOnSignal == eOn)) {
2965             sock->r_status = eIO_Interrupt;
2966             break/*interrupt*/;
2967         }
2968     }
2969 
2970     return (EIO_Status) sock->r_status;
2971 }
2972 
2973 
2974 /*fwdecl*/
2975 static EIO_Status s_WritePending(SOCK, const struct timeval*, int, int);
2976 
2977 
2978 /* Read/Peek data from the socket.  Return eIO_Success iff some data have been
2979  * read.  Return other (error) code if an error/EOF occurred (zero bytes read).
2980  * (MSG_PEEK is not implemented on Mac, and it is poorly implemented
2981  * on Win32, so we had to implement this feature by ourselves.)
2982  * NB:  peek = {-1=upread(!buf && !size), 0=read, 1=peek}
2983  */
s_Read_(SOCK sock,void * buf,size_t size,size_t * n_read,int peek)2984 static EIO_Status s_Read_(SOCK    sock,
2985                           void*   buf,
2986                           size_t  size,
2987                           size_t* n_read,
2988                           int     peek)
2989 {
2990     unsigned int rtv_set;
2991     struct timeval rtv;
2992     char _id[MAXIDLEN];
2993     EIO_Status status;
2994     int/*bool*/ done;
2995 
2996     assert(sock->type & eSocket);
2997 
2998     if (sock->type != eDatagram  &&  peek >= 0) {
2999         *n_read = 0;
3000         status = s_WritePending(sock, SOCK_GET_TIMEOUT(sock, r), 0, 0);
3001         if (sock->pending) {
3002             assert(status != eIO_Success);
3003             return status == eIO_Closed ? eIO_Unknown : status;
3004         }
3005         if (!size  &&  peek >= 0) {
3006             status = (EIO_Status) sock->r_status;
3007             if (status == eIO_Closed)
3008                 status = eIO_Unknown;
3009             else if (sock->eof)
3010                 status = eIO_Closed;
3011             return status;
3012         }
3013     }
3014 
3015     if (sock->type == eDatagram  ||  peek >= 0) {
3016         *n_read = (peek
3017                    ? BUF_Peek(sock->r_buf, buf, size)
3018                    : BUF_Read(sock->r_buf, buf, size));
3019         if (sock->type == eDatagram) {
3020             if (size  &&  !*n_read) {
3021                 sock->r_status = eIO_Closed;
3022                 return eIO_Closed;
3023             }
3024             return !size ? (EIO_Status) sock->r_status : eIO_Success;
3025         }
3026         if (*n_read  &&  (*n_read == size  ||  !peek))
3027             return eIO_Success;
3028     } else
3029         *n_read = 0;
3030 
3031     if ((status = (EIO_Status) sock->r_status) == eIO_Closed  ||  sock->eof) {
3032         if (*n_read)
3033             return eIO_Success;
3034         if (status == eIO_Closed) {
3035             CORE_TRACEF(("%s[SOCK::Read] "
3036                          " Socket already shut down for reading",
3037                          s_ID(sock, _id)));
3038             return eIO_Unknown;
3039         }
3040         return eIO_Closed/*EOF*/;
3041     }
3042 
3043     done = 0/*false*/;
3044     if ((rtv_set = sock->r_tv_set) != 0)
3045         rtv = sock->r_tv;
3046     assert(!*n_read  ||  peek > 0);
3047     assert((peek >= 0  &&  size)  ||  (peek < 0  &&  !buf  &&  !size));
3048     do {
3049         char   xx_buf[SOCK_BUF_CHUNK_SIZE / 4], *x_buf, *p_buf;
3050         size_t x_todo, x_read, x_save;
3051 
3052         if (buf  &&  (x_todo = size - *n_read) >= SOCK_BUF_CHUNK_SIZE) {
3053             x_buf  = (char*) buf + *n_read;
3054             p_buf  = 0;
3055         } else if (!(p_buf = (char*) malloc(SOCK_BUF_CHUNK_SIZE))) {
3056             x_todo = sizeof(xx_buf);
3057             x_buf  = xx_buf;
3058         } else {
3059             x_todo = SOCK_BUF_CHUNK_SIZE;
3060             x_buf  = p_buf;
3061         }
3062         if (sock->sslctx) {
3063             int error;
3064             FSSLRead sslread = s_SSL ? s_SSL->Read : 0;
3065             if (!sslread) {
3066                 if (p_buf)
3067                     free(p_buf);
3068                 status = eIO_NotSupported;
3069                 break/*error*/;
3070             }
3071             status = sslread(sock->sslctx->sess,
3072                              x_buf, x_todo, &x_read, &error);
3073             assert(status == eIO_Success  ||  !x_read);
3074             assert(status == eIO_Success  ||  error);
3075             assert(x_read <= x_todo);
3076 
3077             /* statistics & logging */
3078             if ((status != eIO_Success  &&  sock->log != eOff)  ||
3079                 sock->log == eOn  ||  (sock->log == eDefault && s_Log == eOn)){
3080                 s_DoLog(x_read ? eLOG_Note : eLOG_Trace, sock, eIO_Read,
3081                         x_read ? x_buf :
3082                         status != eIO_Closed  ||  !sock->eof ?
3083                         (void*) &error : 0,
3084                         status != eIO_Success ? 0 : x_read,
3085                         x_read ? " [decrypt]" : 0);
3086             }
3087 
3088             if (status == eIO_Closed  &&  !sock->eof)
3089                 sock->r_status = eIO_Closed;
3090         } else {
3091             x_read = 0;
3092             status = s_Recv(sock, x_buf, x_todo, &x_read, 0);
3093             assert(status == eIO_Success  ||  !x_read);
3094             assert(x_read <= x_todo);
3095         }
3096         if (status != eIO_Success  ||  !x_read) {
3097             if (p_buf)
3098                 free(p_buf);
3099             if (status == eIO_Success)
3100                 status  = eIO_Closed/*EOF*/;
3101             break;
3102         }
3103         assert(status == eIO_Success  &&  0 < x_read  &&  x_read <= x_todo);
3104 
3105         if (x_read < x_todo)
3106             done = 1/*true*/;
3107 
3108         if (peek >= 0) {
3109             assert(size > *n_read);  /* NB: subsumes size > 0 */
3110             x_todo = size - *n_read;
3111             if (x_todo > x_read)
3112                 x_todo = x_read;
3113             if (buf  &&  (p_buf  ||  x_buf == xx_buf))
3114                 memcpy((char*) buf + *n_read, x_buf, x_todo);
3115             x_save = peek ? x_read : x_read - x_todo;
3116         } else
3117             x_save = x_read;
3118         if (x_save) {
3119             /* store the newly read/excess data in the internal input buffer */
3120             if (!p_buf  ||  x_save < SOCK_BUF_CHUNK_SIZE / 2) {
3121                 sock->eof = !BUF_Write(&sock->r_buf,
3122                                        peek ? x_buf : x_buf + x_todo,
3123                                        x_save);
3124                 if (p_buf)
3125                     free(p_buf);
3126             } else {
3127                 sock->eof = !BUF_AppendEx(&sock->r_buf, p_buf, peek
3128                                           ? SOCK_BUF_CHUNK_SIZE
3129                                           : SOCK_BUF_CHUNK_SIZE - x_todo,
3130                                           peek ? p_buf : p_buf + x_todo,
3131                                           x_save);
3132                 if (sock->eof)
3133                     free(p_buf);
3134             }
3135             if (sock->eof) {
3136                 CORE_LOGF_ERRNO_X(8, eLOG_Error, errno,
3137                                   ("%s[SOCK::Read] "
3138                                    " Cannot save %lu byte%s of unread data",
3139                                    s_ID(sock, _id), (unsigned long) x_save,
3140                                    &"s"[x_save == 1]));
3141                 sock->r_status = eIO_Closed/*failure*/;
3142                 x_read = peek >= 0 ? x_todo : 0;
3143                 status = eIO_Unknown;
3144             } else if (peek >= 0)
3145                 x_read = x_todo;
3146         } else if (p_buf)
3147             free(p_buf);
3148         *n_read += x_read;
3149 
3150         if (status != eIO_Success  ||  done)
3151             break;
3152         /*zero timeout*/
3153         sock->r_tv_set = 1;
3154         memset(&sock->r_tv, 0, sizeof(sock->r_tv));
3155     } while (peek < 0  ||  (!buf  &&  *n_read < size));
3156     if ((sock->r_tv_set = rtv_set & 1) != 0)
3157         x_tvcpy(&sock->r_tv, &rtv);
3158 
3159     return *n_read ? eIO_Success : status;
3160 }
3161 
3162 
s_Read(SOCK sock,void * buf,size_t size,size_t * n_read,int peek)3163 static EIO_Status s_Read(SOCK    sock,
3164                          void*   buf,
3165                          size_t  size,
3166                          size_t* n_read,
3167                          int     peek)
3168 {
3169     EIO_Status status = s_Read_(sock, buf, size, n_read, peek);
3170     if (s_ErrHook  &&  status != eIO_Success
3171         &&  (status != eIO_Closed
3172              ||  !(sock->r_status == eIO_Success  &&  sock->eof))) {
3173         SSOCK_ErrInfo info;
3174         char          addr[40];
3175         memset(&info, 0, sizeof(info));
3176         info.type = eSOCK_ErrIO;
3177         info.sock = sock;
3178         if (sock->port) {
3179             SOCK_ntoa(sock->host, addr, sizeof(addr));
3180             info.host =       addr;
3181             info.port = sock->port;
3182         }
3183 #ifdef NCBI_OS_UNIX
3184         else
3185             info.host = sock->path;
3186 #endif /*NCBI_OS_UNIX*/
3187         info.event = eIO_Read;
3188         info.status = status;
3189         s_ErrorCallback(&info);
3190     }
3191     assert(*n_read <= size);
3192     return status;
3193 }
3194 
3195 
3196 /* s_Select() with stall protection:  try pull incoming data from sockets.
3197  * This method returns array of polls, "revent"s of which are always
3198  * compatible with requested "event"s.  That is, it always strips additional
3199  * events that s_Select() may have set to indicate additional I/O events
3200  * some sockets are ready for.  Return eIO_Timeout if no compatible events
3201  * were found (all sockets are not ready for inquired respective I/O) within
3202  * the specified timeout (and no other socket error was flagged).
3203  * Return eIO_Success if at least one socket is ready.  Return the number
3204  * of sockets that are ready via pointer argument "n_ready" (may be NULL).
3205  * Return other error code to indicate an error condition.
3206  */
s_SelectStallsafe(size_t n,SSOCK_Poll polls[],const struct timeval * tv,size_t * n_ready)3207 static EIO_Status s_SelectStallsafe(size_t                n,
3208                                     SSOCK_Poll            polls[],
3209                                     const struct timeval* tv,
3210                                     size_t*               n_ready)
3211 {
3212     size_t i, k;
3213 
3214     assert(!n  ||  polls);
3215 
3216     for (;;) { /* until one full "tv" term expires or an error occurs */
3217         int/*bool*/ pending;
3218         EIO_Status  status;
3219 
3220         status = s_Select(n, polls, tv, 0);
3221         if (status != eIO_Success) {
3222             if (n_ready)
3223                 *n_ready = 0;
3224             return status;
3225         }
3226 
3227         k = 0;
3228         pending = 0;
3229         for (i = 0;  i < n;  ++i) {
3230             if (polls[i].revent == eIO_Close)
3231                 break;
3232             if (polls[i].revent & polls[i].event)
3233                 break;
3234             if (polls[i].revent != eIO_Open  &&  !pending) {
3235                 pending = 1;
3236                 k = i;
3237             }
3238         }
3239         if (i < n/*ready*/)
3240             break;
3241 
3242         /* all sockets are not ready for the requested events */
3243         assert(pending);
3244         for (i = k;  i < n;  ++i) {
3245             static const struct timeval zero = { 0 };
3246             SOCK sock = polls[i].sock;
3247             /* try to push pending writes */
3248             if (polls[i].event == eIO_Read  &&  polls[i].revent == eIO_Write) {
3249                 assert(sock                          &&
3250                        sock->sock != SOCK_INVALID    &&
3251                        sock->type == eSocket         &&
3252                        sock->w_status != eIO_Closed  &&
3253                        (sock->pending | sock->w_len));
3254                 s_WritePending(sock, &zero, 1/*writeable*/, 0);
3255                 if (sock->r_status == eIO_Closed  ||  sock->eof) {
3256                     polls[i].revent = eIO_Read;
3257                     pending = 0;
3258                 } else
3259                     polls[i].revent = eIO_Open;
3260                 continue;
3261             }
3262             /* try to upread immediately readable sockets */
3263             if (polls[i].event == eIO_Write  &&  polls[i].revent == eIO_Read) {
3264                 size_t dummy;
3265                 assert(sock                          &&
3266                        sock->sock != SOCK_INVALID    &&
3267                        sock->type == eSocket         &&
3268                        sock->w_status != eIO_Closed  &&
3269                        sock->r_status != eIO_Closed  &&
3270                        !sock->eof  && !sock->pending &&
3271                        (sock->r_on_w == eOn
3272                         ||  (sock->r_on_w == eDefault
3273                              &&  s_ReadOnWrite == eOn)));
3274                 s_Read_(sock, 0, 0, &dummy, -1/*upread*/);
3275                 if (sock->w_status == eIO_Closed) {
3276                     polls[i].revent = eIO_Write;
3277                     pending = 0;
3278                 } else
3279                     polls[i].revent = eIO_Open;
3280             }
3281         }
3282         if (!pending)
3283             break;
3284     }
3285 
3286     k = 0;
3287     for (i = 0;  i < n;  ++i) {
3288         if (polls[i].revent != eIO_Close) {
3289             polls[i].revent = (EIO_Event)(polls[i].revent & polls[i].event);
3290             if (!polls[i].revent)
3291                 continue;
3292         }
3293         ++k;
3294     }
3295 
3296     if (n_ready)
3297         *n_ready = k;
3298 
3299     return k ? eIO_Success : eIO_Timeout;
3300 }
3301 
3302 
s_Wait(SOCK sock,EIO_Event event,const STimeout * timeout)3303 static EIO_Status s_Wait(SOCK sock, EIO_Event event, const STimeout* timeout)
3304 {
3305     struct timeval tv;
3306     SSOCK_Poll     poll;
3307     EIO_Status     status;
3308 
3309     poll.sock   = sock;
3310     poll.event  = event;
3311     poll.revent = eIO_Open;
3312     status = s_SelectStallsafe(1, &poll, s_to2tv(timeout, &tv), 0);
3313     assert(poll.event == event);
3314     if (status != eIO_Success)
3315         return status;
3316     if (poll.revent == eIO_Close)
3317         return eIO_Unknown;
3318     assert(!(poll.revent ^ (poll.revent & event)));
3319     return status/*success*/;
3320 }
3321 
3322 
3323 #ifdef NCBI_OS_MSWIN
s_AddTimeout(struct timeval * tv,int ms_addend)3324 static void s_AddTimeout(struct timeval* tv, int ms_addend)
3325 {
3326     tv->tv_usec += (ms_addend % 1000) * 1000;
3327     tv->tv_sec  +=  ms_addend / 1000;
3328     if (tv->tv_usec >= 10000000) {
3329         tv->tv_sec  += tv->tv_usec / 10000000;
3330         tv->tv_usec %= 10000000;
3331     }
3332 }
3333 #endif /*NCBI_OS_MSWIN*/
3334 
3335 
3336 #ifdef SOCK_SEND_SLICE
3337 #  define s_Send  s_Send_
3338 #endif /*SOCK_SEND_SLICE*/
3339 
3340 
3341 /* Write data to the socket "as is" (as many bytes at once as possible).
3342  * Return eIO_Success iff at least some bytes have been written successfully.
3343  * Otherwise (nothing written), return an error code to indicate the problem.
3344  * NOTE: This call is for stream sockets only.
3345  */
s_Send(SOCK sock,const void * data,size_t size,size_t * n_written,int flag)3346 static EIO_Status s_Send(SOCK        sock,
3347                          const void* data,
3348                          size_t      size,
3349                          size_t*     n_written,
3350                          int         flag)
3351 {
3352 #ifdef NCBI_OS_MSWIN
3353     int wait_buf_ms = 0;
3354     struct timeval waited;
3355     memset(&waited, 0, sizeof(waited));
3356 #endif /*NCBI_OS_MSWIN*/
3357 
3358     assert(sock->type == eSocket  &&  data  &&  size > 0  &&  !*n_written);
3359 
3360     if (sock->w_status == eIO_Closed)
3361         return eIO_Closed;
3362 
3363     for (;;) { /* optionally auto-resume if interrupted */
3364         int error = 0;
3365 
3366         ssize_t x_written = send(sock->sock, (void*) data, WIN_INT_CAST size,
3367                                  flag < 0 ? MSG_OOB : 0);
3368 
3369         if (x_written >= 0  ||
3370             (x_written < 0  &&  ((error = SOCK_ERRNO) == SOCK_EPIPE       ||
3371                                  error                == SOCK_ENOTCONN    ||
3372                                  error                == SOCK_ETIMEDOUT   ||
3373                                  error                == SOCK_ENETRESET   ||
3374                                  error                == SOCK_ECONNRESET  ||
3375                                  error                == SOCK_ECONNABORTED))) {
3376             /* statistics & logging */
3377             if ((x_written <= 0  &&  sock->log != eOff)  ||
3378                 ((sock->log == eOn || (sock->log == eDefault && s_Log == eOn))
3379                  &&  (!sock->sslctx  ||  flag > 0))) {
3380                 s_DoLog(x_written <= 0
3381                         ? (sock->n_read  &&  sock->n_written
3382                            ? eLOG_Error : eLOG_Trace)
3383                         : eLOG_Note, sock, eIO_Write,
3384                         x_written <= 0 ? (void*) &error : data,
3385                         (size_t)(x_written <= 0 ? 0 : x_written),
3386                         flag < 0 ? "" : 0);
3387             }
3388 
3389             if (x_written > 0) {
3390                 sock->n_written += (TNCBI_BigCount) x_written;
3391                 *n_written       = (size_t)         x_written;
3392                 sock->w_status = eIO_Success;
3393                 break/*success*/;
3394             }
3395             if (x_written < 0) {
3396                 if (error != SOCK_EPIPE)
3397                     sock->r_status = eIO_Closed;
3398                 sock->w_status = eIO_Closed;
3399                 break/*closed*/;
3400             }
3401         }
3402 
3403         if (flag < 0/*OOB*/  ||  !x_written)
3404             return eIO_Unknown;
3405 
3406         /* blocked -- retry if unblocked before the timeout expires
3407          * (use stall protection if specified) */
3408         if (error == SOCK_EWOULDBLOCK  ||  error == SOCK_EAGAIN
3409 #ifdef NCBI_OS_MSWIN
3410             ||  error == WSAENOBUFS
3411 #endif /*NCBI_OS_MSWIN*/
3412             ) {
3413             SSOCK_Poll            poll;
3414             EIO_Status            status;
3415             const struct timeval* timeout;
3416 
3417 #ifdef NCBI_OS_MSWIN
3418             struct timeval        slice;
3419             unsigned int          writable = sock->writable;
3420 
3421             /* special send()'s semantics of IO event recording reset */
3422             sock->writable = 0/*false*/;
3423             if (error == WSAENOBUFS) {
3424                 if (size < SOCK_BUF_CHUNK_SIZE / 4) {
3425                     s_AddTimeout(&waited, wait_buf_ms);
3426                     if (s_IsSmallerTimeout(SOCK_GET_TIMEOUT(sock, w),&waited)){
3427                         sock->w_status = eIO_Timeout;
3428                         return eIO_Timeout;
3429                     }
3430                     if (wait_buf_ms == 0)
3431                         wait_buf_ms  = 10;
3432                     else if (wait_buf_ms < 500/*640*/)
3433                         wait_buf_ms <<= 1;
3434                     slice.tv_sec  = 0;
3435                     slice.tv_usec = wait_buf_ms * 1000;
3436                 } else {
3437                     size >>= 1;
3438                     memset(&slice, 0, sizeof(slice));
3439                 }
3440                 timeout = &slice;
3441             } else {
3442                 if (wait_buf_ms) {
3443                     wait_buf_ms = 0;
3444                     memset(&waited, 0, sizeof(waited));
3445                 }
3446                 timeout = SOCK_GET_TIMEOUT(sock, w);
3447             }
3448 #else
3449             {
3450                 if (sock->w_tv_set && !(sock->w_tv.tv_sec|sock->w_tv.tv_usec)){
3451                     sock->w_status = eIO_Timeout;
3452                     break/*timeout*/;
3453                 }
3454                 timeout = SOCK_GET_TIMEOUT(sock, w);
3455             }
3456 #endif /*NCBI_OS_MSWIN*/
3457 
3458             poll.sock   = sock;
3459             poll.event  = eIO_Write;
3460             poll.revent = eIO_Open;
3461             /* stall protection:  try pulling incoming data from the socket */
3462             status = s_SelectStallsafe(1, &poll, timeout, 0);
3463             assert(poll.event == eIO_Write);
3464 #ifdef NCBI_OS_MSWIN
3465             if (error == WSAENOBUFS) {
3466                 assert(timeout == &slice);
3467                 sock->writable = writable/*restore*/;
3468                 if (status == eIO_Timeout)
3469                     continue/*try to write again*/;
3470             } else
3471 #endif /*NCBI_OS_MSWIN*/
3472             if (status == eIO_Timeout) {
3473                 sock->w_status = eIO_Timeout;
3474                 break/*timeout*/;
3475             }
3476             if (status != eIO_Success)
3477                 return status;
3478             if (poll.revent == eIO_Close)
3479                 return eIO_Unknown;
3480             assert(poll.event == eIO_Write);
3481             continue/*write again*/;
3482         }
3483 
3484         if (error != SOCK_EINTR) {
3485             char _id[MAXIDLEN];
3486             const char* strerr = SOCK_STRERROR(error);
3487             CORE_LOGF_ERRNO_EXX(11, eLOG_Trace,
3488                                 error, strerr ? strerr : "",
3489                                 ("%s[SOCK::Send] "
3490                                  " Failed send()",
3491                                  s_ID(sock, _id)));
3492             UTIL_ReleaseBuffer(strerr);
3493             /* don't want to handle all possible errors...
3494                let them be "unknown" */
3495             sock->w_status = eIO_Unknown;
3496             break/*unknown*/;
3497         }
3498 
3499         if (sock->i_on_sig == eOn
3500             ||  (sock->i_on_sig == eDefault  &&  s_InterruptOnSignal == eOn)) {
3501             sock->w_status = eIO_Interrupt;
3502             break/*interrupt*/;
3503         }
3504     }
3505 
3506     return (EIO_Status) sock->w_status;
3507 }
3508 
3509 
3510 /* Wrapper for s_Send() that slices the output buffer for some brain-dead
3511  * systems (e.g. old Macs) that cannot handle large data chunks in "send()".
3512  * Return eIO_Success only if some data have been successfully sent;
3513  * otherwise, an error code if nothing at all has been sent.
3514  */
3515 #ifdef SOCK_SEND_SLICE
3516 #  undef s_Send
s_Send(SOCK sock,const void * data,size_t size,size_t * n_written,int flag)3517 static EIO_Status s_Send(SOCK        sock,
3518                          const void* data,
3519                          size_t      size,
3520                          size_t*     n_written,
3521                          int         flag)
3522 {
3523     /* split output buffer in slices (of size <= SOCK_SEND_SLICE) */
3524     EIO_Status status;
3525 
3526     assert(!*n_written);
3527 
3528     do {
3529         size_t n_todo = size > SOCK_SEND_SLICE ? SOCK_SEND_SLICE : size;
3530         size_t n_done = 0;
3531         status = s_Send_(sock, data, n_todo, &n_done, flag);
3532         assert((status == eIO_Success) == (n_done > 0));
3533         if (status != eIO_Success)
3534             break;
3535         *n_written += n_done;
3536         if (n_todo != n_done)
3537             break;
3538         size       -= n_done;
3539         data        = (const char*) data + n_done;
3540     } while (size);
3541 
3542     return *n_written ? eIO_Success : status;
3543 }
3544 #endif /*SOCK_SEND_SLICE*/
3545 
3546 
3547 /* Return eIO_Success iff some data have been written; error code otherwise */
s_WriteData(SOCK sock,const void * data,size_t size,size_t * n_written,int oob)3548 static EIO_Status s_WriteData(SOCK        sock,
3549                               const void* data,
3550                               size_t      size,
3551                               size_t*     n_written,
3552                               int/*bool*/ oob)
3553 {
3554     assert(sock->type == eSocket  &&  !sock->pending  &&  size);
3555 
3556     if (sock->sslctx) {
3557         int error;
3558         EIO_Status status;
3559         FSSLWrite sslwrite = s_SSL ? s_SSL->Write : 0;
3560         if (!sslwrite  ||  oob) {
3561             *n_written = 0;
3562             return eIO_NotSupported;
3563         }
3564         status = sslwrite(sock->sslctx->sess, data, size, n_written, &error);
3565         assert((status == eIO_Success) == (*n_written > 0));
3566         assert(status == eIO_Success  ||  error);
3567         assert(*n_written <= size);
3568 
3569         /* statistics & logging */
3570         if ((status != eIO_Success  &&  sock->log != eOff)  ||
3571             sock->log == eOn  ||  (sock->log == eDefault  &&  s_Log == eOn)) {
3572             s_DoLog(*n_written > 0 ? eLOG_Note : eLOG_Trace, sock, eIO_Write,
3573                     status == eIO_Success ? data : (void*) &error,
3574                     status != eIO_Success ? 0    : *n_written,
3575                     n_written > 0 ? " [encrypt]" : 0);
3576         }
3577 
3578         if (status == eIO_Closed)
3579             sock->w_status = eIO_Closed;
3580         return status;
3581     }
3582 
3583     *n_written = 0;
3584     return s_Send(sock, data, size, n_written, oob ? -1 : 0);
3585 }
3586 
3587 
3588 struct XWriteBufCtx {
3589     SOCK       sock;
3590     EIO_Status status;
3591 };
3592 
3593 
x_WriteBuf(void * data,const void * buf,size_t size)3594 static size_t x_WriteBuf(void* data, const void* buf, size_t size)
3595 {
3596     struct XWriteBufCtx* ctx = (struct XWriteBufCtx*) data;
3597     size_t n_written = 0;
3598 
3599     assert(buf  &&  size  &&  ctx->status == eIO_Success);
3600 
3601     do {
3602         size_t x_written;
3603         ctx->status = s_WriteData(ctx->sock, buf, size, &x_written, 0);
3604         assert((ctx->status == eIO_Success) == (x_written > 0));
3605         assert(x_written <= size);
3606         if (ctx->status != eIO_Success)
3607             break;
3608         n_written += x_written;
3609         size      -= x_written;
3610         buf        = (const char*) buf + x_written;
3611     } while (size);
3612 
3613     assert(!size/*n_written == initial size*/  ||  ctx->status != eIO_Success);
3614 
3615     return n_written;
3616 }
3617 
3618 
s_WritePending(SOCK sock,const struct timeval * tv,int writeable,int oob)3619 static EIO_Status s_WritePending(SOCK                  sock,
3620                                  const struct timeval* tv,
3621                                  int/*bool*/           writeable,
3622                                  int/*bool*/           oob)
3623 {
3624     struct XWriteBufCtx ctx;
3625     unsigned int restore;
3626     unsigned int wtv_set;
3627     struct timeval wtv;
3628 
3629     assert(sock->type == eSocket  &&  sock->sock != SOCK_INVALID);
3630 
3631     if (sock->pending) {
3632         const char* what;
3633         int        error;
3634         EIO_Status status = s_IsConnected_(sock, tv, &what, &error, writeable);
3635         if (status != eIO_Success) {
3636             if (status != eIO_Timeout) {
3637                 char _id[MAXIDLEN];
3638                 const char* strerr = s_StrError(sock, error);
3639                 CORE_LOGF_ERRNO_EXX(12, sock->log != eOff
3640                                     ? eLOG_Error : eLOG_Trace,
3641                                     error, strerr ? strerr : "",
3642                                     ("%s[SOCK::WritePending] "
3643                                      " Failed %s: %s",
3644                                      s_ID(sock, _id),
3645                                      what ? what : "pending connect()",
3646                                      IO_StatusStr(status)));
3647                 UTIL_ReleaseBuffer(strerr);
3648                 sock->w_status = status;
3649             }
3650             return status;
3651         }
3652         assert(sock->connected  &&  !sock->pending);
3653     }
3654     if ((!sock->sslctx  &&  oob)  ||  !sock->w_len)
3655         return eIO_Success;
3656     if (sock->w_status == eIO_Closed)
3657         return eIO_Closed;
3658     assert(sock->w_len == BUF_Size(sock->w_buf));
3659 
3660     if (tv != &sock->w_tv) {
3661         if ((wtv_set = sock->w_tv_set) != 0)
3662             wtv = sock->w_tv;
3663         SOCK_SET_TIMEOUT(sock, w, tv);
3664         restore = 1;
3665     } else
3666         restore = wtv_set/*to silence compiler warning*/ = 0;
3667 
3668     ctx.sock     = sock;
3669     ctx.status   = eIO_Success;
3670     sock->w_len -= BUF_PeekAtCB(sock->w_buf,
3671                                 BUF_Size(sock->w_buf) - sock->w_len,
3672                                 x_WriteBuf, &ctx, sock->w_len);
3673     assert((sock->w_len != 0) == (ctx.status != eIO_Success));
3674 
3675     if (restore  &&  (sock->w_tv_set = wtv_set & 1) != 0)
3676         x_tvcpy(&sock->w_tv, &wtv);
3677     return ctx.status;
3678 }
3679 
3680 
3681 /* Write to the socket.  Return eIO_Success if some data have been written.
3682  * Return other (error) code only if nothing at all can be written.
3683  */
s_Write_(SOCK sock,const void * data,size_t size,size_t * n_written,int oob)3684 static EIO_Status s_Write_(SOCK        sock,
3685                            const void* data,
3686                            size_t      size,
3687                            size_t*     n_written,
3688                            int/*bool*/ oob)
3689 {
3690     EIO_Status status;
3691 
3692     assert(sock->type & eSocket);
3693 
3694     if (sock->type == eDatagram) {
3695         sock->w_len = 0;
3696         if (sock->eof) {
3697             BUF_Erase(sock->w_buf);
3698             sock->eof = 0;
3699         }
3700         if (BUF_Write(&sock->w_buf, data, size)) {
3701             *n_written = size;
3702             sock->w_status = eIO_Success;
3703         } else {
3704             *n_written = 0;
3705             sock->w_status = eIO_Unknown;
3706         }
3707         return (EIO_Status) sock->w_status;
3708     }
3709 
3710     if (sock->w_status == eIO_Closed) {
3711         if (size) {
3712             CORE_DEBUG_ARG(char _id[MAXIDLEN];)
3713             CORE_TRACEF(("%s[SOCK::Write] "
3714                          " Socket already shut down for writing",
3715                          s_ID(sock, _id)));
3716         }
3717         *n_written = 0;
3718         return eIO_Closed;
3719     }
3720 
3721     status = s_WritePending(sock, SOCK_GET_TIMEOUT(sock, w), 0, oob);
3722     if (status != eIO_Success  ||  !size) {
3723         *n_written = 0;
3724         if (status == eIO_Timeout  ||  status == eIO_Closed)
3725             return status;
3726         return size ? status : eIO_Success;
3727     }
3728 
3729     assert(size  &&  sock->w_len == 0);
3730     return s_WriteData(sock, data, size, n_written, oob);
3731 }
3732 
3733 
s_Write(SOCK sock,const void * data,size_t size,size_t * n_written,int oob)3734 static EIO_Status s_Write(SOCK        sock,
3735                           const void* data,
3736                           size_t      size,
3737                           size_t*     n_written,
3738                           int/*bool*/ oob)
3739 {
3740     EIO_Status status = s_Write_(sock, data, size, n_written, oob);
3741     if (s_ErrHook  &&  status != eIO_Success) {
3742         SSOCK_ErrInfo info;
3743         char          addr[40];
3744         memset(&info, 0, sizeof(info));
3745         info.type = eSOCK_ErrIO;
3746         info.sock = sock;
3747         if (sock->port) {
3748             SOCK_ntoa(sock->host, addr, sizeof(addr));
3749             info.host =       addr;
3750             info.port = sock->port;
3751         }
3752 #ifdef NCBI_OS_UNIX
3753         else
3754             info.host = sock->path;
3755 #endif /*NCBI_OS_UNIX*/
3756         info.event = eIO_Write;
3757         info.status = status;
3758         s_ErrorCallback(&info);
3759     }
3760     assert(*n_written <= size);
3761     return status;
3762 }
3763 
3764 
3765 /* For non-datagram sockets only */
s_Shutdown(SOCK sock,EIO_Event dir,const struct timeval * tv)3766 static EIO_Status s_Shutdown(SOCK                  sock,
3767                              EIO_Event             dir,
3768                              const struct timeval* tv)
3769 {
3770     int        error;
3771     char       _id[MAXIDLEN];
3772     EIO_Status status = eIO_Success;
3773     int        how = SOCK_SHUTDOWN_WR;
3774 
3775     assert(sock->type == eSocket);
3776 
3777     switch (dir) {
3778     case eIO_Read:
3779         if (sock->eof) {
3780             /* hit EOF (and may be not yet shut down) -- so, flag it as been
3781              * shut down, but do not perform the actual system call,
3782              * as it can cause smart OS'es like Linux to complain.
3783              */
3784             sock->eof = 0/*false*/;
3785             sock->r_status = eIO_Closed;
3786         }
3787         if (sock->r_status == eIO_Closed)
3788             return eIO_Success;  /* has been shut down already */
3789         sock->r_status = eIO_Closed;
3790         how = SOCK_SHUTDOWN_RD;
3791         break;
3792 
3793     case eIO_ReadWrite:
3794         if (sock->eof) {
3795             sock->eof = 0/*false*/;
3796             sock->r_status = eIO_Closed;
3797         } else
3798             how = SOCK_SHUTDOWN_RDWR;
3799         if (sock->w_status == eIO_Closed  &&  sock->r_status == eIO_Closed)
3800             return eIO_Success;  /* has been shut down already */
3801         /*FALLTHRU*/
3802 
3803     case eIO_Write:
3804         if (sock->w_status == eIO_Closed  &&  dir == eIO_Write)
3805             return eIO_Success;  /* has been shut down already */
3806         /*FALLTHRU*/
3807 
3808     case eIO_Open:
3809     case eIO_Close:
3810         if (sock->w_status != eIO_Closed) {
3811             if ((status = s_WritePending(sock, tv, 0, 0)) != eIO_Success) {
3812                 if (!sock->pending  &&  sock->w_len) {
3813                     CORE_LOGF_X(13, !tv  ||  (tv->tv_sec | tv->tv_usec)
3814                                 ? eLOG_Warning : eLOG_Trace,
3815                                 ("%s[SOCK::%s] "
3816                                  " %s with output (%lu byte%s) still pending"
3817                                  " (%s)", s_ID(sock, _id),
3818                                  dir & eIO_ReadWrite ? "Shutdown" : "Close",
3819                                  !dir ? "Leaving " : dir == eIO_Close
3820                                  ?      "Closing"  : dir == eIO_Write
3821                                  ?      "Shutting down for write"
3822                                  :      "Shutting down for read/write",
3823                                  (unsigned long) sock->w_len,
3824                                  &"s"[sock->w_len == 1],
3825                                  IO_StatusStr(status)));
3826                 } else if (!(dir & eIO_ReadWrite)  &&  !sock->w_len)
3827                     status = eIO_Success;
3828             }
3829             if (!sock->pending  &&  sock->sslctx) {
3830                 FSSLClose sslclose = s_SSL ? s_SSL->Close : 0;
3831                 if (sslclose) {
3832                     const unsigned int rtv_set = sock->r_tv_set;
3833                     const unsigned int wtv_set = sock->w_tv_set;
3834                     struct timeval rtv;
3835                     struct timeval wtv;
3836                     if (rtv_set)
3837                         rtv = sock->r_tv;
3838                     if (wtv_set)
3839                         wtv = sock->w_tv;
3840                     SOCK_SET_TIMEOUT(sock, r, tv);
3841                     SOCK_SET_TIMEOUT(sock, w, tv);
3842                     status = sslclose(sock->sslctx->sess, how, &error);
3843                     if ((sock->w_tv_set = wtv_set & 1) != 0)
3844                         x_tvcpy(&sock->w_tv, &wtv);
3845                     if ((sock->r_tv_set = rtv_set & 1) != 0)
3846                         x_tvcpy(&sock->r_tv, &rtv);
3847                     if (status != eIO_Success) {
3848                         const char* strerr = s_StrError(sock, error);
3849                         CORE_LOGF_ERRNO_EXX(127, eLOG_Trace,
3850                                             error, strerr ? strerr : "",
3851                                             ("%s[SOCK::%s] "
3852                                              " Failed SSL bye",
3853                                              s_ID(sock, _id),
3854                                              dir & eIO_ReadWrite
3855                                              ? "Shutdown" : "Close"));
3856                         UTIL_ReleaseBuffer(strerr);
3857                     }
3858                 }
3859             }
3860             sock->w_status = eIO_Closed;
3861         }
3862 
3863         sock->w_len = 0;
3864         BUF_Erase(sock->w_buf);
3865         if (dir != eIO_Write) {
3866             sock->eof = 0/*false*/;
3867             sock->r_status = eIO_Closed;
3868             if (!(dir & eIO_ReadWrite))
3869                 return status;
3870         }
3871         break;
3872 
3873     default:
3874         assert(0);
3875         return eIO_InvalidArg;
3876     }
3877     assert((EIO_Event)(dir | eIO_ReadWrite) == eIO_ReadWrite);
3878 
3879 #ifdef NCBI_OS_BSD
3880     /* at least on FreeBSD: shutting down a socket for write (i.e. forcing to
3881      * send a FIN) for a socket that has been already closed by another end
3882      * (e.g. when peer has done writing, so this end has done reading and is
3883      * about to close) seems to cause ECONNRESET in the coming close()...
3884      * see kern/146845 @ http://www.freebsd.org/cgi/query-pr.cgi?pr=146845 */
3885     if (dir == eIO_ReadWrite  &&  how != SOCK_SHUTDOWN_RDWR)
3886         return status;
3887 #endif /*NCBI_OS_BSD*/
3888 
3889 #ifdef NCBI_OS_UNIX
3890     if (sock->path[0])
3891         return status;
3892 #endif /*NCBI_OS_UNIX*/
3893 
3894 #ifndef NCBI_OS_MSWIN
3895     /* on MS-Win, socket shutdown for write apparently messes up (?!)
3896      * with later reading, especially when reading a lot of data... */
3897 
3898     if (s_Initialized > 0  &&  shutdown(sock->sock, how) != 0) {
3899         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
3900         CORE_LOGF_ERRNO_EXX(16, eLOG_Trace,
3901                             error, strerr ? strerr : "",
3902                             ("%s[SOCK::Shutdown] "
3903                              " Failed shutdown(%s)",
3904                              s_ID(sock, _id), dir == eIO_Read ? "R" :
3905                              dir == eIO_Write ? "W" : "RW"));
3906         UTIL_ReleaseBuffer(strerr);
3907         status = eIO_Unknown;
3908     }
3909 #endif /*!NCBI_OS_MSWIN*/
3910 
3911     return status;
3912 }
3913 
3914 
3915 enum {
3916     fSOCK_KeepNone    = 0,
3917     fSOCK_KeepEvent   = 1,
3918     fSOCK_KeepSession = 2,
3919     fSOCK_KeepPending = 4
3920 } ESOCK_Keep;
3921 typedef unsigned int TSOCK_Keep;  /* Bitwise-OR of ESOCK_Keep */
3922 
3923 
3924 /* Close the socket either orderly (abort==0) or abnormally (abort!=0):
3925  * abort == -2 to abort the socket silently;
3926  * abort == -1 to abort the socket internally;
3927  * abort ==  1 to abort the socket from SOCK_Abort();
3928  * abort ==  2 to re-close the socket when SOCK_CreateOnTop*(sock) failed.
3929  */
s_Close_(SOCK sock,int abort,TSOCK_Keep keep)3930 static EIO_Status s_Close_(SOCK sock, int abort, TSOCK_Keep keep)
3931 {
3932     int/*bool*/ linger = 0/*false*/;
3933     char       _id[MAXIDLEN];
3934     EIO_Status status;
3935     int        error;
3936 
3937     assert(abs(abort) <= 2);
3938     assert(sock->sock != SOCK_INVALID);
3939     if (sock->type == eDatagram) {
3940         assert(!abort);
3941         sock->r_len = 0;
3942         status = eIO_Success;
3943         BUF_Erase(sock->r_buf);
3944         BUF_Erase(sock->w_buf);
3945         keep = fSOCK_KeepNone;
3946     } else if ((abort  &&  abort < 2)  ||  !sock->keep) {
3947         keep = fSOCK_KeepNone;
3948 #if (defined(NCBI_OS_UNIX) && !defined(NCBI_OS_BEOS)) || defined(NCBI_OS_MSWIN)
3949         /* setsockopt() is not implemented for MAC (MIT socket emulation lib)*/
3950         if (sock->w_status != eIO_Closed
3951 #  ifdef NCBI_OS_UNIX
3952             &&  !sock->path[0]
3953 #  endif /*NCBI_OS_UNIX*/
3954             ) {
3955             /* set the close()'s linger period be equal to the close timeout */
3956             struct linger lgr;
3957 
3958             if (abort) {
3959                 lgr.l_linger = 0;   /* RFC 793, Abort */
3960                 lgr.l_onoff  = 1;
3961             } else if (!sock->c_tv_set) {
3962                 linger = 1/*true*/;
3963                 lgr.l_linger = 120; /* this is standard TCP TTL, 2 minutes */
3964                 lgr.l_onoff  = 1;
3965             } else if (sock->c_tv.tv_sec | sock->c_tv.tv_usec) {
3966                 int seconds = (int)(sock->c_tv.tv_sec  +
3967                                    (sock->c_tv.tv_usec + 500000) / 1000000);
3968                 if (seconds) {
3969                     linger = 1/*true*/;
3970                     lgr.l_linger = seconds;
3971                     lgr.l_onoff  = 1;
3972                 } else
3973                     lgr.l_onoff  = 0;
3974             } else
3975                 lgr.l_onoff = 0;
3976             if (lgr.l_onoff
3977                 &&  setsockopt(sock->sock, SOL_SOCKET, SO_LINGER,
3978                                (char*) &lgr, sizeof(lgr)) != 0
3979                 &&  abort >= 0  &&  sock->connected) {
3980                 const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
3981                 CORE_LOGF_ERRNO_EXX(17, eLOG_Trace,
3982                                     error, strerr ? strerr : "",
3983                                     ("%s[SOCK::%s] "
3984                                      " Failed setsockopt(SO_LINGER)",
3985                                      s_ID(sock, _id),
3986                                      abort ? "Abort" : "Close"));
3987                 UTIL_ReleaseBuffer(strerr);
3988             }
3989 #  ifdef TCP_LINGER2
3990             if (abort  ||
3991                 (sock->c_tv_set && !(sock->c_tv.tv_sec | sock->c_tv.tv_usec))){
3992                 int no = -1;
3993                 if (setsockopt(sock->sock, IPPROTO_TCP, TCP_LINGER2,
3994                                (char*) &no, sizeof(no)) != 0
3995                     &&  !abort  &&  sock->connected) {
3996                     const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
3997                     CORE_LOGF_ERRNO_EXX(18, eLOG_Trace,
3998                                         error, strerr ? strerr : "",
3999                                         ("%s[SOCK::Close] "
4000                                          " Failed setsockopt(TCP_LINGER2)",
4001                                          s_ID(sock, _id)));
4002                     UTIL_ReleaseBuffer(strerr);
4003                 }
4004             }
4005 #  endif /*TCP_LINGER2*/
4006         }
4007 #endif /*(NCBI_OS_UNIX && !NCBI_OS_BEOS) || NCBI_OS_MSWIN*/
4008 
4009         if (abort) {
4010             sock->eof = 0;
4011             sock->w_len = 0;
4012             sock->pending = 0;
4013             status = eIO_Success;
4014             BUF_Erase(sock->r_buf);
4015             BUF_Erase(sock->w_buf);
4016             sock->r_status = sock->w_status = eIO_Closed;
4017         } else {
4018             /* orderly shutdown in both directions */
4019             status = s_Shutdown(sock, eIO_Close, SOCK_GET_TIMEOUT(sock, c));
4020             assert(sock->r_status == eIO_Closed  &&
4021                    sock->w_status == eIO_Closed);
4022             assert(sock->w_len == 0);
4023         }
4024     } else if (!(keep & fSOCK_KeepPending)) {
4025         /* flush everything out */
4026         status = s_Shutdown(sock, eIO_Open, SOCK_GET_TIMEOUT(sock, c));
4027         assert(sock->w_len == 0);
4028     } else
4029         status = eIO_Success;
4030 
4031     if (!(keep & fSOCK_KeepSession)  &&  sock->sslctx  &&  sock->sslctx->sess){
4032         FSSLDelete ssldelete = s_SSL ? s_SSL->Delete : 0;
4033         if (ssldelete)
4034             ssldelete(sock->sslctx->sess);
4035         sock->sslctx->sess = 0;
4036         sock->sslctx->sock = 0;
4037     }
4038 
4039     if (abs(abort) <= 1) {
4040         /* statistics & logging */
4041         if (sock->type != eDatagram) {
4042             sock->n_in  += sock->n_read;
4043             sock->n_out += sock->n_written;
4044         }
4045         if (sock->log == eOn  ||  (sock->log == eDefault  &&  s_Log == eOn))
4046             s_DoLog(eLOG_Note, sock, eIO_Close, 0, 0, abort ? "Aborting" : 0);
4047     }
4048 
4049 #ifdef NCBI_OS_MSWIN
4050     if (!(keep & fSOCK_KeepEvent))
4051         WSAEventSelect(sock->sock, sock->event/*ignored*/, 0/*de-associate*/);
4052 #endif /*NCBI_OS_MSWIN*/
4053 
4054     if (s_Initialized > 0  &&  !sock->keep
4055         /* set the socket back to blocking mode */
4056         &&  linger  &&  !s_SetNonblock(sock->sock, 0/*false*/)) {
4057         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
4058         assert(!abort);
4059         CORE_LOGF_ERRNO_EXX(19, eLOG_Trace,
4060                             error, strerr ? strerr : "",
4061                             ("%s[SOCK::Close] "
4062                              " Cannot set socket back to blocking mode",
4063                              s_ID(sock, _id)));
4064         UTIL_ReleaseBuffer(strerr);
4065     }
4066 
4067     if ((abort  &&  abort < 2)  ||  !sock->keep) {
4068         TSOCK_Handle fd = sock->sock;
4069         if (abort)
4070             abort = 1;
4071 #ifdef NCBI_MONKEY
4072         /* Not interception of close():  only to "forget" this socket */
4073         if (g_MONKEY_Close)
4074             g_MONKEY_Close(fd);
4075 #endif /*NCBI_MONKEY*/
4076         for (;;) { /* close persistently - retry if interrupted by a signal */
4077             if (SOCK_CLOSE(fd) == 0)
4078                 break;
4079             /* error */
4080             if (s_Initialized <= 0)
4081                 break;
4082             error = SOCK_ERRNO;
4083 #ifdef NCBI_OS_MSWIN
4084             if (error == WSANOTINITIALISED) {
4085                 s_Initialized = -1/*deinited*/;
4086                 break;
4087             }
4088 #endif /*NCBI_OS_MSWIN*/
4089             if (error == SOCK_ENOTCONN/*already closed by now*/
4090                 ||  (!(sock->n_read | sock->n_written)
4091                      &&  (error == SOCK_ENETRESET   ||
4092                           error == SOCK_ECONNRESET  ||
4093                           error == SOCK_ECONNABORTED))) {
4094                 break;
4095             }
4096             if (abort  ||  error != SOCK_EINTR) {
4097                 const char* strerr = SOCK_STRERROR(error);
4098                 /* NB: s_ID() won't show fd here since it's INVALID by now */
4099                 CORE_LOGF_ERRNO_EXX(21, abort == 1 ? eLOG_Warning : eLOG_Error,
4100                                     error, strerr ? strerr : "",
4101                                     ("%s[SOCK::%s] "
4102                                      " Failed close()",
4103                                      s_ID(sock, _id),
4104                                      abort ? "Abort" : "Close"));
4105                 UTIL_ReleaseBuffer(strerr);
4106                 if (abort > 1  ||  error != SOCK_EINTR) {
4107                     status =
4108                         error == SOCK_ETIMEDOUT ? eIO_Timeout : eIO_Unknown;
4109                     break;
4110                 }
4111                 if (abort)
4112                     abort = 2;
4113             }
4114         }
4115         sock->sock = SOCK_INVALID;
4116 #ifdef NCBI_OS_MSWIN
4117         WSASetEvent(sock->event); /*signal closure*/
4118 #endif /*NCBI_OS_MSWIN*/
4119     } else
4120         sock->sock = SOCK_INVALID;
4121 
4122 #ifdef NCBI_OS_MSWIN
4123     if (!(keep & fSOCK_KeepEvent)) {
4124         WSACloseEvent(sock->event);
4125         sock->event = 0;
4126     }
4127 #endif /*NCBI_OS_MSWIN*/
4128     sock->myport = 0;
4129     return status;
4130 }
4131 
4132 
s_Close(SOCK sock,int reclose,TSOCK_Keep keep)4133 static EIO_Status s_Close(SOCK sock, int/*bool*/ reclose, TSOCK_Keep keep)
4134 {
4135     EIO_Status status;
4136 
4137     assert(!reclose  ||  !keep);
4138     status = s_Close_(sock, reclose << 1, keep);
4139     if (s_ErrHook  &&  status != eIO_Success) {
4140         SSOCK_ErrInfo info;
4141         char          addr[40];
4142         memset(&info, 0, sizeof(info));
4143         info.type = eSOCK_ErrIO;
4144         info.sock = sock;
4145         if (sock->port) {
4146             SOCK_ntoa(sock->host, addr, sizeof(addr));
4147             info.host =       addr;
4148             info.port = sock->port;
4149         }
4150 #ifdef NCBI_OS_UNIX
4151         else
4152             info.host = sock->path;
4153 #endif /*NCBI_OS_UNIX*/
4154         info.event = eIO_Close;
4155         info.status = status;
4156         s_ErrorCallback(&info);
4157     }
4158 
4159     assert(sock->sock == SOCK_INVALID);
4160     return status;
4161 }
4162 
4163 
4164 /* Connect the (pre-allocated) socket to the specified "host:port"/"file" peer.
4165  * HINT: if "host" is NULL then keep the original host;
4166  *       likewise for zero "port".
4167  * NOTE: Client-side stream sockets only.
4168  */
s_Connect_(SOCK sock,const char * host,unsigned short port,const STimeout * timeout)4169 static EIO_Status s_Connect_(SOCK            sock,
4170                              const char*     host,
4171                              unsigned short  port,
4172                              const STimeout* timeout)
4173 {
4174     union {
4175         struct sockaddr    sa;
4176         struct sockaddr_in in;
4177 #ifdef NCBI_OS_UNIX
4178         struct sockaddr_un un;
4179 #endif /*NCBI_OS_UNIX*/
4180     } addr;
4181     char            _id[MAXIDLEN];
4182     TSOCK_socklen_t addrlen;
4183     SNcbiSSLctx*    sslctx;
4184     EIO_Status      status;
4185     int             error;
4186     int             type;
4187     TSOCK_Handle    fd;
4188     int             n;
4189 
4190     assert(sock->sock == SOCK_INVALID);
4191     assert(sock->type == eSocket  &&  sock->side == eSOCK_Client);
4192 
4193     sslctx = sock->sslctx;
4194     /* initialize internals */
4195     if (s_InitAPI(sslctx ? 1/*secure*/ : 0/*regular*/) != eIO_Success)
4196         return eIO_NotSupported;
4197 
4198     memset(&addr, 0, sizeof(addr));
4199 #ifdef NCBI_OS_UNIX
4200     if (sock->path[0]) {
4201         size_t pathlen = strlen(sock->path);
4202         if (sizeof(addr.un.sun_path) <= pathlen++/*account for '\0'*/) {
4203             CORE_LOGF_X(142, eLOG_Error,
4204                         ("%s[SOCK::Connect] "
4205                          " Path too long (%lu vs %lu bytes allowed)",
4206                          s_ID(sock, _id), (unsigned long) pathlen,
4207                          (unsigned long) sizeof(addr.un.sun_path)));
4208             return eIO_InvalidArg;
4209         }
4210         addrlen = (TSOCK_socklen_t) sizeof(addr.un);
4211 #  ifdef HAVE_SIN_LEN
4212         addr.un.sun_len    = addrlen;
4213 #  endif /*HAVE_SIN_LEN*/
4214         addr.un.sun_family = AF_UNIX;
4215         memcpy(addr.un.sun_path, sock->path, pathlen);
4216         assert(!sock->port  &&  !port);
4217     } else
4218 #endif /*NCBI_OS_UNIX*/
4219     {
4220         /* first, set the port to connect to (same port if zero) */
4221         if (port)
4222             sock->port = port;
4223         else
4224             assert(sock->port);
4225         /* get address of the remote host (assume the same host if NULL) */
4226         if (host
4227             &&  !(sock->host = s_gethostbyname(host, 0, (ESwitch)sock->log))) {
4228             CORE_LOGF_X(22, eLOG_Error,
4229                         ("%s[SOCK::Connect] "
4230                          " Failed SOCK_gethostbyname(\"%.*s\")",
4231                          s_ID(sock, _id), CONN_HOST_LEN, host));
4232             return eIO_Unknown;
4233         }
4234         addrlen = (TSOCK_socklen_t) sizeof(addr.in);
4235 #ifdef HAVE_SIN_LEN
4236         addr.in.sin_len         = addrlen;
4237 #endif /*HAVE_SIN_LEN*/
4238         addr.in.sin_family      = AF_INET;
4239         addr.in.sin_addr.s_addr =       sock->host;
4240         addr.in.sin_port        = htons(sock->port);
4241     }
4242 
4243     /* create a new socket */
4244     type  = SOCK_STREAM;
4245 #ifdef SOCK_NONBLOCK
4246     type |= SOCK_NONBLOCK;
4247 #endif /*SOCK_NONBLOCK*/
4248 #ifdef SOCK_CLOEXEC
4249     if (!sock->crossexec  ||  sslctx)
4250         type |= SOCK_CLOEXEC;
4251 #endif /*SOCK_CLOEXEC*/
4252     if ((fd = socket(addr.sa.sa_family, type, 0)) == SOCK_INVALID) {
4253         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
4254         CORE_LOGF_ERRNO_EXX(23, eLOG_Error,
4255                             error, strerr ? strerr : "",
4256                             ("%s[SOCK::Connect] "
4257                              " Cannot create socket",
4258                              s_ID(sock, _id)));
4259         UTIL_ReleaseBuffer(strerr);
4260         return eIO_Unknown;
4261     }
4262     sock->sock = fd;
4263 
4264 #ifdef NCBI_MONKEY
4265     /* Bind created fd to the sock in Chaos Monkey, this information is
4266        important to keep rules working */
4267     if (g_MONKEY_SockHasSocket)
4268         g_MONKEY_SockHasSocket(sock, fd);
4269 #endif /*NCBI_MONKEY*/
4270 
4271 #if defined(NCBI_OS_MSWIN)
4272     assert(!sock->event);
4273     if (!(sock->event = WSACreateEvent())) {
4274         DWORD err = GetLastError();
4275         const char* strerr = s_WinStrerror(err);
4276         CORE_LOGF_ERRNO_EXX(122, eLOG_Error,
4277                             err, strerr ? strerr : "",
4278                             ("%s[SOCK::Connect] "
4279                              " Failed to create IO event",
4280                              s_ID(sock, _id)));
4281         UTIL_ReleaseBufferOnHeap(strerr);
4282         s_Close_(sock, -2/*silent abort*/, fSOCK_KeepNone);
4283         return eIO_Unknown;
4284     }
4285     /* NB: WSAEventSelect() sets non-blocking automatically */
4286     if (WSAEventSelect(sock->sock, sock->event, SOCK_EVENTS) != 0) {
4287         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
4288         CORE_LOGF_ERRNO_EXX(123, eLOG_Error,
4289                             error, strerr ? strerr : "",
4290                             ("%s[SOCK::Connect] "
4291                              " Failed to bind IO event",
4292                              s_ID(sock, _id)));
4293         UTIL_ReleaseBuffer(strerr);
4294         s_Close_(sock, -2/*silent abort*/, fSOCK_KeepNone);
4295         return eIO_Unknown;
4296     }
4297 #elif !defined(SOCK_NONBLOCK)
4298     /* set non-blocking mode */
4299     if (!s_SetNonblock(fd, 1/*true*/)) {
4300         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
4301         CORE_LOGF_ERRNO_EXX(24, eLOG_Error,
4302                             error, strerr ? strerr : "",
4303                             ("%s[SOCK::Connect] "
4304                              " Cannot set socket to non-blocking mode",
4305                              s_ID(sock, _id)));
4306         UTIL_ReleaseBuffer(strerr);
4307         s_Close_(sock, -2/*silent abort*/, fSOCK_KeepNone);
4308         return eIO_Unknown;
4309     }
4310 #endif
4311 
4312     if (sock->port) {
4313 #ifdef SO_KEEPALIVE
4314         if (sock->keepalive  &&  !s_SetKeepAlive(fd, 1/*true*/)) {
4315             const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
4316             CORE_LOGF_ERRNO_EXX(151, eLOG_Warning,
4317                                 error, strerr ? strerr : "",
4318                                 ("%s[SOCK::Connect] "
4319                                  " Failed setsockopt(KEEPALIVE)",
4320                                  s_ID(sock, _id)));
4321             UTIL_ReleaseBuffer(strerr);
4322         }
4323 #endif /*SO_KEEPALIVE*/
4324 #ifdef SO_OOBINLINE
4325         if (!s_SetOobInline(fd, 1/*true*/)) {
4326             const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
4327             CORE_LOGF_ERRNO_EXX(135, eLOG_Warning,
4328                                 error, strerr ? strerr : "",
4329                                 ("%s[SOCK::Connect] "
4330                                  " Failed setsockopt(OOBINLINE)",
4331                                  s_ID(sock, _id)));
4332             UTIL_ReleaseBuffer(strerr);
4333         }
4334 #endif /*SO_OOBINLINE*/
4335     }
4336 
4337 #ifndef SOCK_CLOEXEC
4338     if ((!sock->crossexec  ||  sslctx)  &&  !s_SetCloexec(fd, 1)) {
4339         const char* strerr;
4340 #  ifdef NCBI_OS_MSWIN
4341         DWORD err = GetLastError();
4342         strerr = s_WinStrerror(err);
4343         error = err;
4344 #  else
4345         error = errno;
4346         strerr = SOCK_STRERROR(error);
4347 #  endif /*NCBI_OS_MSWIN*/
4348         CORE_LOGF_ERRNO_EXX(129, eLOG_Warning,
4349                             error, strerr ? strerr : "",
4350                             ("%s[SOCK::Connect] "
4351                              " Cannot set socket close-on-exec mode",
4352                              s_ID(sock, _id)));
4353 #  ifdef NCBI_OS_MSWIN
4354         UTIL_ReleaseBufferOnHeap(strerr);
4355 #  else
4356         UTIL_ReleaseBuffer(strerr);
4357 #  endif /*NCBI_OS_MSWIN*/
4358     }
4359 #endif /*!SOCK_CLOEXEC*/
4360 
4361     if (sslctx) {
4362         FSSLCreate sslcreate = s_SSL ? s_SSL->Create : 0;
4363         assert(!sslctx->sess);
4364         assert(!sslctx->host  ||  *sslctx->host);
4365         if (sslcreate) {
4366             sslctx->sock = sock;
4367             sslctx->sess = sslcreate(eSOCK_Client, sslctx, &error);
4368         } else
4369             error = 0;
4370         if (!sslctx->sess) {
4371             const char* strerr = s_StrError(sock, error);
4372             CORE_LOGF_ERRNO_EXX(131, eLOG_Error,
4373                                 error, strerr ? strerr : "",
4374                                 ("%s[SOCK::Connect] "
4375                                  " %s to initialize secure session%s%s",
4376                                  s_ID(sock, _id),
4377                                  sslcreate ? "Failed" : "Unable",
4378                                  sslctx->host ? " with "     : "",
4379                                  sslctx->host ? sslctx->host : 0));
4380             UTIL_ReleaseBuffer(strerr);
4381             s_Close_(sock, -2/*silent abort*/, fSOCK_KeepNone);
4382             return eIO_NotSupported;
4383         }
4384     }
4385 
4386     /* establish connection to the peer */
4387     sock->eof       = 0/*false*/;
4388     sock->r_status  = eIO_Success;
4389     sock->w_status  = eIO_Success;
4390     sock->pending   = 1/*true*/;
4391     sock->connected = 0/*false*/;
4392 #ifdef NCBI_OS_MSWIN
4393     sock->readable  = 0/*false*/;
4394     sock->writable  = 0/*false*/;
4395     sock->closing   = 0/*false*/;
4396 #endif /*NCBI_OS_MSWIN*/
4397     assert(sock->w_len == 0);
4398     for (n = 0;  ; n = 1) { /* optionally auto-resume if interrupted */
4399         if (connect(fd, &addr.sa, addrlen) == 0) {
4400             error = 0;
4401             break;
4402         }
4403         error = SOCK_ERRNO;
4404         if (error != SOCK_EINTR  ||  sock->i_on_sig == eOn
4405             ||  (sock->i_on_sig == eDefault  &&  s_InterruptOnSignal == eOn)) {
4406             break;
4407         }
4408     }
4409 
4410     /* statistics & logging */
4411     if (sock->log == eOn  ||  (sock->log == eDefault  &&  s_Log == eOn))
4412         s_DoLog(eLOG_Note, sock, eIO_Open, 0, 0, error ? 0 : "Connected");
4413 
4414     if (error) {
4415         if (((n == 0  &&  error != SOCK_EINPROGRESS)  ||
4416              (n != 0  &&  error != SOCK_EALREADY))
4417             &&  error != SOCK_EWOULDBLOCK) {
4418             if (error != SOCK_EINTR) {
4419                 const char* strerr = SOCK_STRERROR(error);
4420                 CORE_LOGF_ERRNO_EXX(25, sock->log != eOff
4421                                     ? eLOG_Error : eLOG_Trace,
4422                                     error, strerr ? strerr : "",
4423                                     ("%s[SOCK::Connect] "
4424                                      " Failed connect()",
4425                                      s_ID(sock, _id)));
4426                 UTIL_ReleaseBuffer(strerr);
4427                 if (error == SOCK_ECONNREFUSED
4428 #ifdef NCBI_OS_UNIX
4429                     ||  (sock->path[0]  &&  error == ENOENT)
4430 #endif /*NCBI_OS_UNIX*/
4431                     ) {
4432                     status = eIO_Closed;
4433                 } else
4434                     status = eIO_Unknown;
4435             } else
4436                 status = eIO_Interrupt;
4437             s_Close_(sock, -1/*internal abort*/, fSOCK_KeepNone);
4438             return status/*error*/;
4439         }
4440     }
4441 
4442     if (!error  ||  !timeout  ||  (timeout->sec | timeout->usec)) {
4443         const char* what;
4444         struct timeval tv;
4445         const struct timeval* x_tv = s_to2tv(timeout, &tv);
4446 
4447         status = s_IsConnected_(sock, x_tv, &what, &error, !error);
4448         if (status != eIO_Success) {
4449             char buf[80];
4450             const char* reason;
4451             if (status == eIO_Timeout) {
4452                 assert(x_tv/*it is also normalized*/);
4453                 sprintf(buf, "%s[%u.%06u]",
4454                         IO_StatusStr(status),
4455                         (unsigned int) x_tv->tv_sec,
4456                         (unsigned int) x_tv->tv_usec);
4457                 reason = buf;
4458             } else
4459                 reason = IO_StatusStr(status);
4460             {
4461                 const char* strerr = s_StrError(sock, error);
4462                 CORE_LOGF_ERRNO_EXX(26, sock->log != eOff
4463                                     ? eLOG_Error : eLOG_Trace,
4464                                     error, strerr ? strerr : "",
4465                                     ("%s[SOCK::Connect] "
4466                                      " Failed %s: %s", s_ID(sock, _id),
4467                                      what ? what : "pending connect()",
4468                                      reason));
4469                 UTIL_ReleaseBuffer(strerr);
4470             }
4471             s_Close_(sock, -1/*internal abort*/, fSOCK_KeepNone);
4472             return status;
4473         }
4474     }
4475 
4476     /* success: do not change any timeouts */
4477     sock->w_len = BUF_Size(sock->w_buf);
4478     return eIO_Success;
4479 }
4480 
4481 
s_Connect(SOCK sock,const char * host,unsigned short port,const STimeout * timeout)4482 static EIO_Status s_Connect(SOCK            sock,
4483                             const char*     host,
4484                             unsigned short  port,
4485                             const STimeout* timeout)
4486 {
4487     EIO_Status status = s_Connect_(sock, host, port, timeout);
4488     if (s_ErrHook  &&  status != eIO_Success) {
4489         SSOCK_ErrInfo info;
4490         char          addr[40];
4491         memset(&info, 0, sizeof(info));
4492         info.type = eSOCK_ErrIO;
4493         info.sock = sock;
4494         if (!host) {
4495 #ifdef NCBI_OS_UNIX
4496             if (sock->path[0]) {
4497                 assert(!sock->port  &&  !port);
4498                 info.host = sock->path;
4499             } else
4500 #endif /*NCBI_OS_UNIX*/
4501             {
4502                 SOCK_ntoa(sock->host, addr, sizeof(addr));
4503                 info.host = addr;
4504             }
4505         } else
4506             info.host = host;
4507         info.port = port ? port : sock->port;
4508         info.event = eIO_Open;
4509         info.status = status;
4510         s_ErrorCallback(&info);
4511     }
4512     return status;
4513 }
4514 
4515 
s_Create(const char * hostpath,unsigned short port,const STimeout * timeout,SOCK * sock,const SSOCK_Init * init,TSOCK_Flags flags)4516 static EIO_Status s_Create(const char*       hostpath,
4517                            unsigned short    port,
4518                            const STimeout*   timeout,
4519                            SOCK*             sock,
4520                            const SSOCK_Init* init,
4521                            TSOCK_Flags       flags)
4522 {
4523     size_t       size = port ? 0 : strlen(hostpath);
4524     unsigned int x_id = ++s_ID_Counter * 1000;
4525     char         _id[MAXIDLEN];
4526     EIO_Status   status;
4527     SOCK         x_sock;
4528 
4529     assert(!*sock);
4530 
4531     /* allocate memory for the internal socket structure(s) */
4532     if (!(x_sock = (SOCK) calloc(1, sizeof(*x_sock) + size)))
4533         return eIO_Unknown;
4534     if (flags & fSOCK_Secure) {
4535         SNcbiSSLctx* sslctx;
4536         const char* host = init ? init->host : 0;
4537         if (port  &&  !host)
4538             host = hostpath;
4539         if (SOCK_isip(host))
4540             host = 0;
4541         if (!(sslctx = (SNcbiSSLctx*) calloc(1, sizeof(*sslctx)))) {
4542             free(x_sock);
4543             return eIO_Unknown;
4544         }
4545         sslctx->host = host  &&  *host ? strdup(host) : 0;
4546         sslctx->cred = init ? init->cred : 0;
4547         x_sock->sslctx = sslctx;
4548     }
4549     x_sock->sock      = SOCK_INVALID;
4550     x_sock->id        = x_id;
4551     x_sock->type      = eSocket;
4552     x_sock->side      = eSOCK_Client;
4553     x_sock->log       = flags & (fSOCK_LogDefault | fSOCK_LogOn);
4554     x_sock->keep      = flags & fSOCK_KeepOnClose ? 1/*true*/ : 0/*false*/;
4555     x_sock->r_on_w    = flags & fSOCK_ReadOnWrite       ? eOn : eDefault;
4556     x_sock->i_on_sig  = flags & fSOCK_InterruptOnSignal ? eOn : eDefault;
4557     x_sock->crossexec = flags & fSOCK_KeepOnExec  ? 1/*true*/ : 0/*false*/;
4558     x_sock->keepalive = flags & fSOCK_KeepAlive   ? 1/*true*/ : 0/*false*/;
4559 #ifdef NCBI_OS_UNIX
4560     if (!port)
4561         memcpy(x_sock->path, hostpath, size + 1);
4562 #endif /*NCBI_OS_UNIX*/
4563 
4564     /* setup initial data */
4565     size = init ? init->size : 0;
4566     BUF_SetChunkSize(&x_sock->r_buf, SOCK_BUF_CHUNK_SIZE);
4567     if (size
4568         &&  (BUF_SetChunkSize(&x_sock->w_buf, size) < size
4569              ||  !BUF_Write(&x_sock->w_buf, init ? init->data : 0, size))) {
4570         CORE_LOGF_ERRNO_X(27, eLOG_Error, errno,
4571                           ("%s[SOCK::Create] "
4572                            " Cannot store initial data",
4573                            s_ID(x_sock, _id)));
4574         SOCK_Destroy(x_sock);
4575         return eIO_Unknown;
4576     }
4577 
4578     /* connect */
4579     status = s_Connect(x_sock, hostpath, port, timeout);
4580     if (status != eIO_Success)
4581         SOCK_Destroy(x_sock);
4582     else
4583         *sock = x_sock;
4584     return status;
4585 }
4586 
4587 
4588 /* Mimic SOCK_CLOSE() */
SOCK_ABORT(TSOCK_Handle x_sock)4589 static void SOCK_ABORT(TSOCK_Handle x_sock)
4590 {
4591     struct SOCK_tag temp;
4592     memset(&temp, 0, sizeof(temp));
4593     temp.side = eSOCK_Server;
4594     temp.type = eSocket;
4595     temp.sock = x_sock;
4596     s_Close_(&temp, -2/*silent abort*/, fSOCK_KeepNone);
4597 }
4598 
4599 
s_CreateOnTop(const void * handle,size_t handle_size,SOCK * sock,const SSOCK_Init * init,TSOCK_Flags flags)4600 static EIO_Status s_CreateOnTop(const void*       handle,
4601                                 size_t            handle_size,
4602                                 SOCK*             sock,
4603                                 const SSOCK_Init* init,
4604                                 TSOCK_Flags       flags)
4605 {
4606     union {
4607         struct sockaddr    sa;
4608         struct sockaddr_in in;
4609 #ifdef NCBI_OS_UNIX
4610         struct sockaddr_un un;
4611 #endif /*NCBI_OS_UNIX*/
4612     } peer;
4613     size_t          size;
4614     int             error;
4615     EIO_Status      status;
4616     TSOCK_socklen_t peerlen;
4617     size_t          socklen;
4618 #ifdef NCBI_OS_MSWIN
4619     WSAEVENT        event = 0;
4620 #endif /*NCBI_OS_MSWIN*/
4621     BUF             w_buf = 0;
4622     unsigned short  myport = 0;
4623     char            _id[MAXIDLEN];
4624     SOCK            x_sock, x_orig = 0;
4625     SNcbiSSLctx*    sslctx = 0, *oldctx = 0;
4626     TSOCK_Handle    fd, oldfd = SOCK_INVALID;
4627     unsigned int    x_id = ++s_ID_Counter * 1000;
4628 
4629     assert(!*sock);
4630 
4631     if (!handle  ||  (handle_size  &&  handle_size != sizeof(fd))) {
4632         CORE_LOGF_X(47, eLOG_Error,
4633                     ("SOCK#%u[?]: [SOCK::CreateOnTop] "
4634                      " Invalid handle%s %lu",
4635                      x_id,
4636                      handle ? " size"                     : "",
4637                      handle ? (unsigned long) handle_size : 0));
4638         assert(0);
4639         return eIO_InvalidArg;
4640     }
4641 
4642     if (!handle_size) {
4643         TSOCK_Keep keep = fSOCK_KeepEvent;
4644         x_orig = (SOCK) handle;
4645         if (x_orig->type != eSocket) {
4646             assert(0);
4647             return eIO_InvalidArg;
4648         }
4649         fd = x_orig->sock;
4650         if (s_Initialized <= 0  ||  fd == SOCK_INVALID)
4651             return eIO_Closed;
4652         if (!x_orig->keep) {
4653             x_orig->keep = 1/*true*/;
4654             oldfd = fd;
4655         }
4656         if (!x_orig->sslctx == !(flags & fSOCK_Secure)) {
4657             keep |= fSOCK_KeepPending;
4658             if (flags & fSOCK_Secure) {
4659                 keep |= fSOCK_KeepSession;
4660                 oldctx = x_orig->sslctx;
4661             }
4662         }
4663         myport = x_orig->myport;
4664         s_Close(x_orig, 0, keep);
4665 #ifdef NCBI_OS_MSWIN
4666         event = x_orig->event;
4667         x_orig->event = 0;
4668         assert(event);
4669 #endif/*NCBI_OS_MSWIN*/
4670         if (oldfd != SOCK_INVALID)
4671             x_orig->keep = 0/*false*/;
4672     } else
4673         memcpy(&fd, handle, sizeof(fd));
4674 
4675     /* initialize internals */
4676     if (s_InitAPI(flags & fSOCK_Secure) != eIO_Success) {
4677         status = eIO_NotSupported;
4678         goto errout;
4679     }
4680 
4681     /* get peer's address */
4682     memset(&peer, 0, sizeof(peer));
4683     if (!x_orig) {
4684         peerlen = (TSOCK_socklen_t) sizeof(peer);
4685 #ifdef HAVE_SIN_LEN
4686         peer.sa.sa_len = peerlen;
4687 #endif /*HAVE_SIN_LEN*/
4688         if (getpeername(fd, &peer.sa, &peerlen) != 0) {
4689             const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
4690             CORE_LOGF_ERRNO_EXX(148, eLOG_Error,
4691                                 error, strerr ? strerr : "",
4692                                 ("SOCK#%u[%u]: [SOCK::CreateOnTop] "
4693                                  " %s %s handle",
4694                                  x_id, (unsigned int) fd,
4695                                  error == SOCK_ENOTCONN
4696                                  ? "Unconnected" : "Invalid",
4697                                  x_orig ? "SOCK" : "OS socket"));
4698             UTIL_ReleaseBuffer(strerr);
4699             return eIO_Closed;
4700         }
4701 #ifdef NCBI_OS_UNIX
4702         if (peer.sa.sa_family != AF_INET  &&  peer.sa.sa_family != AF_UNIX)
4703 #  if defined(NCBI_OS_BSD)     ||  \
4704       defined(NCBI_OS_DARWIN)  ||  \
4705       defined(NCBI_OS_IRIX)
4706             if (peer.sa.sa_family != AF_UNSPEC/*0*/)
4707 #  endif /*NCBI_OS_???*/
4708 #else
4709         if (peer.sa.sa_family != AF_INET)
4710 #endif /*NCBI_OS_UNIX*/
4711             return eIO_NotSupported;
4712     }
4713 
4714 #ifdef NCBI_OS_UNIX
4715     if (x_orig  &&  x_orig->path[0])
4716         socklen = strlen(x_orig->path);
4717     else if (!x_orig  &&  (
4718 #  if defined(NCBI_OS_BSD)     ||  \
4719       defined(NCBI_OS_DARWIN)  ||  \
4720       defined(NCBI_OS_IRIX)
4721                              peer.sa.sa_family == AF_UNSPEC/*0*/  ||
4722 #  endif /*NCBI_OS*/
4723                              peer.sa.sa_family == AF_UNIX)) {
4724         if (peerlen == sizeof(peer.sa.sa_family)  ||  !peer.un.sun_path[0]) {
4725             peerlen = (TSOCK_socklen_t) sizeof(peer);
4726             memset(&peer, 0, sizeof(peer));
4727 #  ifdef HAVE_SIN_LEN
4728             peer.sa.sa_len = peerlen;
4729 #  endif /*HAVE_SIN_LEN*/
4730             if (getsockname(fd, &peer.sa, &peerlen) != 0)
4731                 return eIO_Closed;
4732             assert(peer.sa.sa_family == AF_UNIX);
4733             if (peerlen == sizeof(peer.sa.sa_family) || !peer.un.sun_path[0]) {
4734                 CORE_LOGF_X(48, eLOG_Error,
4735                             ("SOCK#%u[%u]: [SOCK::CreateOnTop] "
4736                              " %s UNIX socket handle",
4737                              x_id, (unsigned int) fd,
4738                              peerlen == sizeof(peer.sa.sa_family)
4739                              ? "Unnamed" : "Abstract"));
4740                 return eIO_InvalidArg;
4741             }
4742         }
4743         socklen = strnlen(peer.un.sun_path,sizeof(peer.un.sun_path));
4744         assert(socklen);
4745     } else
4746 #endif /*NCBI_OS_UNIX*/
4747     {
4748         socklen = 0;
4749         assert(!x_orig  ||  x_orig->port);
4750     }
4751 
4752 #ifdef NCBI_OS_MSWIN
4753     if (!event) {
4754         assert(!x_orig);
4755         if (!(event = WSACreateEvent())) {
4756             DWORD err = GetLastError();
4757             const char* strerr = s_WinStrerror(err);
4758             CORE_LOGF_ERRNO_EXX(31, eLOG_Error,
4759                                 err, strerr ? strerr : "",
4760                                 ("SOCK#%u[%u]: [SOCK::CreateOnTop] "
4761                                  " Failed to create IO event",
4762                                  x_id, (unsigned int) fd));
4763             UTIL_ReleaseBufferOnHeap(strerr);
4764             return eIO_Unknown;
4765         }
4766         /* NB: WSAEventSelect() sets non-blocking automatically */
4767         if (WSAEventSelect(fd, event, SOCK_EVENTS) != 0) {
4768             const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
4769             CORE_LOGF_ERRNO_EXX(32, eLOG_Error,
4770                                 error, strerr ? strerr : "",
4771                                 ("SOCK#%u[%u]: [SOCK::CreateOnTop] "
4772                                  " Failed to bind IO event",
4773                                  x_id, (unsigned int) fd));
4774             UTIL_ReleaseBuffer(strerr);
4775             return eIO_Unknown;
4776         }
4777     } else
4778         assert(x_orig);
4779 #else
4780     /* set to non-blocking mode */
4781     if (!x_orig  &&  !s_SetNonblock(fd, 1/*true*/)) {
4782         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
4783         CORE_LOGF_ERRNO_EXX(50, eLOG_Error,
4784                             error, strerr ? strerr : "",
4785                             ("SOCK#%u[%u]: [SOCK::CreateOnTop] "
4786                              " Cannot set socket to non-blocking mode",
4787                              x_id, (unsigned int) fd));
4788         UTIL_ReleaseBuffer(strerr);
4789         return eIO_Unknown;
4790     }
4791 #endif /*NCBI_OS_MSWIN*/
4792 
4793     status = eIO_Unknown;
4794 
4795     /* setup initial data */
4796     size = init ? init->size : 0;
4797     if (size
4798         &&  (BUF_SetChunkSize(&w_buf, size) < size
4799              ||  !BUF_Write(&w_buf, init ? init->data : 0, size))) {
4800         CORE_LOGF_ERRNO_X(49, eLOG_Error, errno,
4801                           ("SOCK#%u[%u]: [SOCK::CreateOnTop] "
4802                            " Cannot store initial data",
4803                            x_id, (unsigned int) fd));
4804         goto errout;
4805     }
4806 
4807     /* create and fill in a socket handle */
4808     if ((flags & fSOCK_Secure)
4809         &&  !(sslctx = (SNcbiSSLctx*) calloc(1, sizeof(*sslctx)))) {
4810         goto errout;
4811     }
4812     if (!(x_sock = (SOCK) calloc(1, sizeof(*x_sock) + socklen)))
4813         goto errout;
4814     x_sock->sock      = fd;
4815     x_sock->id        = x_id;
4816 #ifdef NCBI_OS_UNIX
4817     if (socklen) {
4818         strncpy0(x_sock->path,
4819                  x_orig ? x_orig->path : peer.un.sun_path, socklen);
4820     } else
4821 #endif /*NCBI_OS_UNIX*/
4822     {
4823         assert(x_orig  ||  peer.sa.sa_family == AF_INET);
4824         x_sock->host  = x_orig ? x_orig->host :       peer.in.sin_addr.s_addr;
4825         x_sock->port  = x_orig ? x_orig->port : ntohs(peer.in.sin_port);
4826         assert(x_sock->port);
4827     }
4828     x_sock->myport    = myport;
4829     x_sock->type      = eSocket;
4830     x_sock->side      = x_orig ? x_orig->side : eSOCK_Server;
4831     x_sock->log       = flags & (fSOCK_LogDefault | fSOCK_LogOn);
4832     x_sock->keep      = flags & fSOCK_KeepOnClose ? 1/*true*/ : 0/*false*/;
4833     x_sock->r_on_w    = flags & fSOCK_ReadOnWrite       ? eOn : eDefault;
4834     x_sock->i_on_sig  = flags & fSOCK_InterruptOnSignal ? eOn : eDefault;
4835     x_sock->pending   = 1/*have to check at the nearest I/O*/;
4836  #ifdef NCBI_OS_MSWIN
4837     x_sock->event     = event;
4838     x_sock->writable  = 1/*true*/;
4839 #endif /*NCBI_OS_MSWIN*/
4840     x_sock->connected = x_orig ? x_orig->connected            : 0/*false*/;
4841     x_sock->crossexec = flags & fSOCK_KeepOnExec  ? 1/*true*/ : 0/*false*/;
4842     x_sock->keepalive = flags & fSOCK_KeepAlive   ? 1/*true*/ : 0/*false*/;
4843     /* all timeout bits zeroed - infinite */
4844     x_sock->w_buf     = w_buf;
4845     if (oldctx  &&  sslctx) {
4846         NCBI_CRED   cred;
4847         const char* host;
4848         if (!oldctx->sess  &&  init) {
4849             cred = init->cred;
4850             host = SOCK_isip(init->host) ? 0 : init->host;
4851         } else {
4852             cred = oldctx->cred;
4853             host = oldctx->host;
4854         }
4855         x_sock->sslctx = oldctx;
4856         x_sock->sslctx->sock = x_sock;
4857         x_orig->sslctx = sslctx;
4858         x_orig->sslctx->cred = oldctx->cred;
4859         x_orig->sslctx->host = oldctx->host;
4860         x_sock->sslctx->cred = cred;
4861         x_sock->sslctx->host = host  &&  *host ? strdup(host) : 0;
4862         sslctx = x_sock->sslctx;
4863     } else if (sslctx) {
4864         const char* host;
4865         if (init)
4866             host = SOCK_isip(init->host) ? 0 : init->host;
4867         else
4868             host = 0;
4869         x_sock->sslctx = sslctx;
4870         x_sock->sslctx->sock = x_sock;
4871         x_sock->sslctx->cred = init ? init->cred : 0;
4872         x_sock->sslctx->host = host  &&  *host ? strdup(host) : 0;
4873     }
4874 
4875     if (sslctx) {
4876         assert(sslctx == x_sock->sslctx);
4877         if (!sslctx->sess) {
4878             FSSLCreate sslcreate = s_SSL ? s_SSL->Create : 0;
4879             assert(!sslctx->host  ||  *sslctx->host);
4880             if (sslcreate) {
4881                 assert(sslctx->sock == x_sock);
4882                 sslctx->sess = sslcreate(eSOCK_Client, sslctx, &error);
4883             } else
4884                 error = 0;
4885             if (!sslctx->sess) {
4886                 const char* strerr = s_StrError(x_sock, error);
4887                 CORE_LOGF_ERRNO_EXX(132, eLOG_Error,
4888                                     error, strerr ? strerr : "",
4889                                     ("%s[SOCK::CreateOnTop] "
4890                                      " %s to initialize secure session%s%s",
4891                                      s_ID(x_sock, _id),
4892                                      sslcreate ? "Failed" : "Unable",
4893                                      sslctx->host ? " with "     : "",
4894                                      sslctx->host ? sslctx->host : ""));
4895                 UTIL_ReleaseBuffer(strerr);
4896                 x_sock->sock = SOCK_INVALID;
4897 #ifdef NCBI_OS_MSWIN
4898                 WSAEventSelect(fd, event, 0/*de-associate*/);
4899                 WSACloseEvent(event);
4900 #endif /*NCBI_OS_MSWIN*/
4901                 SOCK_Destroy(x_sock);
4902                 if (oldfd != SOCK_INVALID)
4903                     SOCK_ABORT(oldfd);
4904                 return eIO_NotSupported;
4905             }
4906         } else {
4907             if (x_sock->log == eOn
4908                 ||  (x_sock->log == eDefault  &&  s_Log == eOn)) {
4909                 CORE_LOGF(eLOG_Trace,
4910                           ("%sSSL session re-acquired%s%s%s",
4911                            s_ID(x_sock, _id),
4912                            sslctx->host ? " \""        : "",
4913                            sslctx->host ? sslctx->host : "",
4914                            &"\""[!sslctx->host]));
4915             }
4916             x_sock->pending = x_orig->pending;
4917         }
4918     }
4919 
4920     if (x_orig) {
4921         size_t w_len = BUF_Size(x_orig->w_buf) - x_orig->w_len;
4922         x_sock->r_buf = x_orig->r_buf;
4923         x_orig->r_buf = 0;
4924         x_sock->w_buf = x_orig->w_buf;
4925         x_orig->w_buf = 0;
4926         x_orig->w_len = 0;
4927         BUF_Splice(&x_sock->w_buf, w_buf);
4928         BUF_Read(x_sock->w_buf, 0, w_len);
4929         BUF_Destroy(w_buf);
4930     } else
4931         BUF_SetChunkSize(&x_sock->r_buf, SOCK_BUF_CHUNK_SIZE);
4932     x_sock->w_len = BUF_Size(x_sock->w_buf);
4933 
4934     if (!x_orig  &&  x_sock->port) {
4935 #ifdef SO_KEEPALIVE
4936         if (!s_SetKeepAlive(fd, x_sock->keepalive)) {
4937             const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
4938             CORE_LOGF_ERRNO_EXX(153, eLOG_Warning,
4939                                 error, strerr ? strerr : "",
4940                                 ("%s[SOCK::CreateOnTop] "
4941                                  " Failed setsockopt(KEEPALIVE)",
4942                                  s_ID(x_sock, _id)));
4943             UTIL_ReleaseBuffer(strerr);
4944         }
4945 #endif /*SO_KEEPALIVE*/
4946 #ifdef SO_OOBINLINE
4947         if (!s_SetOobInline(fd, 1/*true*/)) {
4948             const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
4949             CORE_LOGF_ERRNO_EXX(138, eLOG_Warning,
4950                                 error, strerr ? strerr : "",
4951                                 ("%s[SOCK::CreateOnTop] "
4952                                  " Failed setsockopt(OOBINLINE)",
4953                                  s_ID(x_sock, _id)));
4954             UTIL_ReleaseBuffer(strerr);
4955         }
4956 #endif /*SO_OOBINLINE*/
4957     }
4958 
4959     if ((!x_orig
4960          ||  ((!x_orig->crossexec  ||  x_orig->sslctx) !=
4961               (!x_sock->crossexec  ||          sslctx)))
4962         &&  !s_SetCloexec(fd, !x_sock->crossexec  ||  sslctx)) {
4963         const char* strerr;
4964 #ifdef NCBI_OS_MSWIN
4965         DWORD err = GetLastError();
4966         strerr = s_WinStrerror(err);
4967         error = err;
4968 #else
4969         error = errno;
4970         strerr = SOCK_STRERROR(error);
4971 #endif /*NCBI_OS_MSWIN*/
4972         CORE_LOGF_ERRNO_EXX(124, eLOG_Warning,
4973                             error, strerr ? strerr : "",
4974                             ("%s[SOCK::CreateOnTop] "
4975                              " Cannot modify socket close-on-exec mode",
4976                              s_ID(x_sock, _id)));
4977 #ifdef NCBI_OS_MSWIN
4978         UTIL_ReleaseBufferOnHeap(strerr);
4979 #else
4980         UTIL_ReleaseBuffer(strerr);
4981 #endif /*NCBI_OS_MSWIN*/
4982     }
4983 
4984     if (!x_orig) {
4985         struct linger lgr;
4986         memset(&lgr, 0, sizeof(lgr));
4987         if (setsockopt(fd, SOL_SOCKET, SO_LINGER,
4988                        (char*) &lgr, sizeof(lgr)) != 0) {
4989             const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
4990             CORE_LOGF_ERRNO_EXX(43, eLOG_Warning,
4991                                 error, strerr ? strerr : "",
4992                                 ("%s[SOCK::CreateOnTop] "
4993                                  " Failed setsockopt(SO_NOLINGER)",
4994                                  s_ID(x_sock, _id)));
4995             UTIL_ReleaseBuffer(strerr);
4996         }
4997     }
4998 
4999     /* statistics & logging */
5000     if (x_sock->log == eOn  ||  (x_sock->log == eDefault  &&  s_Log == eOn))
5001         s_DoLog(eLOG_Note, x_sock, eIO_Open, 0, 0, "");
5002 
5003     /* success */
5004     *sock = x_sock;
5005     return eIO_Success;
5006 
5007  errout:
5008     assert(status != eIO_Success);
5009     if (sslctx) {
5010         assert(!oldctx  ||  oldctx != sslctx);
5011         if (sslctx->host)
5012             free((void*) sslctx->host);
5013         free(sslctx);
5014     }
5015     BUF_Destroy(w_buf);
5016     if (x_orig) {
5017         assert(oldfd != SOCK_INVALID);
5018         x_orig->sock  = oldfd;
5019 #ifdef NCBI_OS_MSWIN
5020         x_orig->event = event;
5021 #endif /*NCBI_OS_MSWIN*/
5022         s_Close(x_orig, 1/*re-close*/, fSOCK_KeepNone);
5023     }
5024     return status;
5025 }
5026 
5027 
s_CreateListening(const char * path,unsigned short port,unsigned short backlog,LSOCK * lsock,TSOCK_Flags flags)5028 static EIO_Status s_CreateListening(const char*    path,
5029                                     unsigned short port,
5030                                     unsigned short backlog,
5031                                     LSOCK*         lsock,
5032                                     TSOCK_Flags    flags)
5033 {
5034     union {
5035         struct sockaddr    sa;
5036         struct sockaddr_in in;
5037 #ifdef NCBI_OS_UNIX
5038         struct sockaddr_un un;
5039 #endif /*NCBI_OS_UNIX*/
5040     } addr;
5041 #ifdef NCBI_OS_UNIX
5042     mode_t          u;
5043 #endif /*NCBI_OS_UNIX*/
5044     TSOCK_Handle    fd;
5045     const char*     cp;
5046     int             type;
5047 #ifdef NCBI_OS_MSWIN
5048     WSAEVENT        event;
5049 #endif /*NCBI_OS_MSWIN*/
5050     int             error;
5051     TSOCK_socklen_t addrlen;
5052     LSOCK           x_lsock;
5053     char            _id[MAXIDLEN];
5054     unsigned int    x_id = ++s_ID_Counter;
5055 
5056     assert(!*lsock);
5057     assert(!path  ||  *path);
5058 
5059     memset(&addr, 0, sizeof(addr));
5060     if (path) {
5061 #ifdef NCBI_OS_UNIX
5062         size_t pathlen = strlen(path);
5063         if (sizeof(addr.un.sun_path) <= pathlen++/*account for end '\0'*/) {
5064             CORE_LOGF_X(144, eLOG_Error,
5065                         ("LSOCK#%u[?]@%s: [LSOCK::Create] "
5066                          " Path too long (%lu vs %lu bytes allowed)",
5067                          x_id, path, (unsigned long) pathlen,
5068                          (unsigned long) sizeof(addr.un.sun_path)));
5069             return eIO_InvalidArg;
5070         }
5071         addr.sa.sa_family = AF_UNIX;
5072 #else
5073         return eIO_NotSupported;
5074 #endif /*NCBI_OS_UNIX*/
5075     } else
5076         addr.sa.sa_family = AF_INET;
5077 
5078     /* initialize internals */
5079     if (s_InitAPI(flags & fSOCK_Secure) != eIO_Success)
5080         return eIO_NotSupported;
5081 
5082     if (flags & fSOCK_Secure) {
5083         /*FIXME:  Add secure server support later*/
5084         return eIO_NotSupported;
5085     }
5086 
5087     /* create new(listening) socket */
5088     type  = SOCK_STREAM;
5089 #ifdef SOCK_NONBLOCK
5090     type |= SOCK_NONBLOCK;
5091 #endif /*SOCK_NONBLOCK*/
5092 #ifdef SOCK_CLOEXEC
5093     if (!(flags & fSOCK_KeepOnExec))
5094         type |= SOCK_CLOEXEC;
5095 #endif /*SOCK_CLOEXEC*/
5096     if ((fd = socket(addr.sa.sa_family, type, 0)) == SOCK_INVALID){
5097         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
5098         if (!path) {
5099             if (port)
5100                 sprintf(_id, ":%hu", port);
5101             else
5102                 strcpy (_id, ":?");
5103             cp = _id;
5104         } else
5105             cp = path;
5106         CORE_LOGF_ERRNO_EXX(34, eLOG_Error,
5107                             error, strerr ? strerr : "",
5108                             ("LSOCK#%u[?]@%s: [LSOCK::Create] "
5109                              " Failed socket()", x_id, cp));
5110         UTIL_ReleaseBuffer(strerr);
5111         return eIO_Unknown;
5112     }
5113 
5114 
5115     if (!path) {
5116         const char* failed = 0;
5117 #if    defined(NCBI_OS_MSWIN)  &&  defined(SO_EXCLUSIVEADDRUSE)
5118         /* The use of this option comes with caveats, but it is better
5119          * to use it rather than to have (or leave) a chance for another
5120          * process (which uses SO_REUSEADDR, maliciously or not) to be able
5121          * to bind to the same port number, and snatch incoming connections.
5122          * Until a connection exists, that originated from the port with this
5123          * option set, the port (even if the listening instance was closed)
5124          * cannot be re-bound (important for service restarts!).  See MSDN.
5125          */
5126         BOOL excl = TRUE;
5127         if (setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
5128                        (const char*) &excl, sizeof(excl)) != 0) {
5129             failed = "EXCLUSIVEADDRUSE";
5130         }
5131 #elif !defined(NCBI_OS_MSWIN)
5132         /*
5133          * It was confirmed(?) that at least on Solaris 2.5 this precaution:
5134          * 1) makes the address(port) released immediately upon the process
5135          *    termination;
5136          * 2) still issues EADDRINUSE error on the attempt to bind() to the
5137          *    same address being in-use by a living process (if SOCK_STREAM).
5138          * 3) MS-Win treats SO_REUSEADDR completely differently in (as always)
5139          *    their own twisted way:  it *allows* to bind() to an already
5140          *    listening socket, which is why we jump the hoops above (also,
5141          *    note that SO_EXCLUSIVEADDRUSE == ~SO_REUSEADDR on MS-Win).
5142          */
5143         if (!s_SetReuseAddress(fd, 1/*true*/))
5144             failed = "REUSEADDR";
5145 #endif /*NCBI_OS_MSWIN...*/
5146         if (failed) {
5147             const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
5148             if (port)
5149                 sprintf(_id, "%hu", port);
5150             else
5151                 strcpy (_id, "?");
5152             CORE_LOGF_ERRNO_EXX(35, eLOG_Error,
5153                                 error, strerr ? strerr : "",
5154                                 ("LSOCK#%u[%u]@:%s: [LSOCK::Create] "
5155                                  " Failed setsockopt(%s)", x_id,
5156                                  (unsigned int) fd, _id, failed));
5157             UTIL_ReleaseBuffer(strerr);
5158             SOCK_CLOSE(fd);
5159             return eIO_Unknown;
5160         }
5161     }
5162 
5163     /* bind */
5164 #ifdef NCBI_OS_UNIX
5165     if (path) {
5166         assert(addr.un.sun_family == AF_UNIX);
5167         addrlen = (TSOCK_socklen_t) sizeof(addr.un);
5168 #  ifdef HAVE_SIN_LEN
5169         addr.un.sun_len = addrlen;
5170 #  endif /*HAVE_SIN_LEN*/
5171         strcpy(addr.un.sun_path, path);
5172         u = umask(0);
5173     } else
5174 #endif /*NCBI_OS_UNIX*/
5175     {
5176         unsigned int host =
5177             flags & fSOCK_BindLocal ? SOCK_LOOPBACK : htonl(INADDR_ANY);
5178         assert(addr.in.sin_family == AF_INET);
5179         addrlen = sizeof(addr.in);
5180 #ifdef HAVE_SIN_LEN
5181         addr.in.sin_len         = addrlen;
5182 #endif /*HAVE_SIN_LEN*/
5183         addr.in.sin_addr.s_addr =       host;
5184         addr.in.sin_port        = htons(port);
5185 #ifdef NCBI_OS_UNIX
5186         u = 0/*dummy*/;
5187 #endif /*NCBI_OS_UNIX*/
5188     }
5189     error = bind(fd, &addr.sa, addrlen) != 0 ? SOCK_ERRNO : 0;
5190 #ifdef NCBI_OS_UNIX
5191     if (path)
5192         umask(u);
5193 #endif /*NCBI_OS_UNIX*/
5194     if (error) {
5195         const char* strerr = SOCK_STRERROR(error);
5196         if (!path) {
5197             if (!port) {
5198                 SOCK_ntoa(addr.in.sin_addr.s_addr, _id, sizeof(_id));
5199                 strcat(_id + strlen(_id), ":?");
5200             } else {
5201                 SOCK_HostPortToString(addr.in.sin_addr.s_addr, port,
5202                                       _id, sizeof(_id));
5203             }
5204             cp = _id;
5205         } else
5206             cp = path;
5207         CORE_LOGF_ERRNO_EXX(36, error != SOCK_EADDRINUSE
5208                             ? eLOG_Error : eLOG_Trace,
5209                             error, strerr ? strerr : "",
5210                             ("LSOCK#%u[%u]@%s: [LSOCK::Create] "
5211                              " Failed bind()",
5212                              x_id, (unsigned int) fd, cp));
5213         UTIL_ReleaseBuffer(strerr);
5214         SOCK_CLOSE(fd);
5215         return error != SOCK_EADDRINUSE ? eIO_Unknown : eIO_Closed;
5216     }
5217     if (path)
5218 #ifdef NCBI_OS_IRIX
5219         (void) fchmod(fd, S_IRWXU | S_IRWXG | S_IRWXO)
5220 #endif /*NCBI_OS_IRIX*/
5221             ;
5222     else if (!port) {
5223         assert(addr.in.sin_family == AF_INET);
5224         error = getsockname(fd, &addr.sa, &addrlen) != 0
5225             ? SOCK_ERRNO : 0;
5226         if (error  ||  addr.sa.sa_family != AF_INET  ||  !addr.in.sin_port) {
5227             const char* strerr = SOCK_STRERROR(error);
5228             CORE_LOGF_ERRNO_EXX(150, eLOG_Error,
5229                                 error, strerr ? strerr : "",
5230                                 ("LSOCK#%u[%u]@:?: [LSOCK::Create] "
5231                                  " Cannot obtain free socket port",
5232                                  x_id, (unsigned int) fd));
5233             UTIL_ReleaseBuffer(strerr);
5234             SOCK_CLOSE(fd);
5235             return eIO_Closed;
5236         }
5237         port = ntohs(addr.in.sin_port);
5238     }
5239     assert((path  &&  !port)  ||
5240            (port  &&  !path));
5241 
5242 #if defined(NCBI_OS_MSWIN)
5243     if (!(event = WSACreateEvent())) {
5244         DWORD err = GetLastError();
5245         const char* strerr = s_WinStrerror(err);
5246         assert(!path);
5247         CORE_LOGF_ERRNO_EXX(118, eLOG_Error,
5248                             err, strerr ? strerr : "",
5249                             ("LSOCK#%u[%u]@:%hu: [LSOCK::Create] "
5250                              " Failed to create IO event",
5251                              x_id, (unsigned int) fd, port));
5252         UTIL_ReleaseBufferOnHeap(strerr);
5253         SOCK_CLOSE(fd);
5254         return eIO_Unknown;
5255     }
5256     /* NB: WSAEventSelect() sets non-blocking automatically */
5257     if (WSAEventSelect(fd, event, FD_CLOSE/*X*/ | FD_ACCEPT/*A*/) != 0) {
5258         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
5259         assert(!path);
5260         CORE_LOGF_ERRNO_EXX(119, eLOG_Error,
5261                             error, strerr ? strerr : "",
5262                             ("LSOCK#%u[%u]@:%hu: [LSOCK::Create] "
5263                              " Failed to bind IO event",
5264                              x_id, (unsigned int) fd, port));
5265         UTIL_ReleaseBuffer(strerr);
5266         SOCK_CLOSE(fd);
5267         WSACloseEvent(event);
5268         return eIO_Unknown;
5269     }
5270 #elif !defined(SOCK_NONBLOCK)
5271     /* set non-blocking mode */
5272     if (!s_SetNonblock(fd, 1/*true*/)) {
5273         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
5274         if (!path) {
5275             sprintf(_id, ":%hu", port);
5276             cp = _id;
5277         } else
5278             cp = path;
5279         CORE_LOGF_ERRNO_EXX(38, eLOG_Error,
5280                             error, strerr ? strerr : "",
5281                             ("LSOCK#%u[%u]@%s: [LSOCK::Create] "
5282                              " Cannot set socket to non-blocking mode",
5283                              x_id, (unsigned int) fd, cp));
5284         UTIL_ReleaseBuffer(strerr);
5285         SOCK_CLOSE(fd);
5286         return eIO_Unknown;
5287     }
5288 #endif
5289 
5290     /* listen */
5291     if (listen(fd, backlog) != 0) {
5292         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
5293         if (!path) {
5294             sprintf(_id, ":%hu", port);
5295             cp = _id;
5296         } else
5297             cp = path;
5298         CORE_LOGF_ERRNO_EXX(37, eLOG_Error,
5299                             error, strerr ? strerr : "",
5300                             ("LSOCK#%u[%u]@%s: [LSOCK::Create] "
5301                              " Failed listen(%hu)",
5302                              x_id, (unsigned int) fd, cp, backlog));
5303         UTIL_ReleaseBuffer(strerr);
5304         SOCK_CLOSE(fd);
5305 #ifdef NCBI_OS_MSWIN
5306         WSACloseEvent(event);
5307 #endif /*NCBI_OS_MSWIN*/
5308         return eIO_Unknown;
5309     }
5310 
5311     /* allocate memory for the internal socket structure */
5312     if (!(x_lsock =(LSOCK)calloc(1,sizeof(*x_lsock) + (path?strlen(path):0)))){
5313         SOCK_CLOSE(fd);
5314 #ifdef NCBI_OS_MSWIN
5315         WSACloseEvent(event);
5316 #endif /*NCBI_OS_MSWIN*/
5317         return eIO_Unknown;
5318     }
5319     x_lsock->sock     = fd;
5320     x_lsock->id       = x_id;
5321     x_lsock->port     = port;
5322     x_lsock->type     = eListening;
5323     x_lsock->side     = eSOCK_Server;
5324     x_lsock->log      = flags & (fSOCK_LogDefault | fSOCK_LogOn);
5325     x_lsock->keep     = flags & fSOCK_KeepOnClose ? 1/*true*/ : 0/*false*/;
5326     x_lsock->i_on_sig = flags & fSOCK_InterruptOnSignal ? eOn : eDefault;
5327 #if   defined(NCBI_OS_UNIX)
5328     if (path)
5329         strcpy(x_lsock->path, path);
5330 #elif defined(NCBI_OS_MSWIN)
5331     x_lsock->event    = event;
5332 #endif /*NCBI_OS*/
5333 
5334 #ifndef SOCK_CLOEXEC
5335     if (!(flags & fSOCK_KeepOnExec)  &&  !s_SetCloexec(fd, 1/*true*/)) {
5336         const char* strerr;
5337 #  ifdef NCBI_OS_MSWIN
5338         DWORD err = GetLastError();
5339         strerr = s_WinStrerror(err);
5340         error = err;
5341 #  else
5342         error = errno;
5343         strerr = SOCK_STRERROR(error);
5344 #  endif /*NCBI_OS_MSWIN*/
5345         if (!path) {
5346             sprintf(_id, ":%hu", port);
5347             cp = _id;
5348         } else
5349             cp = path;
5350         CORE_LOGF_ERRNO_EXX(110, eLOG_Warning,
5351                             error, strerr ? strerr : "",
5352                             ("LSOCK#%u[%u]@%s: [LSOCK::Create] "
5353                              " Cannot set socket close-on-exec mode",
5354                              x_id, (unsigned int) fd, cp));
5355 #  ifdef NCBI_OS_MSWIN
5356         UTIL_ReleaseBufferOnHeap(strerr);
5357 #  else
5358         UTIL_ReleaseBuffer(strerr);
5359 #  endif /*NCBI_OS_MSWIN*/
5360     }
5361 #endif /*!SOCK_CLOEXEC*/
5362 
5363     /* statistics & logging */
5364     if (x_lsock->log == eOn  ||  (x_lsock->log == eDefault && s_Log == eOn)){
5365         CORE_LOGF_X(115, eLOG_Note,
5366                     ("%sListening", s_ID((SOCK) x_lsock, _id)));
5367     }
5368 
5369     *lsock = x_lsock;
5370     return eIO_Success;
5371 }
5372 
5373 
s_Accept(LSOCK lsock,const STimeout * timeout,SOCK * sock,TSOCK_Flags flags)5374 static EIO_Status s_Accept(LSOCK           lsock,
5375                            const STimeout* timeout,
5376                            SOCK*           sock,
5377                            TSOCK_Flags     flags)
5378 {
5379     union {
5380         struct sockaddr    sa;
5381         struct sockaddr_in in;
5382 #ifdef NCBI_OS_UNIX
5383         struct sockaddr_un un;
5384 #endif /*NCBI_OS_UNIX*/
5385     } addr;
5386     TSOCK_Handle    fd;
5387     unsigned int    x_id;
5388     const char*     path;
5389     unsigned int    host;
5390     unsigned short  port;
5391 #ifdef NCBI_OS_MSWIN
5392     WSAEVENT        event;
5393 #endif /*NCBI_OS_MSWIN*/
5394     int             error;
5395     SOCK            x_sock;
5396     TSOCK_socklen_t addrlen;
5397     char            _id[MAXIDLEN];
5398 
5399     *sock = 0;
5400 
5401     if (!lsock  ||  lsock->sock == SOCK_INVALID) {
5402         CORE_LOGF_X(39, eLOG_Error,
5403                     ("%s[LSOCK::Accept] "
5404                      " Invalid socket",
5405                      s_ID((SOCK) lsock, _id)));
5406         assert(0);
5407         return eIO_Unknown;
5408     }
5409 
5410     if (flags & fSOCK_Secure) {
5411         /* FIXME:  Add secure support later */
5412         return eIO_NotSupported;
5413     }
5414 
5415     {{ /* wait for the connection request to come (up to timeout) */
5416         EIO_Status     status;
5417         SSOCK_Poll     poll;
5418         struct timeval tv;
5419 
5420         poll.sock   = (SOCK) lsock;
5421         poll.event  = eIO_Read;
5422         poll.revent = eIO_Open;
5423         status = s_Select(1, &poll, s_to2tv(timeout, &tv), 1/*asis*/);
5424         assert(poll.event == eIO_Read);
5425         if (status != eIO_Success)
5426             return status;
5427         if (poll.revent == eIO_Close)
5428             return eIO_Unknown;
5429         assert(poll.revent == eIO_Read);
5430     }}
5431 
5432     x_id = (lsock->id * 1000 + ++s_ID_Counter) * 1000;
5433 
5434     /* accept next connection */
5435     memset(&addr, 0, sizeof(addr));
5436 #ifdef NCBI_OS_UNIX
5437     if (lsock->path[0]) {
5438         addrlen = (TSOCK_socklen_t) sizeof(addr.un);
5439 #  ifdef HAVE_SIN_LEN
5440         addr.un.sun_len = addrlen;
5441 #  endif /*HAVE_SIN_LEN*/
5442         assert(!lsock->port);
5443     } else
5444 #endif /*NCBI_OS_UNIX*/
5445     {
5446         addrlen = (TSOCK_socklen_t) sizeof(addr.in);
5447 #ifdef HAVE_SIN_LEN
5448         addr.in.sin_len = addrlen;
5449 #endif /*HAVE_SIN_LEN*/
5450         assert(lsock->port);
5451 #ifdef NCBI_OS_MSWIN
5452         /* accept() [to follow shortly] resets IO event recording */
5453         lsock->readable = 0/*false*/;
5454 #endif /*NCBI_OS_MSWIN*/
5455     }
5456     if ((fd = accept(lsock->sock, &addr.sa, &addrlen)) == SOCK_INVALID) {
5457         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
5458         CORE_LOGF_ERRNO_EXX(40, eLOG_Error,
5459                             error, strerr ? strerr : "",
5460                             ("%s[LSOCK::Accept] "
5461                              " Failed accept()",
5462                              s_ID((SOCK) lsock, _id)));
5463         UTIL_ReleaseBuffer(strerr);
5464         return eIO_Unknown;
5465     }
5466     lsock->n_accept++;
5467 
5468 #ifdef NCBI_OS_UNIX
5469     if (lsock->path[0]) {
5470         assert(addr.un.sun_family == AF_UNIX);
5471         path = lsock->path;
5472         host = 0;
5473         port = 0;
5474     } else
5475 #endif /*NCBI_OS_UNIX*/
5476     {
5477         assert(addr.in.sin_family == AF_INET);
5478         host =       addr.in.sin_addr.s_addr;
5479         port = ntohs(addr.in.sin_port);
5480         assert(port);
5481         path = "";
5482     }
5483 
5484 #ifdef NCBI_OS_MSWIN
5485     if (!(event = WSACreateEvent())) {
5486         DWORD err = GetLastError();
5487         const char* strerr = s_WinStrerror(err);
5488         CORE_LOGF_ERRNO_EXX(120, eLOG_Error,
5489                             err, strerr ? strerr : "",
5490                             ("SOCK#%u[%u]@%s: [LSOCK::Accept] "
5491                              " Failed to create IO event",
5492                              x_id, (unsigned int) fd,
5493                              s_CP(host, port, path, _id, sizeof(_id))));
5494         UTIL_ReleaseBufferOnHeap(strerr);
5495         SOCK_ABORT(fd);
5496         return eIO_Unknown;
5497     }
5498     /* NB: WSAEventSelect() sets non-blocking automatically */
5499     if (WSAEventSelect(fd, event, SOCK_EVENTS) != 0) {
5500         int error = SOCK_ERRNO;
5501         const char* strerr = SOCK_STRERROR(error);
5502         CORE_LOGF_ERRNO_EXX(121, eLOG_Error,
5503                             error, strerr ? strerr : "",
5504                             ("SOCK#%u[%u]@%s: [LSOCK::Accept] "
5505                              " Failed to bind IO event",
5506                              x_id, (unsigned int) fd,
5507                              s_CP(host, port, path, _id, sizeof(_id))));
5508         UTIL_ReleaseBuffer(strerr);
5509         SOCK_ABORT(fd);
5510         WSACloseEvent(event);
5511         return eIO_Unknown;
5512     }
5513 #else
5514     /* man accept(2) notes that non-blocking state may not be inherited */
5515     if (!s_SetNonblock(fd, 1/*true*/)) {
5516         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
5517         CORE_LOGF_ERRNO_EXX(41, eLOG_Error,
5518                             error, strerr ? strerr : "",
5519                             ("SOCK#%u[%u]@%s: [LSOCK::Accept] "
5520                              " Cannot set socket to non-blocking mode",
5521                              x_id, (unsigned int) fd,
5522                              s_CP(host, port, path, _id, sizeof(_id))));
5523         UTIL_ReleaseBuffer(strerr);
5524         SOCK_ABORT(fd);
5525         return eIO_Unknown;
5526     }
5527 #endif /*NCBI_OS_MSWIN*/
5528 
5529     /* create new SOCK structure */
5530     addrlen = *path ? (TSOCK_socklen_t) strlen(path) : 0;
5531     if (!(x_sock = (SOCK) calloc(1, sizeof(*x_sock) + addrlen))) {
5532         SOCK_ABORT(fd);
5533 #ifdef NCBI_OS_MSWIN
5534         WSACloseEvent(event);
5535 #endif /*NCBI_OS_MSWIN*/
5536         return eIO_Unknown;
5537     }
5538 
5539     /* success */
5540 #ifdef NCBI_OS_UNIX
5541     if (!port) {
5542         assert(!lsock->port  &&  path[0]);
5543         strcpy(x_sock->path, path);
5544     } else
5545 #endif /*NCBI_OS_UNIX*/
5546     {
5547         assert(!path[0]);
5548         x_sock->host  = host;
5549         x_sock->port  = port;
5550     }
5551     x_sock->myport    = lsock->port;
5552     x_sock->sock      = fd;
5553     x_sock->id        = x_id;
5554     x_sock->type      = eSocket;
5555     x_sock->side      = eSOCK_Server;
5556     x_sock->log       = flags & (fSOCK_LogDefault | fSOCK_LogOn);
5557     x_sock->keep      = flags & fSOCK_KeepOnClose ? 1/*true*/ : 0/*false*/;
5558     x_sock->r_on_w    = flags & fSOCK_ReadOnWrite       ? eOn : eDefault;
5559     x_sock->i_on_sig  = flags & fSOCK_InterruptOnSignal ? eOn : eDefault;
5560 #ifdef NCBI_OS_MSWIN
5561     x_sock->event     = event;
5562     x_sock->writable  = 1/*true*/;
5563 #endif /*NCBI_OS_MSWIN*/
5564     x_sock->connected = 1/*true*/;
5565     x_sock->crossexec = flags & fSOCK_KeepOnExec  ? 1/*true*/ : 0/*false*/;
5566     x_sock->keepalive = flags & fSOCK_KeepAlive   ? 1/*true*/ : 0/*false*/;
5567     /* all timeouts zeroed - infinite */
5568     BUF_SetChunkSize(&x_sock->r_buf, SOCK_BUF_CHUNK_SIZE);
5569     /* w_buf is unused for accepted sockets */
5570 
5571     if (port) {
5572         if (s_ReuseAddress == eOn  &&  !s_SetReuseAddress(fd, 1)) {
5573             const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
5574             CORE_LOGF_ERRNO_EXX(42, eLOG_Warning,
5575                                 error, strerr ? strerr : "",
5576                                 ("%s[LSOCK::Accept] "
5577                                  " Failed setsockopt(REUSEADDR)",
5578                                  s_ID(*sock, _id)));
5579             UTIL_ReleaseBuffer(strerr);
5580         }
5581 
5582 #ifdef SO_KEEPALIVE
5583         if (x_sock->keepalive  &&  !s_SetKeepAlive(fd, 1)) {
5584             const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
5585             CORE_LOGF_ERRNO_EXX(152, eLOG_Warning,
5586                                 error, strerr ? strerr : "",
5587                                 ("%s[LSOCK::Accept] "
5588                                  " Failed setsockopt(KEEPALIVE)",
5589                                  s_ID(*sock, _id)));
5590             UTIL_ReleaseBuffer(strerr);
5591         }
5592 #endif /*SO_KEEPALIVE*/
5593 #ifdef SO_OOBINLINE
5594         if (!s_SetOobInline(fd, 1/*true*/)) {
5595             const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
5596             CORE_LOGF_ERRNO_EXX(137, eLOG_Warning,
5597                                 error, strerr ? strerr : "",
5598                                 ("%s[LSOCK::Accept] "
5599                                  " Failed setsockopt(OOBINLINE)",
5600                                  s_ID(*sock, _id)));
5601             UTIL_ReleaseBuffer(strerr);
5602         }
5603 #endif /*SO_OOBINLINE*/
5604     }
5605 
5606     if (!x_sock->crossexec  &&  !s_SetCloexec(fd, 1/*true*/)) {
5607         const char* strerr;
5608 #ifdef NCBI_OS_MSWIN
5609         DWORD err = GetLastError();
5610         strerr = s_WinStrerror(err);
5611         error = err;
5612 #else
5613         error = errno;
5614         strerr = SOCK_STRERROR(error);
5615 #endif /*NCBI_OS_MSWIN*/
5616         CORE_LOGF_ERRNO_EXX(128, eLOG_Warning,
5617                             error, strerr ? strerr : "",
5618                             ("%s[LSOCK::Accept] "
5619                              " Cannot set socket close-on-exec mode",
5620                              s_ID(*sock, _id)));
5621 #ifdef NCBI_OS_MSWIN
5622         UTIL_ReleaseBufferOnHeap(strerr);
5623 #else
5624         UTIL_ReleaseBuffer(strerr);
5625 #endif /*NCBI_OS_MSWIN*/
5626     }
5627 
5628     /* statistics & logging */
5629     if (x_sock->log == eOn  ||  (x_sock->log == eDefault  &&  s_Log == eOn))
5630         s_DoLog(eLOG_Note, x_sock, eIO_Open, 0, 0, 0);
5631 
5632     *sock = x_sock;
5633     return eIO_Success;
5634 }
5635 
5636 
s_CloseListening(LSOCK lsock)5637 static EIO_Status s_CloseListening(LSOCK lsock)
5638 {
5639     int        error;
5640     EIO_Status status;
5641 
5642     assert(lsock->sock != SOCK_INVALID);
5643 
5644 #if   defined(NCBI_OS_UNIX)
5645     if (!lsock->keep  &&  lsock->path[0]) {
5646         assert(!lsock->port);
5647         remove(lsock->path);
5648     }
5649 #elif defined(NCBI_OS_MSWIN)
5650     assert(lsock->event);
5651     WSAEventSelect(lsock->sock, lsock->event/*ignored*/, 0/*de-associate*/);
5652 #endif /*NCBI_OS*/
5653 
5654     /* statistics & logging */
5655     if (lsock->log == eOn  ||  (lsock->log == eDefault  &&  s_Log == eOn)) {
5656         char port[10];
5657         const char* c;
5658 #ifdef NCBI_OS_UNIX
5659         if (lsock->path[0]) {
5660             assert(!lsock->port);
5661             c = lsock->path;
5662         } else
5663 #endif /*NCBI_OS_UNIX*/
5664         {
5665             sprintf(port, ":%hu", lsock->port);
5666             c = port;
5667         }
5668         CORE_LOGF_X(44, eLOG_Note,
5669                     ("LSOCK#%u[%u]: %s at %s (%u accept%s total)",
5670                      lsock->id, (unsigned int) lsock->sock,
5671                      lsock->keep ? "Leaving" : "Closing", c,
5672                      lsock->n_accept, lsock->n_accept == 1 ? "" : "s"));
5673     }
5674 
5675     status = eIO_Success;
5676     if (!lsock->keep) {
5677         TSOCK_Handle fd = lsock->sock;
5678         for (;;) { /* close persistently - retry if interrupted */
5679             if (SOCK_CLOSE(fd) == 0)
5680                 break;
5681 
5682             /* error */
5683             if (s_Initialized <= 0)
5684                 break;
5685             error = SOCK_ERRNO;
5686 #ifdef NCBI_OS_MSWIN
5687             if (error == WSANOTINITIALISED) {
5688                 s_Initialized = -1/*deinited*/;
5689                 break;
5690             }
5691 #endif /*NCBI_OS_MSWIN*/
5692             if (error != SOCK_EINTR) {
5693                 const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
5694                 CORE_LOGF_ERRNO_EXX(45, eLOG_Error,
5695                                     error, strerr ? strerr : "",
5696                                     ("LSOCK#%u[%u]: [LSOCK::Close] "
5697                                      " Failed close()",
5698                                      lsock->id, (unsigned int) fd));
5699                 UTIL_ReleaseBuffer(strerr);
5700                 status = eIO_Unknown;
5701                 break;
5702             }
5703         }
5704         lsock->sock = SOCK_INVALID;
5705 #ifdef NCBI_OS_MSWIN
5706         WSASetEvent(lsock->event); /*signal closure*/
5707 #endif /*NCBI_OS_MSWIN*/
5708     } else
5709         lsock->sock = SOCK_INVALID;
5710 
5711     /* cleanup & return */
5712 #ifdef NCBI_OS_MSWIN
5713     WSACloseEvent(lsock->event);
5714     lsock->event = 0;
5715 #endif /*NCBI_OS_MSWIN*/
5716     return status;
5717 }
5718 
5719 
s_RecvMsg(SOCK sock,void * buf,size_t bufsize,size_t msgsize,size_t * msglen,unsigned int * sender_addr,unsigned short * sender_port)5720 static EIO_Status s_RecvMsg(SOCK            sock,
5721                             void*           buf,
5722                             size_t          bufsize,
5723                             size_t          msgsize,
5724                             size_t*         msglen,
5725                             unsigned int*   sender_addr,
5726                             unsigned short* sender_port)
5727 {
5728     size_t     x_msgsize;
5729     char       w[1536];
5730     EIO_Status status;
5731     void*      x_msg;
5732 
5733     BUF_Erase(sock->r_buf);
5734     sock->r_len = 0;
5735 
5736     x_msgsize = (msgsize  &&  msgsize < ((1 << 16) - 1))
5737         ? msgsize : ((1 << 16) - 1);
5738 
5739     if (!(x_msg = (x_msgsize <= bufsize
5740                    ? buf : (x_msgsize <= sizeof(w)
5741                             ? w : malloc(x_msgsize))))) {
5742         sock->r_status = eIO_Unknown;
5743         return eIO_Unknown;
5744     }
5745 
5746     for (;;) { /* auto-resume if either blocked or interrupted (optional) */
5747         ssize_t            x_read;
5748         int                error;
5749         struct sockaddr_in sin;
5750         TSOCK_socklen_t    sinlen = (TSOCK_socklen_t) sizeof(sin);
5751         memset(&sin, 0, sizeof(sin));
5752 #ifdef HAVE_SIN_LEN
5753         sin.sin_len = sinlen;
5754 #endif
5755         x_read = recvfrom(sock->sock, x_msg,
5756 #ifdef NCBI_OS_MSWIN
5757                           /*WINSOCK wants it weird*/ (int)
5758 #endif /*NCBI_OS_MSWIN*/
5759                           x_msgsize, 0/*flags*/,
5760                           (struct sockaddr*) &sin, &sinlen);
5761 #ifdef NCBI_OS_MSWIN
5762         /* recvfrom() resets IO event recording */
5763         sock->readable = 0/*false*/;
5764 #endif /*NCBI_OS_MSWIN*/
5765 
5766         if (x_read >= 0) {
5767             /* got a message */
5768             sock->r_status = eIO_Success;
5769             sock->r_len = (TNCBI_BigCount) x_read;
5770             if ( msglen )
5771                 *msglen = (size_t) x_read;
5772             if ( sender_addr )
5773                 *sender_addr =       sin.sin_addr.s_addr;
5774             if ( sender_port )
5775                 *sender_port = ntohs(sin.sin_port);
5776             if ((size_t) x_read > bufsize
5777                 &&  !BUF_Write(&sock->r_buf,
5778                                (char*) x_msg  + bufsize,
5779                                (size_t)x_read - bufsize)) {
5780                 CORE_LOGF_X(20, eLOG_Error,
5781                             ("%s[DSOCK::RecvMsg] "
5782                              " Message truncated: %lu/%lu",
5783                              s_ID(sock, w),
5784                              (unsigned long) bufsize, (unsigned long) x_read));
5785                 status = eIO_Unknown;
5786             } else
5787                 status = eIO_Success;
5788             if (bufsize  &&  bufsize < x_msgsize) {
5789                 memcpy(buf, x_msg,
5790                        (size_t) x_read < bufsize ? (size_t) x_read : bufsize);
5791             }
5792 
5793             /* statistics & logging */
5794             if (sock->log == eOn  ||  (sock->log == eDefault && s_Log == eOn)){
5795                 s_DoLog(eLOG_Note, sock, eIO_Read, x_msg, (size_t) x_read,
5796                         &sin);
5797             }
5798 
5799             sock->n_read += (TNCBI_BigCount) x_read;
5800             sock->n_in++;
5801             break;
5802         }
5803 
5804         error = SOCK_ERRNO;
5805 
5806         /* blocked -- retry if unblocked before the timeout expires */
5807         if (error == SOCK_EWOULDBLOCK  ||  error == SOCK_EAGAIN) {
5808             SSOCK_Poll poll;
5809             poll.sock   = sock;
5810             poll.event  = eIO_Read;
5811             poll.revent = eIO_Open;
5812             status = s_Select(1, &poll, SOCK_GET_TIMEOUT(sock, r), 1/*asis*/);
5813             assert(poll.event == eIO_Read);
5814             if (status != eIO_Success)
5815                 break;
5816             if (poll.revent != eIO_Close) {
5817                 assert(poll.revent == eIO_Read);
5818                 continue/*read again*/;
5819             }
5820         } else if (error == SOCK_EINTR) {
5821             if (sock->i_on_sig == eOn
5822                 || (sock->i_on_sig == eDefault && s_InterruptOnSignal == eOn)){
5823                 sock->r_status = status = eIO_Interrupt;
5824                 break/*interrupt*/;
5825             }
5826             continue;
5827         } else {
5828             const char* strerr = SOCK_STRERROR(error);
5829             CORE_LOGF_ERRNO_EXX(94, eLOG_Trace,
5830                                 error, strerr ? strerr : "",
5831                                 ("%s[DSOCK::RecvMsg] "
5832                                  " Failed recvfrom()",
5833                                  s_ID(sock, w)));
5834             UTIL_ReleaseBuffer(strerr);
5835         }
5836         /* don't want to handle all possible errors... let them be "unknown" */
5837         sock->r_status = status = eIO_Unknown;
5838         break;
5839     }
5840 
5841     if (x_msgsize > bufsize  &&  x_msg != w)
5842         free(x_msg);
5843     return status;
5844 }
5845 
5846 
s_SendMsg(SOCK sock,const char * host,unsigned short port,const void * data,size_t datalen)5847 static EIO_Status s_SendMsg(SOCK           sock,
5848                             const char*    host,
5849                             unsigned short port,
5850                             const void*    data,
5851                             size_t         datalen)
5852 {
5853     size_t             x_msgsize;
5854     char               w[1536];
5855     EIO_Status         status;
5856     unsigned short     x_port;
5857     unsigned int       x_host;
5858     void*              x_msg;
5859     struct sockaddr_in sin;
5860 
5861     if (datalen) {
5862         status = s_Write_(sock, data, datalen, &x_msgsize, 0/*no OOB*/);
5863         if (status != eIO_Success) {
5864             CORE_LOGF_ERRNO_X(154, eLOG_Error, errno,
5865                               ("%s[DSOCK::SendMsg] "
5866                                " Failed to finalize message (%lu byte%s)",
5867                                s_ID(sock, w), (unsigned long) datalen,
5868                                &"s"[datalen == 1]));
5869             return status;
5870         }
5871         assert(x_msgsize == datalen);
5872         assert(sock->w_len == 0);
5873     } else
5874         sock->w_len = 0;
5875     sock->eof = 1/*true - finalized message*/;
5876 
5877     x_port = port ? port : sock->port;
5878     if (!host  ||  !*host)
5879         x_host = sock->host;
5880     else if (!(x_host = s_gethostbyname(host, 0, (ESwitch) sock->log))) {
5881         CORE_LOGF_X(88, eLOG_Error,
5882                     ("%s[DSOCK::SendMsg] "
5883                      " Failed SOCK_gethostbyname(\"%.*s\")",
5884                      s_ID(sock, w), CONN_HOST_LEN, host));
5885         return eIO_Unknown;
5886     }
5887 
5888     if (!x_host  ||  !x_port) {
5889         SOCK_HostPortToString(x_host, x_port, w, sizeof(w)/2);
5890         CORE_LOGF_X(89, eLOG_Error,
5891                     ("%s[DSOCK::SendMsg] "
5892                      " Address \"%s\" incomplete, missing %s",
5893                      s_ID(sock, w + sizeof(w)/2), w,
5894                      x_port ? "host" : &"host:port"[x_host ? 5 : 0]));
5895          return eIO_Unknown;
5896     }
5897 
5898     if ((x_msgsize = BUF_Size(sock->w_buf)) != 0) {
5899         if (x_msgsize <= sizeof(w))
5900             x_msg = w;
5901         else if (!(x_msg = malloc(x_msgsize)))
5902             return eIO_Unknown;
5903         verify(BUF_Peek(sock->w_buf, x_msg, x_msgsize) == x_msgsize);
5904     } else
5905         x_msg = 0;
5906 
5907     memset(&sin, 0, sizeof(sin));
5908 #ifdef HAVE_SIN_LEN
5909     sin.sin_len         = (TSOCK_socklen_t) sizeof(sin);
5910 #endif /*HAVE_SIN_LEN*/
5911     sin.sin_family      = AF_INET;
5912     sin.sin_addr.s_addr =       x_host;
5913     sin.sin_port        = htons(x_port);
5914 
5915     for (;;) { /* optionally auto-resume if interrupted */
5916         int error;
5917         ssize_t x_written;
5918 
5919         if ((x_written = sendto(sock->sock, x_msg,
5920 #ifdef NCBI_OS_MSWIN
5921                                 /*WINSOCK wants it weird*/ (int)
5922 #endif /*NCBI_OS_MSWIN*/
5923                                 x_msgsize, 0/*flags*/,
5924                                 (struct sockaddr*) &sin, sizeof(sin))) >= 0) {
5925             /* statistics & logging */
5926             if (sock->log == eOn  ||  (sock->log == eDefault && s_Log == eOn)){
5927                 s_DoLog(eLOG_Note, sock, eIO_Write, x_msg, (size_t) x_written,
5928                         &sin);
5929             }
5930 
5931             sock->w_len      = (TNCBI_BigCount) x_written;
5932             sock->n_written += (TNCBI_BigCount) x_written;
5933             sock->n_out++;
5934             if ((size_t) x_written != x_msgsize) {
5935                 sock->w_status = status = eIO_Closed;
5936                 if (!host  &&  !port)
5937                     w[0] = '\0';
5938                 else
5939                     SOCK_HostPortToString(x_host, x_port, w, sizeof(w)/2);
5940                 CORE_LOGF_X(90, eLOG_Error,
5941                             ("%s[DSOCK::SendMsg] "
5942                              " Partial datagram sent (%lu out of %lu)%s%s",
5943                              s_ID(sock, w + sizeof(w)/2),
5944                              (unsigned long) x_written,
5945                              (unsigned long) x_msgsize, *w ? " to " : "", w));
5946                 break;
5947             }
5948             sock->w_status = status = eIO_Success;
5949             break;
5950         }
5951 
5952 #ifdef NCBI_OS_MSWIN
5953         /* special sendto()'s semantics of IO recording reset */
5954         sock->writable = 0/*false*/;
5955 #endif /*NCBI_OS_MSWIN*/
5956 
5957         error = SOCK_ERRNO;
5958 
5959         /* blocked -- retry if unblocked before the timeout expires */
5960         if (error == SOCK_EWOULDBLOCK  ||  error == SOCK_EAGAIN) {
5961             SSOCK_Poll poll;
5962             poll.sock   = sock;
5963             poll.event  = eIO_Write;
5964             poll.revent = eIO_Open;
5965             status = s_Select(1, &poll, SOCK_GET_TIMEOUT(sock, w), 1/*asis*/);
5966             assert(poll.event == eIO_Write);
5967             if (status != eIO_Success)
5968                 break;
5969             if (poll.revent != eIO_Close) {
5970                 assert(poll.revent == eIO_Write);
5971                 continue;
5972             }
5973         } else if (error == SOCK_EINTR) {
5974             if (sock->i_on_sig == eOn
5975                 || (sock->i_on_sig == eDefault && s_InterruptOnSignal == eOn)){
5976                 sock->w_status = status = eIO_Interrupt;
5977                 break/*interrupt*/;
5978             }
5979             continue;
5980         } else {
5981             const char* strerr = SOCK_STRERROR(error);
5982             if (!host  &&  !port)
5983                 w[0] = '\0';
5984             else
5985                 SOCK_HostPortToString(x_host, x_port, w, sizeof(w)/2);
5986             CORE_LOGF_ERRNO_EXX(91, eLOG_Trace,
5987                                 error, strerr ? strerr : "",
5988                                 ("%s[DSOCK::SendMsg] "
5989                                  " Failed sendto(%s)",
5990                                  s_ID(sock, w + sizeof(w)/2), w));
5991             UTIL_ReleaseBuffer(strerr);
5992         }
5993         /* don't want to handle all possible errors... let them be "unknown" */
5994         sock->w_status = status = eIO_Unknown;
5995         break;
5996     }
5997 
5998     if (x_msg  &&  x_msg != w)
5999         free(x_msg);
6000     if (status == eIO_Success)
6001         BUF_Erase(sock->w_buf);
6002     return status;
6003 }
6004 
6005 
6006 
6007 /******************************************************************************
6008  *  TRIGGER
6009  */
6010 
6011 
TRIGGER_Create(TRIGGER * trigger,ESwitch log)6012 extern EIO_Status TRIGGER_Create(TRIGGER* trigger, ESwitch log)
6013 {
6014     unsigned int x_id = ++s_ID_Counter;
6015 
6016     *trigger = 0;
6017 
6018     /* initialize internals */
6019     if (s_InitAPI(0) != eIO_Success)
6020         return eIO_NotSupported;
6021 
6022 #ifndef NCBI_CXX_TOOLKIT
6023 
6024     return eIO_NotSupported;
6025 
6026 #elif defined(NCBI_OS_UNIX)
6027     {{
6028         int fd[3];
6029 
6030         if (pipe(fd) != 0) {
6031             CORE_LOGF_ERRNO_X(28, eLOG_Error, errno,
6032                               ("TRIGGER#%u[?]: [TRIGGER::Create] "
6033                                " Cannot create pipe", x_id));
6034             return eIO_Closed;
6035         }
6036 
6037 #  ifdef FD_SETSIZE
6038         if ((fd[2] = fcntl(fd[1], F_DUPFD, FD_SETSIZE)) < 0) {
6039             /* We don't need "out" to be selectable, so move it out
6040              * of the way to spare precious "selectable" fd numbers */
6041             CORE_LOGF_ERRNO_X(143, eLOG_Warning, errno,
6042                               ("TRIGGER#%u[?]: [TRIGGER::Create] "
6043                                " Failed to dup(%d) to higher fd(%d+))",
6044                                x_id, fd[1], FD_SETSIZE));
6045         } else {
6046             close(fd[1]);
6047             fd[1] = fd[2];
6048         }
6049 #  endif /*FD_SETSIZE*/
6050 
6051         if (!s_SetNonblock(fd[0], 1/*true*/)  ||
6052             !s_SetNonblock(fd[1], 1/*true*/)) {
6053             CORE_LOGF_ERRNO_X(29, eLOG_Error, errno,
6054                               ("TRIGGER#%u[?]: [TRIGGER::Create] "
6055                                " Failed to set non-blocking mode", x_id));
6056             close(fd[0]);
6057             close(fd[1]);
6058             return eIO_Unknown;
6059         }
6060 
6061         if (!s_SetCloexec(fd[0], 1/*true*/)  ||
6062             !s_SetCloexec(fd[1], 1/*true*/)) {
6063             CORE_LOGF_ERRNO_X(30, eLOG_Warning, errno,
6064                               ("TRIGGER#%u[?]: [TRIGGER::Create] "
6065                                " Failed to set close-on-exec", x_id));
6066         }
6067 
6068         if (!(*trigger = (TRIGGER) calloc(1, sizeof(**trigger)))) {
6069             close(fd[0]);
6070             close(fd[1]);
6071             return eIO_Unknown;
6072         }
6073         (*trigger)->fd       = fd[0];
6074         (*trigger)->id       = x_id;
6075         (*trigger)->out      = fd[1];
6076         (*trigger)->type     = eTrigger;
6077         (*trigger)->log      = log;
6078         (*trigger)->i_on_sig = eDefault;
6079 
6080         /* statistics & logging */
6081         if (log == eOn  ||  (log == eDefault  &&  s_Log == eOn)) {
6082             CORE_LOGF_X(116, eLOG_Note,
6083                         ("TRIGGER#%u[%u, %u]: Ready", x_id, fd[0], fd[1]));
6084         }
6085     }}
6086     return eIO_Success;
6087 
6088 #elif defined(NCBI_OS_MSWIN)
6089     {{
6090         HANDLE event = WSACreateEvent();
6091         if (!event) {
6092             DWORD err = GetLastError();
6093             const char* strerr = s_WinStrerror(err);
6094             CORE_LOGF_ERRNO_EXX(14, eLOG_Error,
6095                                 err, strerr ? strerr : "",
6096                                 ("TRIGGER#%u: [TRIGGER::Create] "
6097                                  " Cannot create event object", x_id));
6098             UTIL_ReleaseBufferOnHeap(strerr);
6099             return eIO_Closed;
6100         }
6101         if (!(*trigger = (TRIGGER) calloc(1, sizeof(**trigger)))) {
6102             WSACloseEvent(event);
6103             return eIO_Unknown;
6104         }
6105         (*trigger)->fd       = event;
6106         (*trigger)->id       = x_id;
6107         (*trigger)->type     = eTrigger;
6108         (*trigger)->log      = log;
6109         (*trigger)->i_on_sig = eDefault;
6110 
6111         /* statistics & logging */
6112         if (log == eOn  ||  (log == eDefault  &&  s_Log == eOn)) {
6113             CORE_LOGF_X(116, eLOG_Note,
6114                         ("TRIGGER#%u: Ready", x_id));
6115         }
6116     }}
6117     return eIO_Success;
6118 
6119 #else
6120 #  error "Unsupported platform"
6121 #endif /*NCBI_OS*/
6122 }
6123 
6124 
6125 /*ARGSUSED*/
TRIGGER_Close(TRIGGER trigger)6126 extern EIO_Status TRIGGER_Close(TRIGGER trigger)
6127 {
6128 #ifndef NCBI_CXX_TOOLKIT
6129 
6130     return eIO_NotSupported;
6131 
6132 #else
6133 
6134     /* statistics & logging */
6135     if (trigger->log == eOn  ||  (trigger->log == eDefault  &&  s_Log == eOn)){
6136         CORE_LOGF_X(117, eLOG_Note,
6137                     ("TRIGGER#%u[%u]: Closing", trigger->id, trigger->fd));
6138     }
6139 
6140 #  if   defined(NCBI_OS_UNIX)
6141 
6142     /* Prevent SIGPIPE by closing in this order:  writing end first */
6143     close(trigger->out);
6144     close(trigger->fd);
6145 
6146 #  elif defined(NCBI_OS_MSWIN)
6147 
6148     WSACloseEvent(trigger->fd);
6149 
6150 #  endif /*NCBI_OS*/
6151 
6152     free(trigger);
6153     return eIO_Success;
6154 
6155 #endif /*NCBI_CXX_TOOLKIT*/
6156 }
6157 
6158 
6159 /*ARGSUSED*/
TRIGGER_Set(TRIGGER trigger)6160 extern EIO_Status TRIGGER_Set(TRIGGER trigger)
6161 {
6162 #ifndef NCBI_CXX_TOOLKIT
6163 
6164     return eIO_NotSupported;
6165 
6166 #elif defined(NCBI_OS_UNIX)
6167 
6168     if (CORE_Once((void**) &trigger->isset.ptr)) {
6169         if (write(trigger->out, "", 1) < 0  &&  errno != EAGAIN)
6170             return eIO_Unknown;
6171     }
6172 
6173     return eIO_Success;
6174 
6175 #elif defined(NCBI_OS_MSWIN)
6176 
6177     return WSASetEvent(trigger->fd) ? eIO_Success : eIO_Unknown;
6178 
6179 #else
6180 #  error "Unsupported platform"
6181 #endif /*NCBI_OS*/
6182 }
6183 
6184 
6185 /*ARGSUSED*/
x_TriggerRead(const TRIGGER trigger,int isset)6186 static EIO_Status x_TriggerRead(const TRIGGER trigger, int/*bool*/ isset)
6187 {
6188 #ifndef NCBI_CXX_TOOLKIT
6189 
6190     return eIO_NotSupported;
6191 
6192 #elif defined(NCBI_OS_UNIX)
6193 
6194 #  ifdef PIPE_SIZE
6195 #    define MAX_TRIGGER_BUF  PIPE_SIZE
6196 #  else
6197 #    define MAX_TRIGGER_BUF  8192
6198 #  endif /*PIPE_SIZE*/
6199 
6200     EIO_Status  status = eIO_Unknown;
6201     for (;;) {
6202         static char x_buf[MAX_TRIGGER_BUF];
6203         ssize_t     x_read = read(trigger->fd, x_buf, sizeof(x_buf));
6204         if (x_read == 0/*EOF?*/)
6205             break;
6206         if (x_read < 0) {
6207             int error;
6208             if (status == eIO_Success)
6209                 break;
6210             if ((error = errno) == EAGAIN  ||  error == EWOULDBLOCK)
6211                 status  = eIO_Timeout;
6212             break;
6213         }
6214         status = eIO_Success;
6215     }
6216     return status;
6217 
6218 #elif defined(NCBI_OS_MSWIN)
6219 
6220     switch (WaitForSingleObject(trigger->fd, 0)) {
6221     case WAIT_OBJECT_0:
6222         return eIO_Success;
6223     case WAIT_TIMEOUT:
6224         return eIO_Timeout;
6225     default:
6226         break;
6227     }
6228     return eIO_Unknown;
6229 
6230 #else
6231 #  error "Unsupported platform"
6232 #endif /*NCBI_OS*/
6233 }
6234 
6235 
TRIGGER_IsSet(TRIGGER trigger)6236 extern EIO_Status TRIGGER_IsSet(TRIGGER trigger)
6237 {
6238 #ifndef NCBI_CXX_TOOLKIT
6239 
6240     return eIO_NotSupported;
6241 
6242 #else
6243     EIO_Status status = x_TriggerRead(trigger, 1/*IsSet*/);
6244 
6245 #  ifdef NCBI_OS_UNIX
6246 
6247     if (status == eIO_Success)
6248         trigger->isset.ptr = (void*) 1/*true*/;
6249     else if (status != eIO_Timeout)
6250         return status;
6251     return trigger->isset.ptr ? eIO_Success : eIO_Closed;
6252 
6253 #  else
6254 
6255     return status == eIO_Timeout ? eIO_Closed : status;
6256 
6257 #  endif /*NCBI_OS_UNIX*/
6258 
6259 #endif /*NCBI_CXX_TOOLKIT*/
6260 }
6261 
6262 
TRIGGER_Reset(TRIGGER trigger)6263 extern EIO_Status TRIGGER_Reset(TRIGGER trigger)
6264 {
6265     EIO_Status status = x_TriggerRead(trigger, 0/*Reset*/);
6266 
6267 #if   defined(NCBI_OS_UNIX)
6268 
6269     trigger->isset.ptr = (void*) 0/*false*/;
6270 
6271 #elif defined(NCBI_OS_MSWIN)
6272 
6273     if (!WSAResetEvent(trigger->fd))
6274         return eIO_Unknown;
6275 
6276 #endif /*NCBI_OS*/
6277 
6278     return status == eIO_Timeout ? eIO_Success : status;
6279 }
6280 
6281 
6282 
6283 /******************************************************************************
6284  *  LISTENING SOCKET
6285  */
6286 
6287 
LSOCK_Create(unsigned short port,unsigned short backlog,LSOCK * lsock)6288 extern EIO_Status LSOCK_Create(unsigned short port,
6289                                unsigned short backlog,
6290                                LSOCK*         lsock)
6291 {
6292     *lsock = 0;
6293     return s_CreateListening(0, port, backlog, lsock, fSOCK_LogDefault);
6294 }
6295 
6296 
LSOCK_CreateEx(unsigned short port,unsigned short backlog,LSOCK * lsock,TSOCK_Flags flags)6297 extern EIO_Status LSOCK_CreateEx(unsigned short port,
6298                                  unsigned short backlog,
6299                                  LSOCK*         lsock,
6300                                  TSOCK_Flags    flags)
6301 {
6302     *lsock = 0;
6303     return s_CreateListening(0, port, backlog, lsock, flags);
6304 }
6305 
6306 
LSOCK_CreateUNIX(const char * path,unsigned short backlog,LSOCK * lsock,TSOCK_Flags flags)6307 extern EIO_Status LSOCK_CreateUNIX(const char*    path,
6308                                    unsigned short backlog,
6309                                    LSOCK*         lsock,
6310                                    TSOCK_Flags    flags)
6311 {
6312     *lsock = 0;
6313     if (!path  ||  !*path)
6314         return eIO_InvalidArg;
6315     return s_CreateListening(path, 0, backlog, lsock, flags);
6316 }
6317 
6318 
LSOCK_Accept(LSOCK lsock,const STimeout * timeout,SOCK * sock)6319 extern EIO_Status LSOCK_Accept(LSOCK           lsock,
6320                                const STimeout* timeout,
6321                                SOCK*           sock)
6322 {
6323     return s_Accept(lsock, timeout, sock, fSOCK_LogDefault);
6324 }
6325 
6326 
LSOCK_AcceptEx(LSOCK lsock,const STimeout * timeout,SOCK * sock,TSOCK_Flags flags)6327 extern EIO_Status LSOCK_AcceptEx(LSOCK           lsock,
6328                                  const STimeout* timeout,
6329                                  SOCK*           sock,
6330                                  TSOCK_Flags     flags)
6331 {
6332     return s_Accept(lsock, timeout, sock, flags);
6333 }
6334 
6335 
LSOCK_Close(LSOCK lsock)6336 extern EIO_Status LSOCK_Close(LSOCK lsock)
6337 {
6338     EIO_Status status;
6339 
6340     if (lsock) {
6341         status = (lsock->sock != SOCK_INVALID
6342                   ? s_CloseListening(lsock)
6343                   : eIO_Closed);
6344         free(lsock);
6345     } else
6346         status = eIO_InvalidArg;
6347     return status;
6348 }
6349 
6350 
LSOCK_GetOSHandleEx(LSOCK lsock,void * handle,size_t handle_size,EOwnership ownership)6351 extern EIO_Status LSOCK_GetOSHandleEx(LSOCK      lsock,
6352                                       void*      handle,
6353                                       size_t     handle_size,
6354                                       EOwnership ownership)
6355 {
6356     TSOCK_Handle fd;
6357     EIO_Status   status;
6358 
6359     if (!handle  ||  handle_size != sizeof(lsock->sock)) {
6360         CORE_LOGF_X(46, eLOG_Error,
6361                     ("LSOCK#%u[%u]: [LSOCK::GetOSHandle] "
6362                      " Invalid handle%s %lu",
6363                      lsock->id, (unsigned int) lsock->sock,
6364                      handle ? " size"                     : "",
6365                      handle ? (unsigned long) handle_size : 0));
6366         assert(0);
6367         return eIO_InvalidArg;
6368     }
6369     if (!lsock) {
6370         fd = SOCK_INVALID;
6371         memcpy(handle, &fd, handle_size);
6372         return eIO_InvalidArg;
6373     }
6374     fd = lsock->sock;
6375     memcpy(handle, &fd, handle_size);
6376     if (s_Initialized <= 0  ||  fd == SOCK_INVALID)
6377         status = eIO_Closed;
6378     else if (ownership != eTakeOwnership)
6379         status = eIO_Success;
6380     else {
6381         lsock->keep = 1/*true*/;
6382         status = s_CloseListening(lsock);
6383         assert(lsock->sock == SOCK_INVALID);
6384     }
6385     return status;
6386 }
6387 
6388 
LSOCK_GetOSHandle(LSOCK lsock,void * handle,size_t handle_size)6389 extern EIO_Status LSOCK_GetOSHandle(LSOCK  lsock,
6390                                     void*  handle,
6391                                     size_t handle_size)
6392 {
6393     return LSOCK_GetOSHandleEx(lsock, handle, handle_size, eNoOwnership);
6394 }
6395 
6396 
LSOCK_GetPort(LSOCK lsock,ENH_ByteOrder byte_order)6397 extern unsigned short LSOCK_GetPort(LSOCK         lsock,
6398                                     ENH_ByteOrder byte_order)
6399 {
6400     unsigned short port;
6401     port = lsock  &&  lsock->sock != SOCK_INVALID ? lsock->port : 0;
6402     return byte_order == eNH_HostByteOrder ? port : htons(port);
6403 }
6404 
6405 
6406 
6407 /******************************************************************************
6408  *  SOCKET
6409  */
6410 
6411 
SOCK_CreateInternal(const char * host,unsigned short port,const STimeout * timeout,SOCK * sock,const SSOCK_Init * init,TSOCK_Flags flags)6412 EIO_Status SOCK_CreateInternal(const char*       host,
6413                                unsigned short    port,
6414                                const STimeout*   timeout,
6415                                SOCK*             sock,
6416                                const SSOCK_Init* init,
6417                                TSOCK_Flags       flags)
6418 {
6419     EIO_Status status;
6420     *sock = 0;
6421     if (!host  ||  !port)
6422         return eIO_InvalidArg;
6423     status = s_Create(host, port, timeout, sock, init, flags);
6424     assert(!*sock == !(status == eIO_Success));
6425     return status;
6426 }
6427 
6428 
SOCK_CreateEx(const char * host,unsigned short port,const STimeout * timeout,SOCK * sock,const void * data,size_t size,TSOCK_Flags flags)6429 extern EIO_Status SOCK_CreateEx(const char*     host,
6430                                 unsigned short  port,
6431                                 const STimeout* timeout,
6432                                 SOCK*           sock,
6433                                 const void*     data,
6434                                 size_t          size,
6435                                 TSOCK_Flags     flags)
6436 {
6437     SSOCK_Init init;
6438     memset(&init, 0, sizeof(init));
6439     init.data = data;
6440     init.size = size;
6441     return SOCK_CreateInternal(host, port, timeout, sock, &init, flags);
6442 }
6443 
6444 
SOCK_Create(const char * host,unsigned short port,const STimeout * timeout,SOCK * sock)6445 extern EIO_Status SOCK_Create(const char*     host,
6446                               unsigned short  port,
6447                               const STimeout* timeout,
6448                               SOCK*           sock)
6449 {
6450     return SOCK_CreateInternal(host, port, timeout, sock, 0, fSOCK_LogDefault);
6451 }
6452 
6453 
SOCK_CreateUNIX(const char * path,const STimeout * timeout,SOCK * sock,const void * data,size_t size,TSOCK_Flags flags)6454 extern EIO_Status SOCK_CreateUNIX(const char*     path,
6455                                   const STimeout* timeout,
6456                                   SOCK*           sock,
6457                                   const void*     data,
6458                                   size_t          size,
6459                                   TSOCK_Flags     flags)
6460 {
6461     EIO_Status status;
6462     *sock = 0;
6463     if (!path  ||  !*path)
6464         return eIO_InvalidArg;
6465 #ifndef NCBI_OS_UNIX
6466     status = eIO_NotSupported;
6467 #else
6468     {{
6469         SSOCK_Init init;
6470         memset(&init, 0, sizeof(init));
6471         init.data = data;
6472         init.size = size;
6473         status = s_Create(path, 0, timeout, sock, &init, flags);
6474         assert(!*sock == !(status == eIO_Success));
6475     }}
6476 #endif /*!NCBI_OS_UNIX*/
6477     return status;
6478 }
6479 
6480 
SOCK_CreateOnTopInternal(const void * handle,size_t handle_size,SOCK * sock,const SSOCK_Init * init,TSOCK_Flags flags)6481 EIO_Status SOCK_CreateOnTopInternal(const void*       handle,
6482                                     size_t            handle_size,
6483                                     SOCK*             sock,
6484                                     const SSOCK_Init* init,
6485                                     TSOCK_Flags       flags)
6486 {
6487     EIO_Status status;
6488     *sock = 0;
6489     status = s_CreateOnTop(handle, handle_size, sock, init, flags);
6490     assert(!*sock == !(status == eIO_Success));
6491     return status;
6492 }
6493 
6494 
SOCK_CreateOnTopEx(const void * handle,size_t handle_size,SOCK * sock,const void * data,size_t size,TSOCK_Flags flags)6495 extern EIO_Status SOCK_CreateOnTopEx(const void* handle,
6496                                      size_t      handle_size,
6497                                      SOCK*       sock,
6498                                      const void* data,
6499                                      size_t      size,
6500                                      TSOCK_Flags flags)
6501 {
6502     SSOCK_Init init;
6503     memset(&init, 0, sizeof(init));
6504     init.data = data;
6505     init.size = size;
6506     return SOCK_CreateOnTopInternal(handle, handle_size, sock,
6507                                     &init, flags);
6508 }
6509 
6510 
SOCK_CreateOnTop(const void * handle,size_t handle_size,SOCK * sock)6511 extern EIO_Status SOCK_CreateOnTop(const void* handle,
6512                                    size_t      handle_size,
6513                                    SOCK*       sock)
6514 {
6515     return SOCK_CreateOnTopInternal(handle, handle_size, sock,
6516                                     0, fSOCK_LogDefault);
6517 }
6518 
6519 
SOCK_Reconnect(SOCK sock,const char * host,unsigned short port,const STimeout * timeout)6520 extern EIO_Status SOCK_Reconnect(SOCK            sock,
6521                                  const char*     host,
6522                                  unsigned short  port,
6523                                  const STimeout* timeout)
6524 {
6525     char _id[MAXIDLEN];
6526 
6527     if (sock->type == eDatagram) {
6528         CORE_LOGF_X(52, eLOG_Error,
6529                     ("%s[SOCK::Reconnect] "
6530                      " Datagram socket",
6531                      s_ID(sock, _id)));
6532         assert(0);
6533         return eIO_InvalidArg;
6534     }
6535 
6536 #ifdef NCBI_OS_UNIX
6537     if (sock->path[0]  &&  (host  ||  port)) {
6538         CORE_LOGF_X(53, eLOG_Error,
6539                     ("%s[SOCK::Reconnect] "
6540                      " Unable to reconnect UNIX socket as INET at \"%s:%hu\"",
6541                      s_ID(sock, _id), host ? host : "", port));
6542         assert(0);
6543         return eIO_InvalidArg;
6544     }
6545 #endif /*NCBI_OS_UNIX*/
6546 
6547     /* special treatment for server-side socket */
6548     if (sock->side == eSOCK_Server) {
6549         if (!host  ||  !port) {
6550             CORE_LOGF_X(51, eLOG_Error,
6551                         ("%s[SOCK::Reconnect] "
6552                          " Attempt to reconnect server-side socket as"
6553                          " client one to its peer address",
6554                          s_ID(sock, _id)));
6555             return eIO_InvalidArg;
6556         }
6557     }
6558 
6559     /* close the socket if necessary */
6560     if (sock->sock != SOCK_INVALID) {
6561         s_Close(sock, 0, fSOCK_KeepNone);
6562         /* likely NOOP */
6563         BUF_Erase(sock->r_buf);
6564         BUF_Erase(sock->w_buf);
6565     }
6566 
6567     /* connect */
6568     sock->id++;
6569     sock->side      = eSOCK_Client;
6570     sock->n_read    = 0;
6571     sock->n_written = 0;
6572     if (host  &&  sock->sslctx) {
6573         if (sock->sslctx->host)
6574             free((void*) sock->sslctx->host);
6575         sock->sslctx->host = *host  &&  !SOCK_isip(host) ? strdup(host) : 0;
6576     }
6577     return s_Connect(sock, host, port, timeout);
6578 }
6579 
6580 
SOCK_Shutdown(SOCK sock,EIO_Event dir)6581 extern EIO_Status SOCK_Shutdown(SOCK      sock,
6582                                 EIO_Event dir)
6583 {
6584     char       _id[MAXIDLEN];
6585     EIO_Status status;
6586 
6587     if (sock->sock == SOCK_INVALID) {
6588         CORE_LOGF_X(54, eLOG_Error,
6589                     ("%s[SOCK::Shutdown] "
6590                      " Invalid socket",
6591                      s_ID(sock, _id)));
6592         return eIO_Closed;
6593     }
6594     if (sock->type == eDatagram) {
6595         CORE_LOGF_X(55, eLOG_Error,
6596                     ("%s[SOCK::Shutdown] "
6597                      " Datagram socket",
6598                      s_ID(sock, _id)));
6599         assert(0);
6600         return eIO_InvalidArg;
6601     }
6602     if (!dir  ||  (EIO_Event)(dir | eIO_ReadWrite) != eIO_ReadWrite) {
6603         CORE_LOGF_X(15, eLOG_Error,
6604                     ("%s[SOCK::Shutdown] "
6605                      " Invalid direction #%u",
6606                      s_ID(sock, _id), (unsigned int) dir));
6607         return eIO_InvalidArg;
6608     }
6609 
6610     status = s_Shutdown(sock, dir, SOCK_GET_TIMEOUT(sock, c));
6611     if (s_ErrHook  &&  status != eIO_Success) {
6612         SSOCK_ErrInfo info;
6613         char          addr[40];
6614         memset(&info, 0, sizeof(info));
6615         info.type = eSOCK_ErrIO;
6616         info.sock = sock;
6617         if (sock->port) {
6618             SOCK_ntoa(sock->host, addr, sizeof(addr));
6619             info.host =       addr;
6620             info.port = sock->port;
6621         }
6622 #ifdef NCBI_OS_UNIX
6623         else
6624             info.host = sock->path;
6625 #endif /*NCBI_OS_UNIX*/
6626         info.event = eIO_Close;
6627         info.status = status;
6628         s_ErrorCallback(&info);
6629     }
6630     return status;
6631 }
6632 
6633 
6634 /* NB: aka SOCK_Destroy() */
SOCK_Close(SOCK sock)6635 extern EIO_Status SOCK_Close(SOCK sock)
6636 {
6637     return SOCK_CloseEx(sock, 1/*destroy*/);
6638 }
6639 
6640 
SOCK_CloseEx(SOCK sock,int destroy)6641 extern EIO_Status SOCK_CloseEx(SOCK sock, int/*bool*/ destroy)
6642 {
6643     EIO_Status status;
6644     if (!sock)
6645         return eIO_InvalidArg;
6646     if (sock->sock == SOCK_INVALID)
6647         status = eIO_Closed;
6648     else if (s_Initialized > 0)
6649         status = s_Close(sock, 0, fSOCK_KeepNone);
6650     else {
6651         if (sock->sslctx)
6652             sock->sslctx->sess = 0;  /*NB: session may leak out, if deinited*/
6653         sock->sock = SOCK_INVALID;
6654         status = eIO_Success;
6655     }
6656     /* likely NOOP */
6657     BUF_Erase(sock->r_buf);
6658     BUF_Erase(sock->w_buf);
6659     if (destroy) {
6660         if (sock->sslctx) {
6661             if (sock->sslctx->host)
6662                 free((void*) sock->sslctx->host);
6663             assert(!sock->sslctx->sess);
6664             free(sock->sslctx);
6665         }
6666         BUF_Destroy(sock->r_buf);
6667         BUF_Destroy(sock->w_buf);
6668         free(sock);
6669     }
6670     return status;
6671 }
6672 
6673 
SOCK_CloseOSHandle(const void * handle,size_t handle_size)6674 extern EIO_Status SOCK_CloseOSHandle(const void* handle, size_t handle_size)
6675 {
6676     EIO_Status    status;
6677     struct linger lgr;
6678     TSOCK_Handle  fd;
6679 
6680     if (!handle  ||  handle_size != sizeof(fd))
6681         return eIO_InvalidArg;
6682 
6683     memcpy(&fd, handle, sizeof(fd));
6684     if (fd == SOCK_INVALID)
6685         return eIO_Closed;
6686 
6687     /* drop all possible hold-ups w/o checks */
6688     lgr.l_linger = 0;  /* RFC 793, Abort */
6689     lgr.l_onoff  = 1;
6690     setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*) &lgr, sizeof(lgr));
6691 #ifdef TCP_LINGER2
6692     {{
6693         int no = -1;
6694         setsockopt(fd, IPPROTO_TCP, TCP_LINGER2, (char*) &no, sizeof(no));
6695     }}
6696 #endif /*TCP_LINGER2*/
6697 
6698     status = eIO_Success;
6699     for (;;) { /* close persistently - retry if interrupted by a signal */
6700         int error;
6701 
6702         if (SOCK_CLOSE(fd) == 0)
6703             break;
6704 
6705         /* error */
6706         if (s_Initialized <= 0)
6707             break;
6708         error = SOCK_ERRNO;
6709 #ifdef NCBI_OS_MSWIN
6710         if (error == WSANOTINITIALISED) {
6711             s_Initialized = -1/*deinited*/;
6712             break;
6713         }
6714 #endif /*NCBI_OS_MSWIN*/
6715         if (error == SOCK_ENOTCONN    ||
6716             error == SOCK_ENETRESET   ||
6717             error == SOCK_ECONNRESET  ||
6718             error == SOCK_ECONNABORTED) {
6719             status = eIO_Closed;
6720             break;
6721         }
6722         if (error != SOCK_EINTR) {
6723             status = error == SOCK_ETIMEDOUT ? eIO_Timeout : eIO_Unknown;
6724             break;
6725         }
6726         /* Maybe in an Ex version of this call someday...
6727         if (s_InterruptOnSignal == eOn) {
6728             status = eIO_Interrupt;
6729             break;
6730         }
6731         */
6732     }
6733     return status;
6734 }
6735 
6736 
SOCK_Wait(SOCK sock,EIO_Event event,const STimeout * timeout)6737 extern EIO_Status SOCK_Wait(SOCK            sock,
6738                             EIO_Event       event,
6739                             const STimeout* timeout)
6740 {
6741     char       _id[MAXIDLEN];
6742     EIO_Status status;
6743 
6744     if (timeout == kDefaultTimeout) {
6745         assert(0);
6746         return eIO_InvalidArg;
6747     }
6748     if (sock->sock == SOCK_INVALID) {
6749         CORE_LOGF_X(56, eLOG_Error,
6750                     ("%s[SOCK::Wait] "
6751                      " Invalid socket",
6752                      s_ID(sock, _id)));
6753         return eIO_Unknown;
6754     }
6755 
6756     /* check against already shutdown socket there */
6757     switch (event) {
6758     case eIO_Open:
6759         if (sock->type == eDatagram)
6760             return eIO_Success/*always connected*/;
6761         if (!sock->connected  ||  sock->pending) {
6762             struct timeval tv;
6763             return s_WaitConnected(sock, s_to2tv(timeout, &tv));
6764         }
6765         if (sock->r_status == eIO_Success  &&  sock->w_status == eIO_Success)
6766             return sock->eof ? eIO_Unknown : eIO_Success;
6767         if (sock->r_status == eIO_Closed   &&  sock->w_status == eIO_Closed)
6768             return eIO_Closed;
6769         return eIO_Unknown;
6770 
6771     case eIO_Read:
6772         if (BUF_Size(sock->r_buf) != 0)
6773             return eIO_Success;
6774         if (sock->type == eDatagram)
6775             return eIO_Closed;
6776         if (sock->r_status == eIO_Closed) {
6777             CORE_LOGF_X(57, eLOG_Warning,
6778                         ("%s[SOCK::Wait(R)] "
6779                          " Socket already %s",
6780                          s_ID(sock, _id), sock->eof ? "closed" : "shut down"));
6781             return eIO_Closed;
6782         }
6783         if (sock->eof)
6784             return eIO_Closed;
6785         break;
6786 
6787     case eIO_Write:
6788         if (sock->type == eDatagram)
6789             return eIO_Success;
6790         if (sock->w_status == eIO_Closed) {
6791             CORE_LOGF_X(58, eLOG_Warning,
6792                         ("%s[SOCK::Wait(W)] "
6793                          " Socket already shut down",
6794                          s_ID(sock, _id)));
6795             return eIO_Closed;
6796         }
6797         break;
6798 
6799     case eIO_ReadWrite:
6800         if (sock->type == eDatagram  ||  BUF_Size(sock->r_buf) != 0)
6801             return eIO_Success;
6802         if ((sock->r_status == eIO_Closed  ||  sock->eof)  &&
6803             (sock->w_status == eIO_Closed)) {
6804             if (sock->r_status == eIO_Closed) {
6805                 CORE_LOGF_X(59, eLOG_Warning,
6806                             ("%s[SOCK::Wait(RW)] "
6807                              " Socket already shut down",
6808                              s_ID(sock, _id)));
6809             }
6810             return eIO_Closed;
6811         }
6812         if (sock->r_status == eIO_Closed  ||  sock->eof) {
6813             if (sock->r_status == eIO_Closed) {
6814                 CORE_LOGF_X(60, eLOG_Warning,
6815                             ("%s[SOCK::Wait(RW)] "
6816                              " Socket already %s",
6817                              s_ID(sock, _id), sock->eof
6818                              ? "closed" : "shut down for reading"));
6819             }
6820             event = eIO_Write;
6821             break;
6822         }
6823         if (sock->w_status == eIO_Closed) {
6824             CORE_LOGF_X(61, eLOG_Warning,
6825                         ("%s[SOCK::Wait(RW)] "
6826                          " Socket already shut down for writing",
6827                          s_ID(sock, _id)));
6828             event = eIO_Read;
6829             break;
6830         }
6831         break;
6832 
6833     default:
6834         CORE_LOGF_X(62, eLOG_Error,
6835                     ("%s[SOCK::Wait] "
6836                      " Invalid event #%u",
6837                      s_ID(sock, _id), (unsigned int) event));
6838         assert(0);
6839         return eIO_InvalidArg;
6840     }
6841 
6842     assert(sock->type == eSocket);
6843     status = s_Wait(sock, event, timeout);
6844     if (s_ErrHook  &&  status != eIO_Success  &&  status != eIO_Timeout) {
6845         SSOCK_ErrInfo info;
6846         char          addr[40];
6847         memset(&info, 0, sizeof(info));
6848         info.type = eSOCK_ErrIO;
6849         info.sock = sock;
6850         if (sock->port) {
6851             SOCK_ntoa(sock->host, addr, sizeof(addr));
6852             info.host =       addr;
6853             info.port = sock->port;
6854         }
6855 #ifdef NCBI_OS_UNIX
6856         else
6857             info.host = sock->path;
6858 #endif /*NCBI_OS_UNIX*/
6859         info.event = event;
6860         info.status = status;
6861         s_ErrorCallback(&info);
6862     }
6863     return status;
6864 }
6865 
6866 
SOCK_Poll(size_t n,SSOCK_Poll polls[],const STimeout * timeout,size_t * n_ready)6867 extern EIO_Status SOCK_Poll(size_t          n,
6868                             SSOCK_Poll      polls[],
6869                             const STimeout* timeout,
6870                             size_t*         n_ready)
6871 {
6872     EIO_Status status;
6873     struct timeval tv;
6874     size_t         i;
6875 
6876 #ifdef NCBI_MONKEY
6877     SSOCK_Poll* orig_polls = polls; /* to know if 'polls' was replaced */
6878     EIO_Status  mnk_status = -1;
6879     size_t      orig_n = n;
6880     /* Not a poll function itself, just removes some of "polls" items */
6881     if (g_MONKEY_Poll)
6882         g_MONKEY_Poll(&n, &polls, &mnk_status);
6883     /* Even if call was intercepted, s_Select() continues as if nothing
6884        happened, because what we did was just removed some SSOCK_Poll pointers.
6885        The changes made in s_Select() will appear in the original array, but
6886        only for those SSOCK_Poll's that were left by Monkey */
6887 #endif /*NCBI_MONKEY*/
6888 
6889     if (n  &&  !polls) {
6890         if ( n_ready )
6891             *n_ready = 0;
6892         return eIO_InvalidArg;
6893     }
6894 
6895     for (i = 0;  i < n;  ++i) {
6896         SOCK sock = polls[i].sock;
6897         polls[i].revent =
6898             sock  &&  sock->type == eTrigger  &&  ((TRIGGER)sock)->isset.ptr
6899             ? polls[i].event
6900             : eIO_Open;
6901         if (!sock  ||  !(sock->type & eSocket)  ||  sock->sock == SOCK_INVALID)
6902             continue;
6903         if ((polls[i].event & eIO_Read)  &&  BUF_Size(sock->r_buf) != 0) {
6904             polls[i].revent = eIO_Read;
6905             continue;
6906         }
6907         if (sock->type != eSocket)
6908             continue;
6909         if ((polls[i].event == eIO_Read
6910             &&  (sock->r_status == eIO_Closed  ||  sock->eof))  ||
6911             (polls[i].event == eIO_Write
6912             &&   sock->w_status == eIO_Closed)) {
6913             polls[i].revent = eIO_Close;
6914         }
6915     }
6916 
6917     status = s_SelectStallsafe(n, polls, s_to2tv(timeout, &tv), n_ready);
6918 
6919 #ifdef NCBI_MONKEY
6920     if (orig_polls != polls) {
6921         /* Copy poll results to the original array if some were excluded */
6922         size_t orig_iter, new_iter;
6923         for (orig_iter = 0;  orig_iter < orig_n;  ++orig_iter) {
6924             for (new_iter = 0;  new_iter < n;  ++new_iter) {
6925                 if (orig_polls[orig_iter].sock == polls[new_iter].sock) {
6926                     orig_polls[orig_iter] = polls[new_iter];
6927                     break;
6928                 }
6929                 if (new_iter >= n)
6930                     orig_polls[orig_iter].revent = eIO_Open/*no event*/;
6931             }
6932         }
6933         free(polls);
6934         polls = orig_polls;
6935         if (mnk_status != -1)
6936             status = mnk_status;
6937     }
6938 #endif /*NCBI_MONKEY*/
6939 
6940     return status;
6941 }
6942 
6943 
SOCK_SetTimeout(SOCK sock,EIO_Event event,const STimeout * timeout)6944 extern EIO_Status SOCK_SetTimeout(SOCK            sock,
6945                                   EIO_Event       event,
6946                                   const STimeout* timeout)
6947 {
6948     char _id[MAXIDLEN];
6949 
6950     if (timeout == kDefaultTimeout) {
6951         assert(0);
6952         return eIO_InvalidArg;
6953     }
6954     switch (event) {
6955     case eIO_Read:
6956         sock->r_tv_set = s_to2tv(timeout, &sock->r_tv) ? 1 : 0;
6957         break;
6958     case eIO_Write:
6959         sock->w_tv_set = s_to2tv(timeout, &sock->w_tv) ? 1 : 0;
6960         break;
6961     case eIO_ReadWrite:
6962         sock->r_tv_set = s_to2tv(timeout, &sock->r_tv) ? 1 : 0;
6963         sock->w_tv_set = s_to2tv(timeout, &sock->w_tv) ? 1 : 0;
6964         break;
6965     case eIO_Close:
6966         sock->c_tv_set = s_to2tv(timeout, &sock->c_tv) ? 1 : 0;
6967         break;
6968     default:
6969         CORE_LOGF_X(63, eLOG_Error,
6970                     ("%s[SOCK::SetTimeout] "
6971                      " Invalid event #%u",
6972                      s_ID(sock, _id), (unsigned int) event));
6973         assert(0);
6974         return eIO_InvalidArg;
6975     }
6976     return eIO_Success;
6977 }
6978 
6979 
SOCK_GetTimeout(SOCK sock,EIO_Event event)6980 extern const STimeout* SOCK_GetTimeout(SOCK      sock,
6981                                        EIO_Event event)
6982 {
6983     char _id[MAXIDLEN];
6984 
6985     if (event == eIO_ReadWrite) {
6986         if      (!sock->r_tv_set)
6987             event = eIO_Write;
6988         else if (!sock->w_tv_set)
6989             event = eIO_Read;
6990         else {
6991             /* timeouts stored normalized */
6992             if (sock->r_tv.tv_sec > sock->w_tv.tv_sec)
6993                 return s_tv2to(&sock->w_tv, &sock->w_to);
6994             if (sock->w_tv.tv_sec > sock->r_tv.tv_sec)
6995                 return s_tv2to(&sock->r_tv, &sock->r_to);
6996             assert(sock->r_tv.tv_sec == sock->w_tv.tv_sec);
6997             return sock->r_tv.tv_usec > sock->w_tv.tv_usec
6998                 ? s_tv2to(&sock->w_tv, &sock->w_to)
6999                 : s_tv2to(&sock->r_tv, &sock->r_to);
7000         }
7001     }
7002     switch (event) {
7003     case eIO_Read:
7004         return sock->r_tv_set ? s_tv2to(&sock->r_tv, &sock->r_to) : 0;
7005     case eIO_Write:
7006         return sock->w_tv_set ? s_tv2to(&sock->w_tv, &sock->w_to) : 0;
7007     case eIO_Close:
7008         return sock->c_tv_set ? s_tv2to(&sock->c_tv, &sock->c_to) : 0;
7009     default:
7010         CORE_LOGF_X(64, eLOG_Error,
7011                     ("%s[SOCK::GetTimeout] "
7012                      " Invalid event #%u",
7013                      s_ID(sock, _id), (unsigned int) event));
7014         assert(0);
7015     }
7016     return 0/*kInfiniteTimeout*/;
7017 }
7018 
7019 
SOCK_Read(SOCK sock,void * buf,size_t size,size_t * n_read,EIO_ReadMethod how)7020 extern EIO_Status SOCK_Read(SOCK           sock,
7021                             void*          buf,
7022                             size_t         size,
7023                             size_t*        n_read,
7024                             EIO_ReadMethod how)
7025 {
7026     EIO_Status status;
7027     size_t     x_read;
7028     char       _id[MAXIDLEN];
7029 
7030     if (sock->sock != SOCK_INVALID) {
7031         switch (how) {
7032         case eIO_ReadPeek:
7033             status = s_Read(sock, buf, size, &x_read, 1/*peek*/);
7034             break;
7035 
7036         case eIO_ReadPlain:
7037             status = s_Read(sock, buf, size, &x_read, 0/*read*/);
7038             break;
7039 
7040         case eIO_ReadPersist:
7041             x_read = 0;
7042             do {
7043                 size_t xx_read;
7044                 status = s_Read(sock, (char*) buf + (buf ? x_read : 0),
7045                                 size, &xx_read, 0/*read*/);
7046                 x_read += xx_read;
7047                 size   -= xx_read;
7048             } while (size  &&  status == eIO_Success);
7049             break;
7050 
7051         default:
7052             CORE_LOGF_X(65, eLOG_Error,
7053                         ("%s[SOCK::Read] "
7054                          " Unsupported read method #%u",
7055                          s_ID(sock, _id), (unsigned int) how));
7056             status = eIO_NotSupported;
7057             x_read = 0;
7058             assert(0);
7059             break;
7060         }
7061     } else {
7062         CORE_LOGF_X(66, eLOG_Error,
7063                     ("%s[SOCK::Read] "
7064                      " Invalid socket",
7065                      s_ID(sock, _id)));
7066         status = eIO_Unknown;
7067         x_read = 0;
7068     }
7069 
7070     if ( n_read )
7071         *n_read = x_read;
7072     return status;
7073 }
7074 
7075 
7076 #define s_Pushback(s, b, n)  BUF_Pushback(&(s)->r_buf, b, n)
7077 
7078 
SOCK_ReadLine(SOCK sock,char * line,size_t size,size_t * n_read)7079 extern EIO_Status SOCK_ReadLine(SOCK    sock,
7080                                 char*   line,
7081                                 size_t  size,
7082                                 size_t* n_read)
7083 {
7084     unsigned int/*bool*/ cr_seen, done;
7085     EIO_Status  status;
7086     size_t      len;
7087 
7088     if ( n_read )
7089         *n_read = 0;
7090     if (!size  ||  !line) {
7091         assert(0);
7092         return eIO_InvalidArg;
7093     }
7094     if (sock->sock == SOCK_INVALID) {
7095         char _id[MAXIDLEN];
7096         CORE_LOGF_X(125, eLOG_Error,
7097                     ("%s[SOCK::ReadLine] "
7098                      " Invalid socket",
7099                      s_ID(sock, _id)));
7100         return eIO_Unknown;
7101     }
7102 
7103     cr_seen = done = 0/*false*/;
7104     len = 0;
7105     do {
7106         size_t i;
7107         char   w[1024], c;
7108         size_t x_size = BUF_Size(sock->r_buf);
7109         char*  x_buf  = size - len < sizeof(w) - cr_seen ? w : line + len;
7110         if (!x_size  ||  x_size > sizeof(w) - cr_seen)
7111             x_size = sizeof(w) - cr_seen;
7112         status = s_Read(sock, x_buf + cr_seen, x_size, &x_size, 0/*read*/);
7113         assert(status == eIO_Success  ||  !x_size);
7114         if (!x_size)
7115             done = 1/*true*/;
7116         else if (cr_seen)
7117             x_size++;
7118         i = cr_seen;
7119         while (i < x_size  &&  len < size) {
7120             c = x_buf[i++];
7121             if (c == '\n') {
7122                 cr_seen = 0/*false*/;
7123                 done = 1/*true*/;
7124                 break;
7125             }
7126             if (c == '\r'  &&  !cr_seen) {
7127                 cr_seen = 1/*true*/;
7128                 continue;
7129             }
7130             if (cr_seen)
7131                 line[len++] = '\r';
7132             cr_seen = 0/*false*/;
7133             if (len >= size) {
7134                 --i; /* have to read it again */
7135                 break;
7136             }
7137             if (c == '\r') {
7138                 cr_seen = 1/*true*/;
7139                 continue;
7140             } else if (!c) {
7141                 done = 1/*true*/;
7142                 break;
7143             }
7144             line[len++] = c;
7145         }
7146         if (len >= size)
7147             done = 1/*true*/;
7148         if (done  &&  cr_seen) {
7149             c = '\r';
7150             if (!s_Pushback(sock, &c, 1))
7151                 status = eIO_Unknown;
7152         }
7153         if (i < x_size  &&  !s_Pushback(sock, &x_buf[i], x_size - i))
7154             status = eIO_Unknown;
7155     } while (!done  &&  status == eIO_Success);
7156 
7157     if (len < size)
7158         line[len] = '\0';
7159     if ( n_read )
7160         *n_read = len;
7161 
7162     return status;
7163 }
7164 
7165 
SOCK_Pushback(SOCK sock,const void * buf,size_t size)7166 extern EIO_Status SOCK_Pushback(SOCK        sock,
7167                                 const void* buf,
7168                                 size_t      size)
7169 {
7170     if (size  &&  !buf) {
7171         assert(0);
7172         return eIO_InvalidArg;
7173     }
7174     if (sock->sock == SOCK_INVALID) {
7175         char _id[MAXIDLEN];
7176         CORE_LOGF_X(67, eLOG_Error,
7177                     ("%s[SOCK::Pushback] "
7178                      " Invalid socket",
7179                      s_ID(sock, _id)));
7180         return eIO_Closed;
7181     }
7182     return s_Pushback(sock, buf, size) ? eIO_Success : eIO_Unknown;
7183 }
7184 
7185 
SOCK_Status(SOCK sock,EIO_Event direction)7186 extern EIO_Status SOCK_Status(SOCK      sock,
7187                               EIO_Event direction)
7188 {
7189     if (sock) {
7190         switch (direction) {
7191         case eIO_Open:
7192         case eIO_Read:
7193         case eIO_Write:
7194             if (sock->sock == SOCK_INVALID)
7195                 return direction/*!eIO_Open*/ ? eIO_Unknown : eIO_Closed;
7196             if (!sock->connected  ||  sock->pending)
7197                 return eIO_Timeout;
7198             if (direction == eIO_Read) {
7199                 return sock->type == eSocket  &&  sock->eof
7200                     ? eIO_Closed : (EIO_Status) sock->r_status;
7201             }
7202             if (direction == eIO_Write)
7203                 return (EIO_Status) sock->w_status;
7204             return eIO_Success;
7205         default:
7206             break;
7207         }
7208     }
7209     return eIO_InvalidArg;
7210 }
7211 
7212 
SOCK_Write(SOCK sock,const void * buf,size_t size,size_t * n_written,EIO_WriteMethod how)7213 extern EIO_Status SOCK_Write(SOCK            sock,
7214                              const void*     buf,
7215                              size_t          size,
7216                              size_t*         n_written,
7217                              EIO_WriteMethod how)
7218 {
7219     EIO_Status status;
7220     size_t     x_written;
7221     char       _id[MAXIDLEN];
7222 
7223     if (size  &&  !buf) {
7224         if ( n_written )
7225             *n_written = 0;
7226         assert(0);
7227         return eIO_InvalidArg;
7228     }
7229     if (sock->sock != SOCK_INVALID) {
7230         switch (how) {
7231         case eIO_WriteOutOfBand:
7232             if (sock->type == eDatagram) {
7233                 CORE_LOGF_X(68, eLOG_Error,
7234                             ("%s[SOCK::Write] "
7235                              " OOB not supported for datagrams",
7236                              s_ID(sock, _id)));
7237                 status = eIO_NotSupported;
7238                 x_written = 0;
7239                 break;
7240             }
7241             /*FALLTHRU*/
7242 
7243         case eIO_WritePlain:
7244             status = s_Write(sock, buf, size, &x_written,
7245                              how == eIO_WriteOutOfBand ? 1 : 0);
7246             break;
7247 
7248         case eIO_WritePersist:
7249             x_written = 0;
7250             do {
7251                 size_t xx_written;
7252                 status = s_Write(sock, (char*) buf + x_written,
7253                                  size, &xx_written, 0);
7254                 x_written += xx_written;
7255                 size      -= xx_written;
7256             } while (size  &&  status == eIO_Success);
7257             break;
7258 
7259         default:
7260             CORE_LOGF_X(69, eLOG_Error,
7261                         ("%s[SOCK::Write] "
7262                          " Unsupported write method #%u",
7263                          s_ID(sock, _id), (unsigned int) how));
7264             status = eIO_NotSupported;
7265             x_written = 0;
7266             assert(0);
7267             break;
7268         }
7269     } else {
7270         CORE_LOGF_X(70, eLOG_Error,
7271                     ("%s[SOCK::Write] "
7272                      " Invalid socket",
7273                      s_ID(sock, _id)));
7274         status = eIO_Closed;
7275         x_written = 0;
7276     }
7277 
7278     if ( n_written )
7279         *n_written = x_written;
7280     return status;
7281 }
7282 
7283 
SOCK_Abort(SOCK sock)7284 extern EIO_Status SOCK_Abort(SOCK sock)
7285 {
7286     char _id[MAXIDLEN];
7287 
7288     if (sock->sock == SOCK_INVALID) {
7289         CORE_LOGF_X(71, eLOG_Warning,
7290                     ("%s[SOCK::Abort] "
7291                      " Invalid socket",
7292                      s_ID(sock, _id)));
7293         return eIO_Closed;
7294     }
7295     if (sock->type == eDatagram) {
7296         CORE_LOGF_X(72, eLOG_Error,
7297                     ("%s[SOCK::Abort] "
7298                      " Datagram socket",
7299                      s_ID(sock, _id)));
7300         assert(0);
7301         return eIO_InvalidArg;
7302     }
7303 
7304     return s_Close_(sock, 1/*abort*/, fSOCK_KeepNone);
7305 }
7306 
7307 
SOCK_GetLocalPortEx(SOCK sock,int trueport,ENH_ByteOrder byte_order)7308 extern unsigned short SOCK_GetLocalPortEx(SOCK          sock,
7309                                           int/*bool*/   trueport,
7310                                           ENH_ByteOrder byte_order)
7311 {
7312     unsigned short port;
7313 
7314     if (!sock  ||  sock->sock == SOCK_INVALID)
7315         return 0;
7316 
7317 #ifdef NCBI_OS_UNIX
7318     if (sock->path[0])
7319         return 0/*UNIX socket*/;
7320 #endif /*NCBI_OS_UNIX*/
7321 
7322     if (trueport  ||  !sock->myport) {
7323         port = s_GetLocalPort(sock->sock);
7324         if (!trueport)
7325             sock->myport = port; /*cache it*/
7326     } else
7327         port = sock->myport;
7328     return byte_order == eNH_HostByteOrder ? port : htons(port);
7329 }
7330 
7331 
SOCK_GetLocalPort(SOCK sock,ENH_ByteOrder byte_order)7332 extern unsigned short SOCK_GetLocalPort(SOCK          sock,
7333                                         ENH_ByteOrder byte_order)
7334 {
7335     return SOCK_GetLocalPortEx(sock, 0/*false*/, byte_order);
7336 }
7337 
7338 
SOCK_GetPeerAddress(SOCK sock,unsigned int * host,unsigned short * port,ENH_ByteOrder byte_order)7339 extern void SOCK_GetPeerAddress(SOCK            sock,
7340                                 unsigned int*   host,
7341                                 unsigned short* port,
7342                                 ENH_ByteOrder   byte_order)
7343 {
7344     if (!sock) {
7345         if ( host )
7346             *host = 0;
7347         if ( port )
7348             *port = 0;
7349         return;
7350     }
7351     if ( host ) {
7352         *host = byte_order == eNH_HostByteOrder
7353             ? ntohl(sock->host) :       sock->host;
7354     }
7355     if ( port ) {
7356         *port = byte_order == eNH_HostByteOrder
7357             ?       sock->port  : ntohs(sock->port);
7358     }
7359 }
7360 
7361 
SOCK_GetRemotePort(SOCK sock,ENH_ByteOrder byte_order)7362 extern unsigned short SOCK_GetRemotePort(SOCK          sock,
7363                                          ENH_ByteOrder byte_order)
7364 {
7365     unsigned short port;
7366     SOCK_GetPeerAddress(sock, 0, &port, byte_order);
7367     return port;
7368 }
7369 
7370 
SOCK_GetPeerAddressString(SOCK sock,char * buf,size_t bufsize)7371 extern char* SOCK_GetPeerAddressString(SOCK   sock,
7372                                        char*  buf,
7373                                        size_t bufsize)
7374 {
7375     return SOCK_GetPeerAddressStringEx(sock, buf, bufsize, eSAF_Full);
7376 }
7377 
7378 
SOCK_GetPeerAddressStringEx(SOCK sock,char * buf,size_t bufsize,ESOCK_AddressFormat format)7379 extern char* SOCK_GetPeerAddressStringEx(SOCK                sock,
7380                                          char*               buf,
7381                                          size_t              bufsize,
7382                                          ESOCK_AddressFormat format)
7383 {
7384     char   port[10];
7385     size_t len;
7386 
7387     if (!buf  ||  !bufsize)
7388         return 0/*error*/;
7389     if (!sock) {
7390         *buf = '\0';
7391         return 0/*error*/;
7392     }
7393     switch (format) {
7394     case eSAF_Full:
7395 #ifdef NCBI_OS_UNIX
7396         if (sock->path[0]) {
7397             if ((len = strlen(sock->path)) < bufsize)
7398                 memcpy(buf, sock->path, len + 1);
7399             else
7400                 return 0/*error*/;
7401         } else
7402 #endif /*NCBI_OS_UNIX*/
7403             if (!SOCK_HostPortToString(sock->host, sock->port, buf, bufsize))
7404                 return 0/*error*/;
7405         break;
7406     case eSAF_Port:
7407 #ifdef NCBI_OS_UNIX
7408         if (sock->path[0])
7409             *buf = '\0';
7410         else
7411 #endif /*NCBI_OS_UNIX*/
7412             if ((len = (size_t) sprintf(port, "%hu", sock->port)) < bufsize)
7413                 memcpy(buf, port, len + 1);
7414             else
7415                 return 0/*error*/;
7416         break;
7417     case eSAF_IP:
7418 #ifdef NCBI_OS_UNIX
7419         if (sock->path[0])
7420             *buf = '\0';
7421         else
7422 #endif /*NCBI_OS_UNIX*/
7423             if (SOCK_ntoa(sock->host, buf, bufsize) != 0)
7424                 return 0/*error*/;
7425         break;
7426     default:
7427         return 0/*error*/;
7428     }
7429     return buf;
7430 }
7431 
7432 
SOCK_GetOSHandleEx(SOCK sock,void * handle,size_t handle_size,EOwnership ownership)7433 extern EIO_Status SOCK_GetOSHandleEx(SOCK       sock,
7434                                      void*      handle,
7435                                      size_t     handle_size,
7436                                      EOwnership ownership)
7437 {
7438     EIO_Status   status;
7439     TSOCK_Handle fd;
7440 
7441     if (!handle  ||  handle_size != sizeof(sock->sock)) {
7442         char _id[MAXIDLEN];
7443         CORE_LOGF_X(73, eLOG_Error,
7444                     ("%s[SOCK::GetOSHandle] "
7445                      " Invalid handle%s %lu",
7446                      s_ID(sock, _id),
7447                      handle ? " size"                     : "",
7448                      handle ? (unsigned long) handle_size : 0));
7449         assert(0);
7450         return eIO_InvalidArg;
7451     }
7452     if (!sock) {
7453         fd = SOCK_INVALID;
7454         memcpy(handle, &fd, handle_size);
7455         return eIO_InvalidArg;
7456     }
7457     fd = sock->sock;
7458     memcpy(handle, &fd, handle_size);
7459     if (s_Initialized <= 0  ||  fd == SOCK_INVALID)
7460         status = eIO_Closed;
7461     else if (ownership != eTakeOwnership)
7462         status = eIO_Success;
7463     else {
7464         sock->keep = 1/*true*/;
7465         status = s_Close(sock, 0, fSOCK_KeepNone);
7466     }
7467     return status;
7468 }
7469 
7470 
SOCK_GetOSHandle(SOCK sock,void * handle,size_t handle_size)7471 extern EIO_Status SOCK_GetOSHandle(SOCK   sock,
7472                                    void*  handle,
7473                                    size_t handle_size)
7474 {
7475     return SOCK_GetOSHandleEx(sock, handle, handle_size, eNoOwnership);
7476 }
7477 
7478 
SOCK_SetReadOnWriteAPI(ESwitch on_off)7479 extern ESwitch SOCK_SetReadOnWriteAPI(ESwitch on_off)
7480 {
7481     ESwitch old = s_ReadOnWrite;
7482     if (on_off != eDefault)
7483         s_ReadOnWrite = on_off;
7484     return old;
7485 }
7486 
7487 
SOCK_SetReadOnWrite(SOCK sock,ESwitch on_off)7488 extern ESwitch SOCK_SetReadOnWrite(SOCK sock, ESwitch on_off)
7489 {
7490     if (sock->type != eDatagram) {
7491         ESwitch old = (ESwitch) sock->r_on_w;
7492         sock->r_on_w = on_off;
7493         return old;
7494     }
7495     return eDefault;
7496 }
7497 
7498 
7499 /*ARGSUSED*/
SOCK_SetCork(SOCK sock,int on_off)7500 extern void SOCK_SetCork(SOCK sock, int/*bool*/ on_off)
7501 {
7502     char _id[MAXIDLEN];
7503 
7504     if (sock->sock == SOCK_INVALID) {
7505         CORE_LOGF_X(158, eLOG_Warning,
7506                     ("%s[SOCK::SetCork] "
7507                      " Invalid socket",
7508                      s_ID(sock, _id)));
7509         return;
7510     }
7511     if (sock->type == eDatagram) {
7512         CORE_LOGF_X(159, eLOG_Error,
7513                     ("%s[SOCK::SetCork] "
7514                      " Datagram socket",
7515                      s_ID(sock, _id)));
7516         assert(0);
7517         return;
7518     }
7519 
7520 #if defined(TCP_CORK)  &&  !defined(NCBI_OS_CYGWIN)
7521     if (setsockopt(sock->sock, IPPROTO_TCP, TCP_CORK,
7522                    (char*) &on_off, sizeof(on_off)) != 0) {
7523         int error = SOCK_ERRNO;
7524         const char* strerr = SOCK_STRERROR(error);
7525         CORE_LOGF_ERRNO_EXX(160, eLOG_Warning,
7526                             error, strerr ? strerr : "",
7527                             ("%s[SOCK::SetCork] "
7528                              " Failed setsockopt(%sTCP_CORK)",
7529                              s_ID(sock, _id), on_off ? "" : "!"));
7530         UTIL_ReleaseBuffer(strerr);
7531     }
7532 #  ifdef TCP_NOPUSH
7533     /* try to avoid 5 second delay on BSD systems (incl. Darwin) */
7534     if (!on_off)
7535         (void) send(sock->sock, _id/*dontcare*/, 0, 0);
7536 #  endif /*TCP_NOPUSH*/
7537 #endif /*TCP_CORK && !NCBI_OS_CYGWIN*/
7538 }
7539 
7540 
7541 /*ARGSUSED*/
SOCK_DisableOSSendDelay(SOCK sock,int on_off)7542 extern void SOCK_DisableOSSendDelay(SOCK sock, int/*bool*/ on_off)
7543 {
7544     char _id[MAXIDLEN];
7545 
7546     if (sock->sock == SOCK_INVALID) {
7547         CORE_LOGF_X(156, eLOG_Warning,
7548                     ("%s[SOCK::DisableOSSendDelay] "
7549                      " Invalid socket",
7550                      s_ID(sock, _id)));
7551         return;
7552     }
7553     if (sock->type == eDatagram) {
7554         CORE_LOGF_X(157, eLOG_Error,
7555                     ("%s[SOCK::DisableOSSendDelay] "
7556                      " Datagram socket",
7557                      s_ID(sock, _id)));
7558         assert(0);
7559         return;
7560     }
7561 
7562 #ifdef TCP_NODELAY
7563     if (setsockopt(sock->sock, IPPROTO_TCP, TCP_NODELAY,
7564                    (char*) &on_off, sizeof(on_off)) != 0) {
7565         int error = SOCK_ERRNO;
7566         const char* strerr = SOCK_STRERROR(error);
7567         CORE_LOGF_ERRNO_EXX(75, eLOG_Warning,
7568                             error, strerr ? strerr : "",
7569                             ("%s[SOCK::DisableOSSendDelay] "
7570                              " Failed setsockopt(%sTCP_NODELAY)",
7571                              s_ID(sock, _id), on_off ? "" : "!"));
7572         UTIL_ReleaseBuffer(strerr);
7573     }
7574 #endif /*TCP_NODELAY*/
7575 }
7576 
7577 
7578 
7579 /******************************************************************************
7580  *  DATAGRAM SOCKET
7581  */
7582 
7583 
DSOCK_Create(SOCK * sock)7584 extern EIO_Status DSOCK_Create(SOCK* sock)
7585 {
7586     return DSOCK_CreateEx(sock, fSOCK_LogDefault);
7587 }
7588 
7589 
DSOCK_CreateEx(SOCK * sock,TSOCK_Flags flags)7590 extern EIO_Status DSOCK_CreateEx(SOCK* sock, TSOCK_Flags flags)
7591 {
7592     TSOCK_Handle fd;
7593     int          type;
7594 #ifdef NCBI_OS_MSWIN
7595     HANDLE       event;
7596 #endif /*NCBI_OS_MSWIN*/
7597     int          error;
7598     SOCK         x_sock;
7599     unsigned int x_id = ++s_ID_Counter * 1000;
7600 
7601     *sock = 0;
7602 
7603     /* initialize internals */
7604     if ((flags & fSOCK_Secure)  ||  s_InitAPI(0) != eIO_Success)
7605         return eIO_NotSupported;
7606 
7607     type  = SOCK_DGRAM;
7608 #ifdef SOCK_NONBLOCK
7609     type |= SOCK_NONBLOCK;
7610 #endif /*SOCK_NONBLOCK*/
7611 #ifdef SOCK_CLOEXEC
7612     if (!(flags & fSOCK_KeepOnExec))
7613         type |= SOCK_CLOEXEC;
7614 #endif /*SOCK_CLOEXEC*/
7615     /* create new datagram socket */
7616     if ((fd = socket(AF_INET, type, 0)) == SOCK_INVALID) {
7617         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
7618         CORE_LOGF_ERRNO_EXX(76, eLOG_Error,
7619                             error, strerr ? strerr : "",
7620                             ("DSOCK#%u[?]: [DSOCK::Create] "
7621                              " Cannot create socket",
7622                              x_id));
7623         UTIL_ReleaseBuffer(strerr);
7624         return eIO_Unknown;
7625     }
7626 
7627 #if defined(NCBI_OS_MSWIN)
7628     if (!(event = WSACreateEvent())) {
7629         DWORD err = GetLastError();
7630         const char* strerr = s_WinStrerror(err);
7631         CORE_LOGF_ERRNO_EXX(139, eLOG_Error,
7632                             err, strerr ? strerr : "",
7633                             ("DSOCK#%u[%u]: [DSOCK::Create] "
7634                              " Failed to create IO event",
7635                              x_id, (unsigned int) fd));
7636         UTIL_ReleaseBufferOnHeap(strerr);
7637         SOCK_CLOSE(fd);
7638         return eIO_Unknown;
7639     }
7640     /* NB: WSAEventSelect() sets non-blocking automatically */
7641     if (WSAEventSelect(fd, event, SOCK_EVENTS) != 0) {
7642         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
7643         CORE_LOGF_ERRNO_EXX(140, eLOG_Error,
7644                             error, strerr ? strerr : "",
7645                             ("DSOCK#%u[%u]: [DSOCK::Create] "
7646                              " Failed to bind IO event",
7647                              x_id, (unsigned int) fd));
7648         UTIL_ReleaseBuffer(strerr);
7649         SOCK_CLOSE(fd);
7650         WSACloseEvent(event);
7651         return eIO_Unknown;
7652     }
7653 #elif !defined(SOCK_NONBLOCK)
7654     /* set to non-blocking mode */
7655     if (!s_SetNonblock(fd, 1/*true*/)) {
7656         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
7657         CORE_LOGF_ERRNO_EXX(77, eLOG_Error,
7658                             error, strerr ? strerr : "",
7659                             ("DSOCK#%u[%u]: [DSOCK::Create] "
7660                              " Cannot set socket to non-blocking mode",
7661                              x_id, (unsigned int) fd));
7662         UTIL_ReleaseBuffer(strerr);
7663         SOCK_CLOSE(fd);
7664         return eIO_Unknown;
7665     }
7666 #endif
7667 
7668     if (!(x_sock = (SOCK) calloc(1, sizeof(*x_sock)))) {
7669         SOCK_CLOSE(fd);
7670 #ifdef NCBI_OS_MSWIN
7671         WSACloseEvent(event);
7672 #endif /*NCBI_OS_MSWIN*/
7673         return eIO_Unknown;
7674     }
7675 
7676     /* success... */
7677     x_sock->sock      = fd;
7678     x_sock->id        = x_id;
7679     /* no host and port - not "connected" */
7680     x_sock->type      = eDatagram;
7681     x_sock->side      = eSOCK_Client;
7682     x_sock->log       = flags & (fSOCK_LogDefault | fSOCK_LogOn);
7683     x_sock->keep      = flags & fSOCK_KeepOnClose ? 1/*true*/ : 0/*false*/;
7684     x_sock->i_on_sig  = flags & fSOCK_InterruptOnSignal ? eOn : eDefault;
7685 #ifdef NCBI_OS_MSWIN
7686     x_sock->event     = event;
7687     x_sock->writable  = 1/*true*/;
7688 #endif /*NCBI_OS_MSWIN*/
7689     x_sock->crossexec = flags & fSOCK_KeepOnExec  ? 1/*true*/ : 0/*false*/;
7690     /* all timeout bits cleared - infinite */
7691     BUF_SetChunkSize(&x_sock->r_buf, SOCK_BUF_CHUNK_SIZE);
7692     BUF_SetChunkSize(&x_sock->w_buf, SOCK_BUF_CHUNK_SIZE);
7693 
7694 #ifndef SOCK_CLOEXEC
7695     if (!x_sock->crossexec  &&  !s_SetCloexec(fd, 1/*true*/)) {
7696         const char* strerr;
7697         char _id[MAXIDLEN];
7698 #  ifdef NCBI_OS_MSWIN
7699         DWORD err = GetLastError();
7700         strerr = s_WinStrerror(err);
7701         error = err;
7702 #  else
7703         error = errno;
7704         strerr = SOCK_STRERROR(error);
7705 #  endif /*NCBI_OS_MSWIN*/
7706         CORE_LOGF_ERRNO_EXX(130, eLOG_Warning,
7707                             error, strerr ? strerr : "",
7708                             ("%s[DSOCK::Create]  Cannot set"
7709                              " socket close-on-exec mode",
7710                              s_ID(*sock, _id)));
7711 #  ifdef NCBI_OS_MSWIN
7712         UTIL_ReleaseBufferOnHeap(strerr);
7713 #  else
7714         UTIL_ReleaseBuffer(strerr);
7715 #  endif /*NCBI_OS_MSWIN*/
7716     }
7717 #endif /*!SOCK_CLOEXEC*/
7718 
7719     /* statistics & logging */
7720     if (x_sock->log == eOn  ||  (x_sock->log == eDefault  &&  s_Log == eOn))
7721         s_DoLog(eLOG_Note, x_sock, eIO_Open, 0, 0, 0);
7722 
7723     *sock = x_sock;
7724     return eIO_Success;
7725 }
7726 
7727 
DSOCK_Bind(SOCK sock,unsigned short port)7728 extern EIO_Status DSOCK_Bind(SOCK sock, unsigned short port)
7729 {
7730     char _id[MAXIDLEN];
7731     int error;
7732     union {
7733         struct sockaddr    sa;
7734         struct sockaddr_in in;
7735     } addr;
7736 
7737     if (sock->sock == SOCK_INVALID) {
7738         CORE_LOGF_X(79, eLOG_Error,
7739                     ("%s[DSOCK::Bind] "
7740                      " Invalid socket",
7741                      s_ID(sock, _id)));
7742         return eIO_Closed;
7743     }
7744     if (sock->type != eDatagram) {
7745         CORE_LOGF_X(78, eLOG_Error,
7746                     ("%s[DSOCK::Bind] "
7747                      " Not a datagram socket",
7748                      s_ID(sock, _id)));
7749         assert(0);
7750         return eIO_InvalidArg;
7751     }
7752 
7753     /* bind */
7754     memset(&addr, 0, sizeof(addr));
7755 #ifdef HAVE_SIN_LEN
7756     addr.in.sin_len         = (TSOCK_socklen_t) sizeof(addr.in);
7757 #endif /*HAVE_SIN_LEN*/
7758     addr.in.sin_family      = AF_INET;
7759     addr.in.sin_addr.s_addr = htonl(INADDR_ANY);
7760     addr.in.sin_port        = htons(port);
7761     if (bind(sock->sock, &addr.sa, sizeof(addr.in)) != 0) {
7762         const char* strerr = SOCK_STRERROR(error = SOCK_ERRNO);
7763         CORE_LOGF_ERRNO_EXX(80, error != SOCK_EADDRINUSE
7764                             ? eLOG_Error : eLOG_Trace,
7765                             error, strerr ? strerr : "",
7766                             ("%s[DSOCK::Bind] "
7767                              " Failed bind(:%hu)",
7768                              s_ID(sock, _id), port));
7769         UTIL_ReleaseBuffer(strerr);
7770         return error != SOCK_EADDRINUSE ? eIO_Unknown : eIO_Closed;
7771     }
7772     if (!port) {
7773         TSOCK_socklen_t addrlen = sizeof(addr);
7774         assert(addr.sa.sa_family == AF_INET);
7775         error = getsockname(sock->sock, &addr.sa, &addrlen) != 0
7776             ? SOCK_ERRNO : 0;
7777         if (error  ||  addr.sa.sa_family != AF_INET  ||  !addr.in.sin_port) {
7778             const char* strerr = SOCK_STRERROR(error);
7779             CORE_LOGF_ERRNO_EXX(114, eLOG_Error,
7780                                 error, strerr ? strerr : "",
7781                                 ("%s[DSOCK::Bind] "
7782                                  " Cannot obtain a free socket port",
7783                                  s_ID(sock, _id)));
7784             UTIL_ReleaseBuffer(strerr);
7785             return eIO_Closed;
7786         }
7787         port = ntohs(addr.in.sin_port);
7788     }
7789 
7790     /* statistics & logging */
7791     if (sock->log == eOn  ||  (sock->log == eDefault  &&  s_Log == eOn))
7792         s_DoLog(eLOG_Note, sock, eIO_Open, 0, 0, &addr.in);
7793 
7794     sock->myport = port;
7795     return eIO_Success;
7796 }
7797 
7798 
DSOCK_Connect(SOCK sock,const char * hostname,unsigned short port)7799 extern EIO_Status DSOCK_Connect(SOCK sock,
7800                                 const char* hostname, unsigned short port)
7801 {
7802     struct sockaddr_in peer;
7803     char _id[MAXIDLEN];
7804     unsigned int host;
7805     char addr[40];
7806 
7807     if (sock->sock == SOCK_INVALID) {
7808         CORE_LOGF_X(82, eLOG_Error,
7809                     ("%s[DSOCK::Connect] "
7810                      " Invalid socket",
7811                      s_ID(sock, _id)));
7812         return eIO_Closed;
7813     }
7814     if (sock->type != eDatagram) {
7815         CORE_LOGF_X(81, eLOG_Error,
7816                     ("%s[DSOCK::Connect] "
7817                      " Not a datagram socket",
7818                      s_ID(sock, _id)));
7819         assert(0);
7820         return eIO_InvalidArg;
7821     }
7822 
7823     /* drop all pending data */
7824     BUF_Erase(sock->r_buf);
7825     BUF_Erase(sock->w_buf);
7826     sock->r_len = 0;
7827     sock->w_len = 0;
7828     sock->eof = 0;
7829     sock->id++;
7830 
7831     if (!hostname  ||  !*hostname)
7832         host = 0;
7833     else if (!(host = s_gethostbyname(hostname, 0, (ESwitch) sock->log))) {
7834         CORE_LOGF_X(83, eLOG_Error,
7835                     ("%s[DSOCK::Connect] "
7836                      " Failed SOCK_gethostbyname(\"%.*s\")",
7837                      s_ID(sock, _id), CONN_HOST_LEN, hostname));
7838         return eIO_Unknown;
7839     }
7840 
7841     if (!host != !port) {
7842         if (port) {
7843             assert(!host);
7844             sprintf(addr, ":%hu", port);
7845         } else
7846             *addr = '\0';
7847         CORE_LOGF_X(84, eLOG_Error,
7848                     ("%s[DSOCK::Connect] "
7849                      " Address \"%.*s%s\" incomplete, missing %s",
7850                      s_ID(sock, _id), CONN_HOST_LEN, host ? hostname : "",
7851                      addr, port ? "host" : "port"));
7852         return eIO_InvalidArg;
7853     }
7854 
7855     /* connect (non-empty address) or drop association (on empty address) */
7856     memset(&peer, 0, sizeof(peer));
7857 #ifdef HAVE_SIN_LEN
7858     peer.sin_len             = (TSOCK_socklen_t) sizeof(peer);
7859 #endif /*HAVE_SIN_LEN*/
7860     if (host/*  &&  port*/) {
7861         peer.sin_family      = AF_INET;
7862         peer.sin_addr.s_addr =       host;
7863         peer.sin_port        = htons(port);
7864     }
7865 #ifdef AF_UNSPEC
7866     else
7867         peer.sin_family      = AF_UNSPEC;
7868 #endif /*AF_UNSPEC*/
7869     if (connect(sock->sock, (struct sockaddr*) &peer, sizeof(peer)) != 0) {
7870         int error = SOCK_ERRNO;
7871         const char* strerr = SOCK_STRERROR(error);
7872         if (host)
7873             SOCK_HostPortToString(host, port, addr, sizeof(addr));
7874         else
7875             *addr = '\0';
7876         CORE_LOGF_ERRNO_EXX(85, eLOG_Error,
7877                             error, strerr ? strerr : "",
7878                             ("%s[DSOCK::Connect] "
7879                              " Failed %sconnect%s%s%s",
7880                              s_ID(sock, _id), *addr ? "" : "to dis",
7881                              &"("[!*addr], addr, &")"[!*addr]));
7882         UTIL_ReleaseBuffer(strerr);
7883         return eIO_Closed;
7884     }
7885 
7886     /* statistics & logging */
7887     if (sock->log == eOn  ||  (sock->log == eDefault  &&  s_Log == eOn))
7888         s_DoLog(eLOG_Note, sock, eIO_Open, "", 0, &peer);
7889 
7890     sock->host = host;
7891     sock->port = port;
7892     return eIO_Success;
7893 }
7894 
7895 
DSOCK_WaitMsg(SOCK sock,const STimeout * timeout)7896 extern EIO_Status DSOCK_WaitMsg(SOCK sock, const STimeout* timeout)
7897 {
7898     char           _id[MAXIDLEN];
7899     EIO_Status     status;
7900     SSOCK_Poll     poll;
7901     struct timeval tv;
7902 
7903     if (sock->sock == SOCK_INVALID) {
7904         CORE_LOGF_X(96, eLOG_Error,
7905                     ("%s[DSOCK::WaitMsg] "
7906                      " Invalid socket",
7907                      s_ID(sock, _id)));
7908         return eIO_Unknown;
7909     }
7910     if (sock->type != eDatagram) {
7911         CORE_LOGF_X(95, eLOG_Error,
7912                     ("%s[DSOCK::WaitMsg] "
7913                      " Not a datagram socket",
7914                      s_ID(sock, _id)));
7915         assert(0);
7916         return eIO_InvalidArg;
7917     }
7918 
7919     poll.sock   = sock;
7920     poll.event  = eIO_Read;
7921     poll.revent = eIO_Open;
7922     status = s_Select(1, &poll, s_to2tv(timeout, &tv), 1/*asis*/);
7923     assert(poll.event == eIO_Read);
7924     if (status == eIO_Success  &&  poll.revent != eIO_Read) {
7925         assert(poll.revent == eIO_Close);
7926         status = eIO_Closed;
7927     }
7928     if (s_ErrHook  &&  status != eIO_Success  &&  status != eIO_Timeout) {
7929         SSOCK_ErrInfo info;
7930         char          addr[40];
7931         memset(&info, 0, sizeof(info));
7932         info.type = eSOCK_ErrIO;
7933         info.sock = sock;
7934         SOCK_ntoa(sock->host, addr, sizeof(addr));
7935         info.host =       addr;
7936         info.port = sock->port;
7937         info.event = eIO_Read;
7938         info.status = status;
7939         s_ErrorCallback(&info);
7940     }
7941     return status;
7942 }
7943 
7944 
DSOCK_RecvMsg(SOCK sock,void * buf,size_t bufsize,size_t msgsize,size_t * msglen,unsigned int * sender_addr,unsigned short * sender_port)7945 extern EIO_Status DSOCK_RecvMsg(SOCK            sock,
7946                                 void*           buf,
7947                                 size_t          bufsize,
7948                                 size_t          msgsize,
7949                                 size_t*         msglen,
7950                                 unsigned int*   sender_addr,
7951                                 unsigned short* sender_port)
7952 {
7953     char       _id[MAXIDLEN];
7954     EIO_Status status;
7955 
7956     if ( msglen )
7957         *msglen = 0;
7958     if ( sender_addr )
7959         *sender_addr = 0;
7960     if ( sender_port )
7961         *sender_port = 0;
7962 
7963     if (sock->sock == SOCK_INVALID) {
7964         CORE_LOGF_X(93, eLOG_Error,
7965                     ("%s[DSOCK::RecvMsg] "
7966                      " Invalid socket",
7967                      s_ID(sock, _id)));
7968         return eIO_Unknown;
7969     }
7970     if (sock->type != eDatagram) {
7971         CORE_LOGF_X(92, eLOG_Error,
7972                     ("%s[DSOCK::RecvMsg] "
7973                      " Not a datagram socket",
7974                      s_ID(sock, _id)));
7975         assert(0);
7976         return eIO_InvalidArg;
7977     }
7978 
7979     status = s_RecvMsg(sock, buf, bufsize, msgsize, msglen,
7980                        sender_addr, sender_port);
7981     if (s_ErrHook  &&  status != eIO_Success) {
7982         SSOCK_ErrInfo info;
7983         char          addr[40];
7984         memset(&info, 0, sizeof(info));
7985         info.type = eSOCK_ErrIO;
7986         info.sock = sock;
7987         SOCK_ntoa(sock->host, addr, sizeof(addr));
7988         info.host =       addr;
7989         info.port = sock->port;
7990         info.event = eIO_Read;
7991         info.status = status;
7992         s_ErrorCallback(&info);
7993     }
7994     return status;
7995 }
7996 
7997 
DSOCK_SendMsg(SOCK sock,const char * host,unsigned short port,const void * data,size_t datalen)7998 extern EIO_Status DSOCK_SendMsg(SOCK           sock,
7999                                 const char*    host,
8000                                 unsigned short port,
8001                                 const void*    data,
8002                                 size_t         datalen)
8003 {
8004     char       _id[MAXIDLEN];
8005     EIO_Status status;
8006 
8007     if (sock->sock == SOCK_INVALID) {
8008         CORE_LOGF_X(87, eLOG_Error,
8009                     ("%s[DSOCK::SendMsg] "
8010                      " Invalid socket",
8011                      s_ID(sock, _id)));
8012         return eIO_Closed;
8013     }
8014     if (sock->type != eDatagram) {
8015         CORE_LOGF_X(86, eLOG_Error,
8016                     ("%s[DSOCK::SendMsg] "
8017                      " Not a datagram socket",
8018                      s_ID(sock, _id)));
8019         assert(0);
8020         return eIO_InvalidArg;
8021     }
8022 
8023     status = s_SendMsg(sock, host, port, data, datalen);
8024     if (s_ErrHook  &&  status != eIO_Success) {
8025         SSOCK_ErrInfo info;
8026         char          addr[40];
8027         memset(&info, 0, sizeof(info));
8028         info.type = eSOCK_ErrIO;
8029         info.sock = sock;
8030         SOCK_ntoa(sock->host, addr, sizeof(addr));
8031         info.host =       addr;
8032         info.port = sock->port;
8033         info.event = eIO_Write;
8034         info.status = status;
8035         s_ErrorCallback(&info);
8036     }
8037     return status;
8038 }
8039 
8040 
DSOCK_WipeMsg(SOCK sock,EIO_Event direction)8041 extern EIO_Status DSOCK_WipeMsg(SOCK sock, EIO_Event direction)
8042 {
8043     char _id[MAXIDLEN];
8044     EIO_Status status;
8045 
8046     if (sock->sock == SOCK_INVALID) {
8047         CORE_LOGF_X(98, eLOG_Error,
8048                     ("%s[DSOCK::WipeMsg] "
8049                      " Invalid socket",
8050                      s_ID(sock, _id)));
8051         return eIO_Closed;
8052     }
8053     if (sock->type != eDatagram) {
8054         CORE_LOGF_X(97, eLOG_Error,
8055                     ("%s[DSOCK::WipeMsg] "
8056                      " Not a datagram socket",
8057                      s_ID(sock, _id)));
8058         assert(0);
8059         return eIO_InvalidArg;
8060     }
8061 
8062     switch (direction) {
8063     case eIO_Read:
8064         sock->r_len = 0;
8065         BUF_Erase(sock->r_buf);
8066         sock->r_status = status = eIO_Success;
8067         break;
8068     case eIO_Write:
8069         sock->r_len = 0;
8070         BUF_Erase(sock->w_buf);
8071         sock->w_status = status = eIO_Success;
8072         break;
8073     default:
8074         CORE_LOGF_X(99, eLOG_Error,
8075                     ("%s[DSOCK::WipeMsg] "
8076                      " Invalid direction #%u",
8077                      s_ID(sock, _id), (unsigned int) direction));
8078         assert(0);
8079         status = eIO_InvalidArg;
8080         break;
8081     }
8082 
8083     return status;
8084 }
8085 
8086 
DSOCK_SetBroadcast(SOCK sock,int broadcast)8087 extern EIO_Status DSOCK_SetBroadcast(SOCK sock, int/*bool*/ broadcast)
8088 {
8089     char _id[MAXIDLEN];
8090 
8091     if (sock->sock == SOCK_INVALID) {
8092         CORE_LOGF_X(101, eLOG_Error,
8093                     ("%s[DSOCK::SetBroadcast] "
8094                      " Invalid socket",
8095                      s_ID(sock, _id)));
8096         return eIO_Closed;
8097     }
8098     if (sock->type != eDatagram) {
8099         CORE_LOGF_X(100, eLOG_Error,
8100                     ("%s[DSOCK::SetBroadcast] "
8101                      " Not a datagram socket",
8102                      s_ID(sock, _id)));
8103         assert(0);
8104         return eIO_InvalidArg;
8105     }
8106 
8107 #if defined(NCBI_OS_UNIX)  ||  defined(NCBI_OS_MSWIN)
8108     /* setsockopt() is not implemented for MAC (in MIT socket emulation lib) */
8109     {{
8110 #  ifdef NCBI_OS_MSWIN
8111         BOOL bcast = !!broadcast;
8112 #  else
8113         int  bcast = !!broadcast;
8114 #  endif /*NCBI_OS_MSWIN*/
8115         if (setsockopt(sock->sock, SOL_SOCKET, SO_BROADCAST,
8116                        (const char*) &bcast, sizeof(bcast)) != 0) {
8117             int error = SOCK_ERRNO;
8118             const char* strerr = SOCK_STRERROR(error);
8119             CORE_LOGF_ERRNO_EXX(102, eLOG_Error,
8120                                 error, strerr ? strerr : "",
8121                                 ("%s[DSOCK::SetBroadcast] "
8122                                  " Failed setsockopt(%sBROADCAST)",
8123                                  s_ID(sock, _id), bcast ? "" : "NO"));
8124             UTIL_ReleaseBuffer(strerr);
8125             return eIO_Unknown;
8126         }
8127     }}
8128 #else
8129     return eIO_NotSupported;
8130 #endif /*NCBI_OS_UNIX || NXBI_OS_MSWIN*/
8131     return eIO_Success;
8132 }
8133 
8134 
DSOCK_GetMessageCount(SOCK sock,EIO_Event direction)8135 extern TNCBI_BigCount DSOCK_GetMessageCount(SOCK sock, EIO_Event direction)
8136 {
8137     if (sock  &&  sock->type == eDatagram) {
8138         switch (direction) {
8139         case eIO_Read:
8140             return sock->n_in;
8141         case eIO_Write:
8142             return sock->n_out;
8143         default:
8144             assert(0);
8145             break;
8146         }
8147     }
8148     return 0;
8149 }
8150 
8151 
8152 
8153 /******************************************************************************
8154  *  CLASSIFICATION & STATS
8155  */
8156 
8157 
SOCK_IsDatagram(SOCK sock)8158 extern int/*bool*/ SOCK_IsDatagram(SOCK sock)
8159 {
8160     return sock &&  sock->sock != SOCK_INVALID  &&  sock->type == eDatagram;
8161 }
8162 
8163 
SOCK_IsClientSide(SOCK sock)8164 extern int/*bool*/ SOCK_IsClientSide(SOCK sock)
8165 {
8166     return sock &&  sock->sock != SOCK_INVALID  &&  sock->side == eSOCK_Client;
8167 }
8168 
8169 
SOCK_IsServerSide(SOCK sock)8170 extern int/*bool*/ SOCK_IsServerSide(SOCK sock)
8171 {
8172     return sock &&  sock->sock != SOCK_INVALID  &&  sock->side == eSOCK_Server;
8173 }
8174 
8175 
SOCK_IsUNIX(SOCK sock)8176 extern int/*bool*/ SOCK_IsUNIX(SOCK sock)
8177 {
8178 #ifdef NCBI_OS_UNIX
8179     return sock &&  sock->sock != SOCK_INVALID  &&  sock->path[0];
8180 #else
8181     return 0/*false*/;
8182 #endif /*NCBI_OS_UNIX*/
8183 }
8184 
8185 
SOCK_IsSecure(SOCK sock)8186 extern int/*bool*/ SOCK_IsSecure(SOCK sock)
8187 {
8188     return sock &&  sock->sock != SOCK_INVALID  &&  sock->sslctx;
8189 }
8190 
8191 
SOCK_GetPosition(SOCK sock,EIO_Event direction)8192 extern TNCBI_BigCount SOCK_GetPosition(SOCK sock, EIO_Event direction)
8193 {
8194     if (sock) {
8195         switch (direction) {
8196         case eIO_Read:
8197             if (sock->type == eDatagram)
8198                 return sock->r_len - BUF_Size(sock->r_buf);
8199             return sock->n_read    - (TNCBI_BigCount) BUF_Size(sock->r_buf);
8200         case eIO_Write:
8201             if (sock->type == eDatagram)
8202                 return BUF_Size(sock->w_buf);
8203             return sock->n_written + (TNCBI_BigCount)          sock->w_len;
8204         default:
8205             assert(0);
8206             break;
8207         }
8208     }
8209     return 0;
8210 }
8211 
8212 
SOCK_GetCount(SOCK sock,EIO_Event direction)8213 extern TNCBI_BigCount SOCK_GetCount(SOCK sock, EIO_Event direction)
8214 {
8215     if (sock) {
8216         switch (direction) {
8217         case eIO_Read:
8218             return sock->type == eDatagram ? sock->r_len : sock->n_read;
8219         case eIO_Write:
8220             return sock->type == eDatagram ? sock->w_len : sock->n_written;
8221         default:
8222             assert(0);
8223             break;
8224         }
8225     }
8226     return 0;
8227 }
8228 
8229 
SOCK_GetTotalCount(SOCK sock,EIO_Event direction)8230 extern TNCBI_BigCount SOCK_GetTotalCount(SOCK sock, EIO_Event direction)
8231 {
8232     if (sock) {
8233         switch (direction) {
8234         case eIO_Read:
8235             return sock->type != eDatagram ? sock->n_in  : sock->n_read;
8236         case eIO_Write:
8237             return sock->type != eDatagram ? sock->n_out : sock->n_written;
8238         default:
8239             assert(0);
8240             break;
8241         }
8242     }
8243     return 0;
8244 }
8245 
8246 
8247 
8248 /******************************************************************************
8249  *  SOCKET SETTINGS
8250  */
8251 
8252 
SOCK_SetInterruptOnSignalAPI(ESwitch on_off)8253 extern ESwitch SOCK_SetInterruptOnSignalAPI(ESwitch on_off)
8254 {
8255     ESwitch old = s_InterruptOnSignal;
8256     if (on_off != eDefault)
8257         s_InterruptOnSignal = on_off;
8258     return old;
8259 }
8260 
8261 
SOCK_SetInterruptOnSignal(SOCK sock,ESwitch on_off)8262 extern ESwitch SOCK_SetInterruptOnSignal(SOCK sock, ESwitch on_off)
8263 {
8264     ESwitch old = (ESwitch) sock->i_on_sig;
8265     sock->i_on_sig = on_off;
8266     return old;
8267 }
8268 
8269 
SOCK_SetReuseAddressAPI(ESwitch on_off)8270 extern ESwitch SOCK_SetReuseAddressAPI(ESwitch on_off)
8271 {
8272     ESwitch old = s_ReuseAddress;
8273     if (on_off != eDefault)
8274         s_ReuseAddress = on_off;
8275     return old;
8276 }
8277 
8278 
SOCK_SetReuseAddress(SOCK sock,int on_off)8279 extern void SOCK_SetReuseAddress(SOCK sock, int/*bool*/ on_off)
8280 {
8281     if (sock->sock != SOCK_INVALID && !s_SetReuseAddress(sock->sock, on_off)) {
8282         char _id[MAXIDLEN];
8283         int error = SOCK_ERRNO;
8284         const char* strerr = SOCK_STRERROR(error);
8285         CORE_LOGF_ERRNO_EXX(74, eLOG_Warning,
8286                             error, strerr ? strerr : "",
8287                             ("%s[SOCK::SetReuseAddress] "
8288                              " Failed setsockopt(%sREUSEADDR)",
8289                              s_ID(sock, _id), on_off ? "" : "NO"));
8290         UTIL_ReleaseBuffer(strerr);
8291     }
8292 }
8293 
8294 
SOCK_SetDataLoggingAPI(ESwitch log)8295 extern ESwitch SOCK_SetDataLoggingAPI(ESwitch log)
8296 {
8297     ESwitch old = s_Log;
8298     if (log != eDefault)
8299         s_Log = log;
8300     return old;
8301 }
8302 
8303 
SOCK_SetDataLogging(SOCK sock,ESwitch log)8304 extern ESwitch SOCK_SetDataLogging(SOCK sock, ESwitch log)
8305 {
8306     ESwitch old = (ESwitch) sock->log;
8307     sock->log = log;
8308     return old;
8309 }
8310 
8311 
8312 
8313 /******************************************************************************
8314  *  GENERIC POLLABLE API
8315  */
8316 
8317 
POLLABLE_Poll(size_t n,SPOLLABLE_Poll polls[],const STimeout * timeout,size_t * n_ready)8318 extern EIO_Status POLLABLE_Poll(size_t          n,
8319                                 SPOLLABLE_Poll  polls[],
8320                                 const STimeout* timeout,
8321                                 size_t*         n_ready)
8322 {
8323     return SOCK_Poll(n, (SSOCK_Poll*) polls, timeout, n_ready);
8324 }
8325 
8326 
POLLABLE_FromTRIGGER(TRIGGER trigger)8327 extern POLLABLE POLLABLE_FromTRIGGER(TRIGGER trigger)
8328 {
8329     assert(!trigger  ||  trigger->type == eTrigger);
8330     return (POLLABLE) trigger;
8331 }
8332 
8333 
POLLABLE_FromLSOCK(LSOCK lsock)8334 extern POLLABLE POLLABLE_FromLSOCK(LSOCK lsock)
8335 {
8336     assert(!lsock  ||  lsock->type == eListening);
8337     return (POLLABLE) lsock;
8338 }
8339 
8340 
POLLABLE_FromSOCK(SOCK sock)8341 extern POLLABLE POLLABLE_FromSOCK(SOCK sock)
8342 {
8343     assert(!sock  ||  (sock->type & eSocket));
8344     return (POLLABLE) sock;
8345 }
8346 
8347 
POLLABLE_ToTRIGGER(POLLABLE poll)8348 extern TRIGGER POLLABLE_ToTRIGGER(POLLABLE poll)
8349 {
8350     TRIGGER trigger = (TRIGGER) poll;
8351     return trigger  &&  trigger->type == eTrigger ? trigger : 0;
8352 }
8353 
8354 
POLLABLE_ToLSOCK(POLLABLE poll)8355 extern LSOCK POLLABLE_ToLSOCK(POLLABLE poll)
8356 {
8357     LSOCK lsock = (LSOCK) poll;
8358     return lsock  &&  lsock->type == eListening ? lsock : 0;
8359 }
8360 
8361 
POLLABLE_ToSOCK(POLLABLE poll)8362 extern SOCK  POLLABLE_ToSOCK(POLLABLE poll)
8363 {
8364     SOCK sock = (SOCK) poll;
8365     return sock  &&  (sock->type & eSocket) ? sock : 0;
8366 }
8367 
8368 
8369 
8370 /******************************************************************************
8371  *  BSD-LIKE INTERFACE
8372  */
8373 
8374 
SOCK_ntoa(unsigned int host,char * buf,size_t bufsize)8375 extern int SOCK_ntoa(unsigned int host,
8376                      char*        buf,
8377                      size_t       bufsize)
8378 {
8379     if (buf  &&  bufsize) {
8380         char x_buf[16/*sizeof("255.255.255.255")*/];
8381         const unsigned char* b = (const unsigned char*) &host;
8382         int len = sprintf(x_buf, "%u.%u.%u.%u", b[0], b[1], b[2], b[3]);
8383         assert(0 < len  &&  (size_t) len < sizeof(x_buf));
8384         if ((size_t) len < bufsize) {
8385             memcpy(buf, x_buf, (size_t) len + 1);
8386             return 0/*success*/;
8387         }
8388         *buf = '\0';
8389     }
8390     return -1/*failed*/;
8391 }
8392 
8393 
SOCK_isip(const char * str)8394 extern int/*bool*/ SOCK_isip(const char* str)
8395 {
8396     return SOCK_isipEx(str, 0/*nofullquad*/);
8397 }
8398 
8399 
SOCK_isipEx(const char * str,int fullquad)8400 extern int/*bool*/ SOCK_isipEx(const char* str, int/*bool*/ fullquad)
8401 {
8402     unsigned long val;
8403     int dots;
8404 
8405     if (!str  ||  !*str)
8406         return 0/*false*/;
8407 
8408     dots = 0;
8409     for (;;) {
8410         char* e;
8411         if (!isdigit((unsigned char)(*str)))
8412             return 0/*false*/;
8413         errno = 0;
8414         val = strtoul(str, &e, 0);
8415         if (errno  ||  str == e)
8416             return 0/*false*/;
8417         str = e;
8418         if (*str != '.')
8419             break;
8420         if (++dots > 3)
8421             return 0/*false*/;
8422         if (val > 255)
8423             return 0/*false*/;
8424         ++str;
8425     }
8426 
8427     return !*str  &&
8428         (!fullquad  ||  dots == 3)  &&  val <= (0xFFFFFFFFUL >> (dots << 3));
8429 }
8430 
8431 
SOCK_IsLoopbackAddress(unsigned int ip)8432 extern int/*bool*/ SOCK_IsLoopbackAddress(unsigned int ip)
8433 {
8434     if (ip == htonl(INADDR_LOOPBACK))
8435         return 1/*true*/;
8436     /* 127/8 */
8437     if (ip) {
8438         unsigned int addr = ntohl(ip);
8439 #if defined(IN_CLASSA) && defined(IN_CLASSA_NET) && defined(IN_CLASSA_NSHIFT)
8440         return IN_CLASSA(addr)
8441             &&  (addr & IN_CLASSA_NET) == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT);
8442 #else
8443         return !((addr & 0xFF000000) ^ (INADDR_LOOPBACK-1));
8444 #endif /*IN_CLASSA && IN_CLASSA_NET && IN_CLASSA_NSHIFT*/
8445     }
8446     return 0/*false*/;
8447 }
8448 
8449 
SOCK_HostToNetLong(unsigned int value)8450 extern unsigned int SOCK_HostToNetLong(unsigned int value)
8451 {
8452     return htonl(value);
8453 }
8454 
8455 
SOCK_HostToNetShort(unsigned short value)8456 extern unsigned short SOCK_HostToNetShort(unsigned short value)
8457 {
8458     return htons(value);
8459 }
8460 
8461 
SOCK_htonl(unsigned int value)8462 extern unsigned int SOCK_htonl(unsigned int value)
8463 {
8464     return htonl(value);
8465 }
8466 
8467 
SOCK_htons(unsigned short value)8468 extern unsigned short SOCK_htons(unsigned short value)
8469 {
8470     return htons(value);
8471 }
8472 
8473 
SOCK_gethostnameEx(char * buf,size_t bufsize,ESwitch log)8474 extern int SOCK_gethostnameEx(char* buf, size_t bufsize, ESwitch log)
8475 {
8476     assert(buf  &&  bufsize > 0);
8477 
8478     /* initialize internals */
8479     if (s_InitAPI(0) != eIO_Success) {
8480         buf[0] = buf[bufsize - 1] = '\0';
8481         return -1/*failure*/;
8482     }
8483 
8484     return s_gethostname(buf, bufsize, log == eDefault ? s_Log : log);
8485 }
8486 
8487 
SOCK_gethostname(char * buf,size_t bufsize)8488 extern int SOCK_gethostname(char* buf, size_t bufsize)
8489 {
8490     return SOCK_gethostnameEx(buf, bufsize, s_Log);
8491 }
8492 
8493 
SOCK_gethostbynameEx(const char * hostname,ESwitch log)8494 extern unsigned int SOCK_gethostbynameEx(const char* hostname, ESwitch log)
8495 {
8496     /* initialize internals */
8497     if (s_InitAPI(0) != eIO_Success)
8498         return 0;
8499 
8500     return s_gethostbyname(hostname, 0, log == eDefault ? s_Log : log);
8501 }
8502 
8503 
SOCK_gethostbyname(const char * hostname)8504 extern unsigned int SOCK_gethostbyname(const char* hostname)
8505 {
8506     return SOCK_gethostbynameEx(hostname, s_Log);
8507 }
8508 
8509 
SOCK_gethostbyaddrEx(unsigned int host,char * buf,size_t bufsize,ESwitch log)8510 extern const char* SOCK_gethostbyaddrEx(unsigned int host,
8511                                         char*        buf,
8512                                         size_t       bufsize,
8513                                         ESwitch      log)
8514 {
8515     assert(buf  &&  bufsize > 0);
8516 
8517     /* initialize internals */
8518     if (s_InitAPI(0) != eIO_Success) {
8519         *buf = '\0';
8520         return 0;
8521     }
8522 
8523     return s_gethostbyaddr(host, buf, bufsize, log == eDefault ? s_Log : log);
8524 }
8525 
8526 
SOCK_gethostbyaddr(unsigned int host,char * buf,size_t bufsize)8527 extern const char* SOCK_gethostbyaddr(unsigned int host,
8528                                       char*        buf,
8529                                       size_t       bufsize)
8530 {
8531     return SOCK_gethostbyaddrEx(host, buf, bufsize, s_Log);
8532 }
8533 
8534 
SOCK_GetLoopbackAddress(void)8535 extern unsigned int SOCK_GetLoopbackAddress(void)
8536 {
8537     return SOCK_LOOPBACK;
8538 }
8539 
8540 
SOCK_GetLocalHostAddress(ESwitch reget)8541 extern unsigned int SOCK_GetLocalHostAddress(ESwitch reget)
8542 {
8543     /* initialize internals */
8544     if (s_InitAPI(0) != eIO_Success)
8545         return 0;
8546 
8547     return s_getlocalhostaddress(reget, s_Log);
8548 }
8549 
8550 
SOCK_StringToHostPortEx(const char * str,unsigned int * host,unsigned short * port,int flag)8551 const char* SOCK_StringToHostPortEx(const char*     str,
8552                                     unsigned int*   host,
8553                                     unsigned short* port,
8554                                     int/*bool*/     flag)
8555 {
8556     char x_buf[CONN_HOST_LEN + 1];
8557     unsigned short p;
8558     unsigned int h;
8559     const char* s;
8560     size_t len;
8561     size_t n;
8562 
8563     if ( host )
8564         *host = 0;
8565     if ( port )
8566         *port = 0;
8567     if (!*str)
8568         return 0;
8569 
8570     /* initialize internals */
8571     if (s_InitAPI(0) != eIO_Success)
8572         return 0;
8573 
8574     for (s = str;  *s;  ++s) {
8575         if (isspace((unsigned char)(*s))  ||  *s == ':')
8576             break;
8577     }
8578     if ((len = (size_t)(s - str)) > sizeof(x_buf) - 1)
8579         return 0;
8580     if (*s == ':') {
8581         long  i;
8582         char* e;
8583         if (isspace((unsigned char) s[1]))
8584             return str;
8585         errno = 0;
8586         i = strtol(++s, &e, 10);
8587         if (errno  ||  s == e  ||  i ^ (i & 0xFFFF)
8588             ||  (*e  &&  !isspace((unsigned char)(*e)))) {
8589             return str;
8590         }
8591         p = (unsigned short) i;
8592         n = (size_t)(e - s);
8593     } else {
8594         p = 0;
8595         n = 0;
8596     }
8597     if (len) {
8598         memcpy(x_buf, str, len);
8599         x_buf[len] = '\0';
8600         if ((h = inet_addr(x_buf)) == htonl(INADDR_NONE)
8601             &&  !(h = s_gethostbyname(x_buf, 1/*not-IP*/, s_Log))) {
8602             if (!flag)
8603                 return str;
8604             h = htonl(INADDR_NONE);
8605         }
8606         if ( host )
8607             *host = h;
8608     }
8609     if (port  &&  p)
8610         *port = p;
8611     return s + n;
8612 }
8613 
8614 
SOCK_StringToHostPort(const char * str,unsigned int * host,unsigned short * port)8615 extern const char* SOCK_StringToHostPort(const char*     str,
8616                                          unsigned int*   host,
8617                                          unsigned short* port)
8618 {
8619     return SOCK_StringToHostPortEx(str, host, port, 0/*false*/);
8620 }
8621 
8622 
SOCK_HostPortToString(unsigned int host,unsigned short port,char * buf,size_t bufsize)8623 extern size_t SOCK_HostPortToString(unsigned int   host,
8624                                     unsigned short port,
8625                                     char*          buf,
8626                                     size_t         bufsize)
8627 {
8628     char   x_buf[16/*sizeof("255.255.255.255")*/ + 6/*:port#*/];
8629     size_t len;
8630 
8631     if (!buf  ||  !bufsize)
8632         return 0;
8633     if (!host) {
8634         *x_buf = '\0';
8635         len = 0;
8636     } else if (SOCK_ntoa(host, x_buf, sizeof(x_buf)) != 0) {
8637         *buf = '\0';
8638         return 0;
8639     } else
8640         len = strlen(x_buf);
8641     if (port  ||  !host)
8642         len += (size_t) sprintf(x_buf + len, ":%hu", port);
8643     assert(len < sizeof(x_buf));
8644     if (len >= bufsize) {
8645         *buf = '\0';
8646         return 0;
8647     }
8648     memcpy(buf, x_buf, len + 1);
8649     return len;
8650 }
8651 
8652 
8653 
8654 /******************************************************************************
8655  *  SECURE SOCKET LAYER SUPPORT
8656  */
8657 
8658 
SOCK_SetupSSLInternal(FSSLSetup setup,int init)8659 void SOCK_SetupSSLInternal(FSSLSetup setup, int/*bool*/ init)
8660 {
8661     CORE_LOCK_WRITE;
8662 
8663     if (!setup  &&  !init)
8664         x_ShutdownSSL();
8665     else if (s_SSLSetup != setup  ||  (s_SSL  &&  init)) {
8666         if (s_SSLSetup) {
8667             const char* verb;
8668             if (!setup  &&  init) {
8669                 s_SSL = 0;  /* NB: race / memory leak if was non-NULL ! */
8670                 verb = "Must not";
8671             } else
8672                 verb = "Cannot";
8673             CORE_UNLOCK;
8674             CORE_LOGF(eLOG_Critical, ("%s reset SSL while it is in use",verb));
8675             return;
8676         }
8677         assert(!s_SSL);
8678         s_SSLSetup = s_Initialized < 0 ? 0 : setup;
8679     }
8680     g_CORE_Set |= eCORE_SetSSL;
8681 
8682     CORE_UNLOCK;
8683 }
8684 
8685 
SOCK_SetupSSL(FSSLSetup setup)8686 extern void SOCK_SetupSSL(FSSLSetup setup)
8687 {
8688     SOCK_SetupSSLInternal(setup, 0/*false*/);
8689 }
8690 
8691 
SOCK_SetupSSLEx(FSSLSetup setup)8692 extern EIO_Status SOCK_SetupSSLEx(FSSLSetup setup)
8693 {
8694     SOCK_SetupSSLInternal(setup, 0/*false*/);
8695     return setup ? s_InitAPI(1/*secure*/) : eIO_Success;
8696 }
8697 
8698 
SOCK_SSLName(void)8699 extern const char* SOCK_SSLName(void)
8700 {
8701     return !s_SSLSetup ? 0 : !s_SSL ? "" : s_SSL->Name;
8702 }
8703