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