1 /* $Id: ncbi_socket_cxx.cpp 594767 2019-10-09 14:55:40Z 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
27  *
28  * File Description:
29  *   C++ wrappers for the C "SOCK" API (UNIX, MS-Win, MacOS, Darwin)
30  *   Implementation of out-of-line methods
31  *
32  */
33 
34 #include <ncbi_pch.hpp>
35 #include "ncbi_assert.h"                // no _ASSERT()s, keep clean from xncbi
36 #include <connect/ncbi_socket_unix.hpp>
37 #include <limits.h>                     // for PATH_MAX
38 #if defined(NCBI_OS_MSWIN)  &&  !defined(PATH_MAX)
39 #  define PATH_MAX 512                  // will actually use less than 32 chars
40 #endif // NCBI_OS_MSWIN && !PATH_MAX
41 
42 
43 BEGIN_NCBI_SCOPE
44 
45 
46 /////////////////////////////////////////////////////////////////////////////
47 //  CTrigger::
48 //
49 
~CTrigger()50 CTrigger::~CTrigger()
51 {
52     if (m_Trigger)
53         TRIGGER_Close(m_Trigger);
54 }
55 
56 
57 
58 /////////////////////////////////////////////////////////////////////////////
59 //  CSocket::
60 //
61 
62 
CSocket(const string & host,unsigned short port,const STimeout * timeout,TSOCK_Flags flags)63 CSocket::CSocket(const string&   host,
64                  unsigned short  port,
65                  const STimeout* timeout,
66                  TSOCK_Flags     flags)
67     : m_IsOwned(eTakeOwnership),
68       r_timeout(0/*kInfiniteTimeout*/), w_timeout(0), c_timeout(0)
69 {
70     if (timeout  &&  timeout != kDefaultTimeout) {
71         oo_timeout = *timeout;
72         o_timeout  = &oo_timeout;
73     } else
74         o_timeout  = 0/*kInfiniteTimeout*/;
75     SOCK_CreateEx(host.c_str(), port, o_timeout, &m_Socket, 0, 0, flags);
76 }
77 
78 
CSocket(unsigned int host,unsigned short port,const STimeout * timeout,TSOCK_Flags flags)79 CSocket::CSocket(unsigned int    host,
80                  unsigned short  port,
81                  const STimeout* timeout,
82                  TSOCK_Flags     flags)
83     : m_IsOwned(eTakeOwnership),
84       r_timeout(0/*kInfiniteTimeout*/), w_timeout(0), c_timeout(0)
85 {
86     char x_host[16/*sizeof("255.255.255.255")*/];
87     if (timeout  &&  timeout != kDefaultTimeout) {
88         oo_timeout = *timeout;
89         o_timeout  = &oo_timeout;
90     } else
91         o_timeout  = 0/*kInfiniteTimeout*/;
92     if (SOCK_ntoa(host, x_host, sizeof(x_host)) != 0)
93         m_Socket = 0;
94     else
95         SOCK_CreateEx(x_host, port, o_timeout, &m_Socket, 0, 0, flags);
96 
97 }
98 
99 
CUNIXSocket(const string & path,const STimeout * timeout,TSOCK_Flags flags)100 CUNIXSocket::CUNIXSocket(const string&   path,
101                          const STimeout* timeout,
102                          TSOCK_Flags     flags)
103 {
104     if (timeout  &&  timeout != kDefaultTimeout) {
105         oo_timeout = *timeout;
106         o_timeout  = &oo_timeout;
107     } else
108         o_timeout  = 0/*kInfiniteTimeout*/;
109     SOCK_CreateUNIX(path.c_str(), o_timeout, &m_Socket, 0, 0, flags);
110 }
111 
112 
~CSocket()113 CSocket::~CSocket()
114 {
115     if (m_Socket  &&  m_IsOwned != eNoOwnership)
116         SOCK_Close(m_Socket);
117 }
118 
119 
Connect(const string & host,unsigned short port,const STimeout * timeout,TSOCK_Flags flags)120 EIO_Status CSocket::Connect(const string&   host,
121                             unsigned short  port,
122                             const STimeout* timeout,
123                             TSOCK_Flags     flags)
124 {
125     if ( m_Socket ) {
126         if (SOCK_Status(m_Socket, eIO_Open) != eIO_Closed)
127             return eIO_Unknown;
128         if (m_IsOwned != eNoOwnership)
129             SOCK_Close(m_Socket);
130     }
131     if (timeout != kDefaultTimeout) {
132         if ( timeout ) {
133             if (&oo_timeout != timeout)
134                 oo_timeout = *timeout;
135             o_timeout = &oo_timeout;
136         } else
137             o_timeout = 0/*kInfiniteTimeout*/;
138     }
139     EIO_Status status = SOCK_CreateEx(host.c_str(), port, o_timeout,
140                                       &m_Socket, 0, 0, flags);
141     if (status == eIO_Success) {
142         SOCK_SetTimeout(m_Socket, eIO_Read,  r_timeout);
143         SOCK_SetTimeout(m_Socket, eIO_Write, w_timeout);
144         SOCK_SetTimeout(m_Socket, eIO_Close, c_timeout);
145     } else
146         assert(!m_Socket);
147     return status;
148 }
149 
150 
Connect(const string & path,const STimeout * timeout,TSOCK_Flags flags)151 EIO_Status CUNIXSocket::Connect(const string&   path,
152                                 const STimeout* timeout,
153                                 TSOCK_Flags     flags)
154 {
155     if ( m_Socket ) {
156         if (SOCK_Status(m_Socket, eIO_Open) != eIO_Closed)
157             return eIO_Unknown;
158         if (m_IsOwned != eNoOwnership)
159             SOCK_Close(m_Socket);
160     }
161     if (timeout != kDefaultTimeout) {
162         if ( timeout ) {
163             if (&oo_timeout != timeout)
164                 oo_timeout = *timeout;
165             o_timeout = &oo_timeout;
166         } else
167             o_timeout = 0/*kInfiniteTimeout*/;
168     }
169     EIO_Status status = SOCK_CreateUNIX(path.c_str(), o_timeout,
170                                         &m_Socket, 0, 0, flags);
171     if (status == eIO_Success) {
172         SOCK_SetTimeout(m_Socket, eIO_Read,  r_timeout);
173         SOCK_SetTimeout(m_Socket, eIO_Write, w_timeout);
174         SOCK_SetTimeout(m_Socket, eIO_Close, c_timeout);
175     } else
176         assert(!m_Socket);
177     return status;
178 }
179 
180 
Reconnect(const STimeout * timeout)181 EIO_Status CSocket::Reconnect(const STimeout* timeout)
182 {
183     if (timeout != kDefaultTimeout) {
184         if ( timeout ) {
185             if (&oo_timeout != timeout)
186                 oo_timeout = *timeout;
187             o_timeout = &oo_timeout;
188         } else
189             o_timeout = 0/*kInfiniteTimeout*/;
190     }
191     return m_Socket ? SOCK_Reconnect(m_Socket, 0, 0, o_timeout) : eIO_Closed;
192 }
193 
194 
SetTimeout(EIO_Event event,const STimeout * timeout)195 EIO_Status CSocket::SetTimeout(EIO_Event event, const STimeout* timeout)
196 {
197     if (timeout == kDefaultTimeout)
198         return eIO_Success;
199 
200     switch (event) {
201     case eIO_Open:
202         if ( timeout ) {
203             if (&oo_timeout != timeout)
204                 oo_timeout = *timeout;
205             o_timeout = &oo_timeout;
206         } else
207             o_timeout = 0/*kInfiniteTimeout*/;
208         break;
209     case eIO_Read:
210         if ( timeout ) {
211             if (&rr_timeout != timeout)
212                 rr_timeout = *timeout;
213             r_timeout = &rr_timeout;
214         } else
215             r_timeout = 0/*kInfiniteTimeout*/;
216         break;
217     case eIO_Write:
218         if ( timeout ) {
219             if (&ww_timeout != timeout)
220                 ww_timeout = *timeout;
221             w_timeout = &ww_timeout;
222         } else
223             w_timeout = 0/*kInfiniteTimeout*/;
224         break;
225     case eIO_ReadWrite:
226         if ( timeout ) {
227             if (&rr_timeout != timeout)
228                 rr_timeout = *timeout;
229             r_timeout = &rr_timeout;
230             if (&ww_timeout != timeout)
231                 ww_timeout = *timeout;
232             w_timeout = &ww_timeout;
233         } else {
234             r_timeout = 0/*kInfiniteTimeout*/;
235             w_timeout = 0/*kInfiniteTimeout*/;
236         }
237         break;
238     case eIO_Close:
239         if ( timeout ) {
240             if (&cc_timeout != timeout)
241                 cc_timeout = *timeout;
242             c_timeout = &cc_timeout;
243         } else
244             c_timeout = 0/*kInfiniteTimeout*/;
245         break;
246     default:
247         return eIO_InvalidArg;
248     }
249     return m_Socket ? SOCK_SetTimeout(m_Socket, event, timeout) : eIO_Success;
250 }
251 
252 
GetTimeout(EIO_Event event) const253 const STimeout* CSocket::GetTimeout(EIO_Event event) const
254 {
255     switch (event) {
256     case eIO_Open:
257         return o_timeout;
258     case eIO_Read:
259         return r_timeout;
260     case eIO_Write:
261         return w_timeout;
262     case eIO_ReadWrite:
263         if ( !r_timeout )
264             return w_timeout;
265         if ( !w_timeout )
266             return r_timeout;
267         return ((unsigned long) r_timeout->sec * 1000000 + r_timeout->usec >
268                 (unsigned long) w_timeout->sec * 1000000 + w_timeout->usec)
269             ? w_timeout : r_timeout;
270     case eIO_Close:
271         return c_timeout;
272     default:
273         break;
274     }
275     return kDefaultTimeout;
276 }
277 
278 
Read(void * buf,size_t size,size_t * n_read,EIO_ReadMethod how)279 EIO_Status CSocket::Read(void*          buf,
280                          size_t         size,
281                          size_t*        n_read,
282                          EIO_ReadMethod how)
283 {
284     if ( m_Socket )
285         return SOCK_Read(m_Socket, buf, size, n_read, how);
286     if ( n_read )
287         *n_read = 0;
288     return eIO_Closed;
289 }
290 
291 
ReadLine(string & str)292 EIO_Status CSocket::ReadLine(string& str)
293 {
294     str.erase();
295     if ( !m_Socket )
296         return eIO_Closed;
297     EIO_Status status;
298     char buf[1024];
299     size_t size;
300     do {
301         status = SOCK_ReadLine(m_Socket, buf, sizeof(buf), &size);
302         if (!size)
303             break;
304         str.append(buf, size);
305     } while (status == eIO_Success  &&  size == sizeof(buf));
306     return status;
307 }
308 
309 
Write(const void * buf,size_t size,size_t * n_written,EIO_WriteMethod how)310 EIO_Status CSocket::Write(const void*     buf,
311                           size_t          size,
312                           size_t*         n_written,
313                           EIO_WriteMethod how)
314 {
315     if ( m_Socket )
316         return SOCK_Write(m_Socket, buf, size, n_written, how);
317     if ( n_written )
318         *n_written = 0;
319     return eIO_Closed;
320 }
321 
322 
GetPeerAddress(unsigned int * host,unsigned short * port,ENH_ByteOrder byte_order) const323 void CSocket::GetPeerAddress(unsigned int*   host,
324                              unsigned short* port,
325                              ENH_ByteOrder   byte_order) const
326 {
327     if ( !m_Socket ) {
328         if ( host )
329             *host = 0;
330         if ( port )
331             *port = 0;
332     } else
333         SOCK_GetPeerAddress(m_Socket, host, port, byte_order);
334 }
335 
336 
GetPeerAddress(ESOCK_AddressFormat format) const337 string CSocket::GetPeerAddress(ESOCK_AddressFormat format) const
338 {
339     char buf[PATH_MAX + 1];
340     if (m_Socket  &&
341         SOCK_GetPeerAddressStringEx(m_Socket, buf, sizeof(buf), format) != 0) {
342         return string(buf);
343     }
344     return "";
345 }
346 
347 
Reset(SOCK sock,EOwnership if_to_own,ECopyTimeout whence)348 void CSocket::Reset(SOCK sock, EOwnership if_to_own, ECopyTimeout whence)
349 {
350     if (m_Socket != sock) {
351         if (m_Socket  &&  m_IsOwned != eNoOwnership)
352             SOCK_Close(m_Socket);
353         m_Socket  = sock;
354     }
355     m_IsOwned = if_to_own;
356     if (whence == eCopyTimeoutsFromSOCK) {
357         if ( sock ) {
358             const STimeout* timeout;
359             timeout = SOCK_GetTimeout(sock, eIO_Read);
360             if ( timeout ) {
361                 rr_timeout = *timeout;
362                 r_timeout  = &rr_timeout;
363             } else
364                 r_timeout  = 0/*kInfiniteTimeout*/;
365             timeout = SOCK_GetTimeout(sock, eIO_Write);
366             if ( timeout ) {
367                 ww_timeout = *timeout;
368                 w_timeout  = &ww_timeout;
369             } else
370                 w_timeout  = 0/*kInfiniteTimeout*/;
371             timeout = SOCK_GetTimeout(sock, eIO_Close);
372             if ( timeout ) {
373                 cc_timeout = *timeout;
374                 c_timeout  = &cc_timeout;
375             } else
376                 c_timeout  = 0/*kInfiniteTimeout*/;
377         } else
378             r_timeout = w_timeout = c_timeout = 0/*kInfiniteTimeout*/;
379     } else if ( sock ) {
380         SOCK_SetTimeout(sock, eIO_Read,  r_timeout);
381         SOCK_SetTimeout(sock, eIO_Write, w_timeout);
382         SOCK_SetTimeout(sock, eIO_Close, c_timeout);
383     }
384 }
385 
386 
387 
388 /////////////////////////////////////////////////////////////////////////////
389 //  CDatagramSocket::
390 //
391 
392 
Connect(unsigned int host,unsigned short port)393 EIO_Status CDatagramSocket::Connect(unsigned int   host,
394                                     unsigned short port)
395 {
396     char addr[40];
397     if (host  &&  SOCK_ntoa(host, addr, sizeof(addr)) != 0)
398         return eIO_Unknown;
399     return m_Socket
400         ? DSOCK_Connect(m_Socket, host ? addr : 0, port)
401         : eIO_Closed;
402 }
403 
404 
Recv(void * buf,size_t buflen,size_t * msglen,string * sender_host,unsigned short * sender_port,size_t maxmsglen)405 EIO_Status CDatagramSocket::Recv(void*           buf,
406                                  size_t          buflen,
407                                  size_t*         msglen,
408                                  string*         sender_host,
409                                  unsigned short* sender_port,
410                                  size_t          maxmsglen)
411 {
412     if ( !m_Socket ) {
413         if ( msglen )
414             *msglen = 0;
415         if ( sender_host )
416             *sender_host = "";
417         if ( sender_port )
418             *sender_port = 0;
419         return eIO_Closed;
420     }
421 
422     unsigned int addr;
423     EIO_Status status = DSOCK_RecvMsg(m_Socket, buf, buflen, maxmsglen,
424                                       msglen, &addr, sender_port);
425     if ( sender_host )
426         *sender_host = CSocketAPI::ntoa(addr);
427 
428     return status;
429 }
430 
431 
432 
433 /////////////////////////////////////////////////////////////////////////////
434 //  CListeningSocket::
435 //
436 
437 
~CListeningSocket()438 CListeningSocket::~CListeningSocket()
439 {
440     Close();
441 }
442 
443 
Accept(CSocket * & sock,const STimeout * timeout,TSOCK_Flags flags) const444 EIO_Status CListeningSocket::Accept(CSocket*&       sock,
445                                     const STimeout* timeout,
446                                     TSOCK_Flags     flags) const
447 {
448     if ( !m_Socket ) {
449         sock = 0;
450         return eIO_Closed;
451     }
452 
453     SOCK       x_sock;
454     EIO_Status status;
455     status = LSOCK_AcceptEx(m_Socket, timeout, &x_sock, flags);
456     assert(!x_sock ^ !(status != eIO_Success));
457     if (status == eIO_Success) {
458         try {
459             sock = new CSocket;
460         } catch (...) {
461             sock = 0;
462             SOCK_Abort(x_sock);
463             SOCK_Close(x_sock);
464             throw;
465         }
466         sock->Reset(x_sock, eTakeOwnership, eCopyTimeoutsToSOCK);
467     } else
468         sock = 0;
469     return status;
470 }
471 
472 
Accept(CSocket & sock,const STimeout * timeout,TSOCK_Flags flags) const473 EIO_Status CListeningSocket::Accept(CSocket&        sock,
474                                     const STimeout* timeout,
475                                     TSOCK_Flags     flags) const
476 {
477     SOCK       x_sock;
478     EIO_Status status;
479     if ( !m_Socket ) {
480         x_sock = 0;
481         status = eIO_Closed;
482     } else
483         status = LSOCK_AcceptEx(m_Socket, timeout, &x_sock, flags);
484     assert(!x_sock ^ !(status != eIO_Success));
485     sock.Reset(x_sock, eTakeOwnership, eCopyTimeoutsToSOCK);
486     return status;
487 }
488 
489 
Close(void)490 EIO_Status CListeningSocket::Close(void)
491 {
492     if ( !m_Socket )
493         return eIO_Closed;
494 
495     EIO_Status status = m_IsOwned != eNoOwnership
496         ? LSOCK_Close(m_Socket) : eIO_Success;
497     m_Socket = 0;
498     return status;
499 }
500 
501 
502 
503 /////////////////////////////////////////////////////////////////////////////
504 //  CSocketAPI::
505 //
506 
507 
Poll(vector<SPoll> & polls,const STimeout * timeout,size_t * n_ready)508 EIO_Status CSocketAPI::Poll(vector<SPoll>&  polls,
509                             const STimeout* timeout,
510                             size_t*         n_ready)
511 {
512     static const STimeout kZero = {0, 0};
513     size_t          x_n     = polls.size();
514     SPOLLABLE_Poll* x_polls = 0;
515     size_t          x_ready = 0;
516 
517     if (x_n  &&  !(x_polls = new SPOLLABLE_Poll[x_n]))
518         return eIO_Unknown;
519 
520     for (size_t i = 0;  i < x_n;  ++i) {
521         CPollable* p     = polls[i].m_Pollable;
522         EIO_Event  event = polls[i].m_Event;
523         if (p  &&  event) {
524             CSocket* s = dynamic_cast<CSocket*>(p);
525             if (!s) {
526                 CListeningSocket* ls = dynamic_cast<CListeningSocket*>(p);
527                 if (!ls) {
528                     CTrigger* tr = dynamic_cast<CTrigger*>(p);
529                     x_polls[i].poll = POLLABLE_FromTRIGGER(tr
530                                                            ? tr->GetTRIGGER()
531                                                            : 0);
532                 } else
533                     x_polls[i].poll = POLLABLE_FromLSOCK(ls->GetLSOCK());
534                 polls[i].m_REvent = eIO_Open;
535             } else {
536                 EIO_Event revent;
537                 if (s->GetStatus(eIO_Open) != eIO_Closed) {
538                     x_polls[i].poll = POLLABLE_FromSOCK(s->GetSOCK());
539                     revent = eIO_Open;
540                 } else {
541                     x_polls[i].poll = 0;
542                     revent = eIO_Close;
543                     ++x_ready;
544                 }
545                 polls[i].m_REvent = revent;
546             }
547             x_polls[i].event = event;
548         } else {
549             x_polls[i].poll = 0;
550             polls[i].m_REvent = eIO_Open;
551         }
552     }
553 
554     size_t xx_ready;
555     EIO_Status status = POLLABLE_Poll(x_n, x_polls,
556                                       x_ready ? &kZero : timeout, &xx_ready);
557 
558     for (size_t i = 0;  i < x_n;  ++i) {
559         if (x_polls[i].revent)
560             polls[i].m_REvent = x_polls[i].revent;
561     }
562 
563     if (n_ready)
564         *n_ready = xx_ready + x_ready;
565 
566     delete[] x_polls;
567     return status;
568 }
569 
570 
ntoa(unsigned int host)571 string CSocketAPI::ntoa(unsigned int host)
572 {
573     char addr[40];
574     if (SOCK_ntoa(host, addr, sizeof(addr)) != 0)
575         *addr = 0;
576     return string(addr);
577 }
578 
579 
gethostname(ESwitch log)580 string       CSocketAPI::gethostname(ESwitch log)
581 {
582     char hostname[256];
583     if (SOCK_gethostnameEx(hostname, sizeof(hostname), log) != 0)
584         *hostname = 0;
585     return string(hostname);
586 }
587 
588 
gethostbyaddr(unsigned int host,ESwitch log)589 string       CSocketAPI::gethostbyaddr(unsigned int host, ESwitch log)
590 {
591     char hostname[256];
592     if (!SOCK_gethostbyaddrEx(host, hostname, sizeof(hostname), log))
593         *hostname = 0;
594     return string(hostname);
595 }
596 
597 
gethostbyname(const string & host,ESwitch log)598 unsigned int CSocketAPI::gethostbyname(const string& host, ESwitch log)
599 {
600     return SOCK_gethostbynameEx(host == kEmptyStr ? 0 : host.c_str(), log);
601 }
602 
603 
HostPortToString(unsigned int host,unsigned short port)604 string    CSocketAPI::HostPortToString(unsigned int    host,
605                                        unsigned short  port)
606 {
607     char   buf[80];
608     size_t len = SOCK_HostPortToString(host, port, buf, sizeof(buf));
609     return string(buf, len);
610 }
611 
612 
StringToHostPort(const string & str,unsigned int * host,unsigned short * port)613 SIZE_TYPE CSocketAPI::StringToHostPort(const string&   str,
614                                        unsigned int*   host,
615                                        unsigned short* port)
616 {
617     const char* s = str.c_str();
618     const char* e = SOCK_StringToHostPort(s, host, port);
619     return e ? (SIZE_TYPE)(e - s) : NPOS;
620 }
621 
622 
623 END_NCBI_SCOPE
624