Line data Source code
1 : // Copyright (c) 2012-2021 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/eventdispatcher
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program; if not, write to the Free Software
18 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 :
20 : /** \file
21 : * \brief Event dispatch class.
22 : *
23 : * Class used to handle events.
24 : */
25 :
26 : // make sure we use OpenSSL with multi-thread support
27 : // (TODO: move to .cpp once we have the impl!)
28 : #define OPENSSL_THREAD_DEFINES
29 :
30 : // self
31 : //
32 : #include "eventdispatcher/tcp_bio_client.h"
33 :
34 : #include "eventdispatcher/exception.h"
35 : #include "eventdispatcher/tcp_private.h"
36 :
37 :
38 : // snaplogger lib
39 : //
40 : #include <snaplogger/message.h>
41 :
42 :
43 : // snapdev lib
44 : //
45 : #include <snapdev/not_reached.h>
46 :
47 :
48 : // OpenSSL lib
49 : //
50 : #include <openssl/bio.h>
51 : #include <openssl/err.h>
52 : #include <openssl/ssl.h>
53 :
54 :
55 : // C lib
56 : //
57 : #include <netdb.h>
58 : #include <arpa/inet.h>
59 :
60 :
61 : // last include
62 : //
63 : #include <snapdev/poison.h>
64 :
65 :
66 :
67 :
68 : #ifndef OPENSSL_THREADS
69 : #error "OPENSSL_THREADS is not defined. Snap! requires support for multiple threads in OpenSSL."
70 : #endif
71 :
72 : namespace ed
73 : {
74 :
75 :
76 :
77 : /** \class tcp_bio_client
78 : * \brief Create a BIO client and connect to a server, eventually with TLS.
79 : *
80 : * This class is a client socket implementation used to connect to a server.
81 : * The server is expected to be running at the time the client is created
82 : * otherwise it fails connecting.
83 : *
84 : * This class is not appropriate to connect to a server that may come and go
85 : * over time.
86 : *
87 : * The BIO extension is from the OpenSSL library and it allows the client
88 : * to connect using SSL. At this time connections are either secure or
89 : * not secure. If a secure connection fails, you may attempt again without
90 : * TLS or other encryption mechanism.
91 : */
92 :
93 :
94 : #if 0
95 : // code to do traces
96 : namespace
97 : {
98 :
99 :
100 : char const * tls_rt_type(int type)
101 : {
102 : switch(type)
103 : {
104 : #ifdef SSL3_RT_HEADER
105 : case SSL3_RT_HEADER:
106 : return "TLS header";
107 : #endif
108 : case SSL3_RT_CHANGE_CIPHER_SPEC:
109 : return "TLS change cipher";
110 : case SSL3_RT_ALERT:
111 : return "TLS alert";
112 : case SSL3_RT_HANDSHAKE:
113 : return "TLS handshake";
114 : case SSL3_RT_APPLICATION_DATA:
115 : return "TLS app data";
116 : default:
117 : return "TLS Unknown";
118 : }
119 : }
120 :
121 :
122 : char const * ssl_msg_type(int ssl_ver, int msg)
123 : {
124 : #ifdef SSL2_VERSION_MAJOR
125 : if(ssl_ver == SSL2_VERSION_MAJOR)
126 : {
127 : switch(msg)
128 : {
129 : case SSL2_MT_ERROR:
130 : return "Error";
131 : case SSL2_MT_CLIENT_HELLO:
132 : return "Client hello";
133 : case SSL2_MT_CLIENT_MASTER_KEY:
134 : return "Client key";
135 : case SSL2_MT_CLIENT_FINISHED:
136 : return "Client finished";
137 : case SSL2_MT_SERVER_HELLO:
138 : return "Server hello";
139 : case SSL2_MT_SERVER_VERIFY:
140 : return "Server verify";
141 : case SSL2_MT_SERVER_FINISHED:
142 : return "Server finished";
143 : case SSL2_MT_REQUEST_CERTIFICATE:
144 : return "Request CERT";
145 : case SSL2_MT_CLIENT_CERTIFICATE:
146 : return "Client CERT";
147 : }
148 : }
149 : else
150 : #endif
151 : if(ssl_ver == SSL3_VERSION_MAJOR)
152 : {
153 : switch(msg)
154 : {
155 : case SSL3_MT_HELLO_REQUEST:
156 : return "Hello request";
157 : case SSL3_MT_CLIENT_HELLO:
158 : return "Client hello";
159 : case SSL3_MT_SERVER_HELLO:
160 : return "Server hello";
161 : #ifdef SSL3_MT_NEWSESSION_TICKET
162 : case SSL3_MT_NEWSESSION_TICKET:
163 : return "Newsession Ticket";
164 : #endif
165 : case SSL3_MT_CERTIFICATE:
166 : return "Certificate";
167 : case SSL3_MT_SERVER_KEY_EXCHANGE:
168 : return "Server key exchange";
169 : case SSL3_MT_CLIENT_KEY_EXCHANGE:
170 : return "Client key exchange";
171 : case SSL3_MT_CERTIFICATE_REQUEST:
172 : return "Request CERT";
173 : case SSL3_MT_SERVER_DONE:
174 : return "Server finished";
175 : case SSL3_MT_CERTIFICATE_VERIFY:
176 : return "CERT verify";
177 : case SSL3_MT_FINISHED:
178 : return "Finished";
179 : #ifdef SSL3_MT_CERTIFICATE_STATUS
180 : case SSL3_MT_CERTIFICATE_STATUS:
181 : return "Certificate Status";
182 : #endif
183 : }
184 : }
185 : return "Unknown";
186 : }
187 :
188 :
189 : void ssl_trace(
190 : int direction,
191 : int ssl_ver,
192 : int content_type,
193 : void const * buf, size_t len,
194 : SSL * ssl,
195 : void * userp)
196 : {
197 : snap::NOT_USED(ssl, userp);
198 :
199 : std::stringstream out;
200 : char const * msg_name;
201 : int msg_type;
202 :
203 : // VERSION
204 : //
205 : out << SSL_get_version(ssl);
206 :
207 : // DIRECTION
208 : //
209 : out << (direction == 0 ? " (IN), " : " (OUT), ");
210 :
211 : // keep only major version
212 : //
213 : ssl_ver >>= 8;
214 :
215 : // TLS RT NAME
216 : //
217 : if(ssl_ver == SSL3_VERSION_MAJOR
218 : && content_type != 0)
219 : {
220 : out << tls_rt_type(content_type);
221 : }
222 : else
223 : {
224 : out << "(no tls_tr_type)";
225 : }
226 :
227 : if(len >= 1)
228 : {
229 : msg_type = * reinterpret_cast<unsigned char const *>(buf);
230 : msg_name = ssl_msg_type(ssl_ver, msg_type);
231 :
232 : out << ", ";
233 : out << msg_name;
234 : out << " (";
235 : out << std::to_string(msg_type);
236 : out << "):";
237 : }
238 :
239 : out << std::hex;
240 : for(size_t line(0); line < len; line += 16)
241 : {
242 : out << std::endl
243 : << (direction == 0 ? "<" : ">")
244 : << " "
245 : << std::setfill('0') << std::setw(4) << line
246 : << "- ";
247 : size_t idx;
248 : for(idx = 0; line + idx < len && idx < 16; ++idx)
249 : {
250 : if(idx == 8)
251 : {
252 : out << " ";
253 : }
254 : else
255 : {
256 : out << " ";
257 : }
258 : int const c(reinterpret_cast<unsigned char const *>(buf)[line + idx]);
259 : out << std::setfill('0') << std::setw(2) << static_cast<int>(c);
260 : }
261 : for(; idx < 16; ++idx)
262 : {
263 : if(idx == 8)
264 : {
265 : out << " ";
266 : }
267 : out << " ";
268 : }
269 : out << " ";
270 : for(idx = 0; line + idx < len && idx < 16; ++idx)
271 : {
272 : if(idx == 8)
273 : {
274 : out << " ";
275 : }
276 : char c(reinterpret_cast<char const *>(buf)[line + idx]);
277 : if(c < ' ' || c > '~')
278 : {
279 : c = '.';
280 : }
281 : out << c;
282 : }
283 : }
284 :
285 : std::cerr << out.str() << std::endl;
286 : }
287 :
288 :
289 : } // no name namespace
290 : #endif
291 :
292 :
293 :
294 :
295 :
296 :
297 :
298 :
299 :
300 :
301 :
302 : /** \brief Construct a tcp_bio_client object.
303 : *
304 : * The tcp_bio_client constructor initializes a BIO connector and connects
305 : * to the specified server. The server is defined with the \p addr and
306 : * \p port specified as parameters. The connection tries to use TLS if
307 : * the \p mode parameter is set to MODE_SECURE. Note that you may force
308 : * a secure connection using MODE_SECURE_REQUIRED. With MODE_SECURE,
309 : * the connection to the server can be obtained even if a secure
310 : * connection could not be made to work.
311 : *
312 : * \todo
313 : * Create another client with BIO_new_socket() so one can create an SSL
314 : * connection with a socket retrieved from an accept() call.
315 : *
316 : * \exception tcp_client_server_parameter_error
317 : * This exception is raised if the \p port parameter is out of range or the
318 : * IP address is an empty string or otherwise an invalid address.
319 : *
320 : * \exception tcp_client_server_initialization_error
321 : * This exception is raised if the client cannot create the socket or it
322 : * cannot connect to the server.
323 : *
324 : * \param[in] addr The address of the server to connect to. It must be valid.
325 : * \param[in] port The port the server is listening on.
326 : * \param[in] mode Whether to use SSL when connecting.
327 : * \param[in] opt Additional options.
328 : */
329 0 : tcp_bio_client::tcp_bio_client(
330 : std::string const & addr
331 : , int port
332 : , mode_t mode
333 0 : , tcp_bio_options const & opt)
334 0 : : f_impl(std::make_shared<detail::tcp_bio_client_impl>())
335 : {
336 0 : if(port < 0 || port >= 65536)
337 : {
338 0 : throw event_dispatcher_invalid_parameter("invalid port for a client socket");
339 : }
340 0 : if(addr.empty())
341 : {
342 0 : throw event_dispatcher_invalid_parameter("an empty address is not valid for a client socket");
343 : }
344 :
345 0 : detail::bio_initialize();
346 :
347 0 : switch(mode)
348 : {
349 0 : case mode_t::MODE_SECURE:
350 : case mode_t::MODE_ALWAYS_SECURE:
351 : {
352 : // Use TLS v1 only as all versions of SSL are flawed...
353 : // (see below the SSL_CTX_set_options() for additional details
354 : // about that since here it does indeed say SSLv23...)
355 : //
356 0 : std::shared_ptr<SSL_CTX> ssl_ctx; // use a reset(), see SNAP-507
357 0 : ssl_ctx.reset(SSL_CTX_new(SSLv23_client_method()), detail::ssl_ctx_deleter);
358 0 : if(ssl_ctx == nullptr)
359 : {
360 0 : detail::bio_log_errors();
361 0 : throw event_dispatcher_initialization_error("failed creating an SSL_CTX object");
362 : }
363 :
364 : // allow up to `depth` certificates in the chain otherwise fail
365 : // (this is not a very strong security feature though); the depth
366 : // can be changed before calling this function using a
367 : // tcp_bio_options object
368 : //
369 0 : SSL_CTX_set_verify_depth(ssl_ctx.get(), opt.get_verification_depth());
370 :
371 : // make sure SSL v2/3 is not used, also compression in SSL is
372 : // known to have security issues
373 : //
374 0 : SSL_CTX_set_options(ssl_ctx.get(), opt.get_ssl_options());
375 :
376 : // limit the number of ciphers the connection can use
377 0 : if(mode == mode_t::MODE_SECURE)
378 : {
379 : // this is used by local connections and we get a very strong
380 : // algorithm anyway, but at this point I do not know why it
381 : // does not work with the limited list below...
382 : //
383 : // TODO: test with adding DH support in the server then
384 : // maybe (probably) that the "HIGH" will work for
385 : // this entry too...
386 : //
387 0 : SSL_CTX_set_cipher_list(ssl_ctx.get(), "ALL");
388 : }
389 : else
390 : {
391 0 : SSL_CTX_set_cipher_list(ssl_ctx.get(), "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4");
392 : }
393 :
394 : // load root certificates (correct path for Ubuntu?)
395 : // TODO: allow client to set the path to certificates
396 0 : if(SSL_CTX_load_verify_locations(ssl_ctx.get(), nullptr, "/etc/ssl/certs") != 1)
397 : {
398 0 : detail::bio_log_errors();
399 0 : throw event_dispatcher_initialization_error("failed loading verification certificates in an SSL_CTX object");
400 : }
401 : //SSL_CTX_set_msg_callback(ssl_ctx.get(), ssl_trace);
402 : //SSL_CTX_set_msg_callback_arg(ssl_ctx.get(), this);
403 :
404 : // create a BIO connected to SSL ciphers
405 : //
406 0 : std::shared_ptr<BIO> bio;
407 0 : bio.reset(BIO_new_ssl_connect(ssl_ctx.get()), detail::bio_deleter);
408 0 : if(!bio)
409 : {
410 0 : detail::bio_log_errors();
411 0 : throw event_dispatcher_initialization_error("failed initializing a BIO object");
412 : }
413 :
414 : // verify that the connection worked
415 : //
416 0 : SSL * ssl(nullptr);
417 : #pragma GCC diagnostic push
418 : #pragma GCC diagnostic ignored "-Wold-style-cast"
419 0 : BIO_get_ssl(bio.get(), &ssl);
420 : #pragma GCC diagnostic pop
421 0 : if(ssl == nullptr)
422 : {
423 : // TBD: does this mean we would have a plain connection?
424 0 : detail::bio_log_errors();
425 0 : throw event_dispatcher_initialization_error("failed retrieving the SSL contact from BIO object");
426 : }
427 :
428 : // allow automatic retries in case the connection somehow needs
429 : // an SSL renegotiation (maybe we should turn that off for cases
430 : // where we connect to a secure payment gateway?)
431 : //
432 0 : SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
433 :
434 : // setup the Server Name Indication (SNI)
435 : //
436 0 : bool using_sni(false);
437 0 : if(opt.get_sni())
438 : {
439 0 : std::string host(opt.get_host());
440 0 : in6_addr ignore;
441 0 : if(host.empty()
442 0 : && inet_pton(AF_INET, addr.c_str(), &ignore) == 0 // must fail
443 0 : && inet_pton(AF_INET6, addr.c_str(), &ignore) == 0) // must fail
444 : {
445 : // addr is not an IP address written as is,
446 : // it must be a hostname
447 : //
448 0 : host = addr;
449 : }
450 0 : if(!host.empty())
451 : {
452 : #pragma GCC diagnostic push
453 : #pragma GCC diagnostic ignored "-Wold-style-cast"
454 : // not only old style cast (but it's C, so expected)
455 : // but they want a non-constant pointer!?
456 : //
457 0 : SSL_set_tlsext_host_name(ssl, const_cast<char *>(host.c_str()));
458 : #pragma GCC diagnostic pop
459 0 : using_sni = true;
460 : }
461 : }
462 :
463 : // TODO: other SSL initialization?
464 :
465 : #pragma GCC diagnostic push
466 : #pragma GCC diagnostic ignored "-Wold-style-cast"
467 0 : BIO_set_conn_hostname(bio.get(), const_cast<char *>(addr.c_str()));
468 0 : BIO_set_conn_port(bio.get(), const_cast<char *>(std::to_string(port).c_str()));
469 : #pragma GCC diagnostic pop
470 :
471 : // connect to the server (open the socket)
472 : //
473 0 : if(BIO_do_connect(bio.get()) <= 0)
474 : {
475 0 : if(!using_sni)
476 : {
477 0 : SNAP_LOG_WARNING
478 : << "the SNI feature is turned off,"
479 : " often failure to connect with SSL is because the"
480 : " SSL Hello message is missing the SNI (Server Name In)."
481 0 : " See the tcp_bio_options::set_sni()."
482 : << SNAP_LOG_SEND;
483 : }
484 0 : detail::bio_log_errors();
485 0 : throw event_dispatcher_initialization_error("SSL BIO_do_connect() failed connecting BIO object to server");
486 : }
487 :
488 : // encryption handshake
489 : //
490 0 : if(BIO_do_handshake(bio.get()) != 1)
491 : {
492 0 : if(!using_sni)
493 : {
494 0 : SNAP_LOG_WARNING
495 : << "the SNI feature is turned off,"
496 : " often failure to connect with SSL is because the"
497 : " SSL Hello message is missing the SNI (Server Name In)."
498 0 : " See the tcp_bio_options::set_sni()."
499 : << SNAP_LOG_SEND;
500 : }
501 0 : detail::bio_log_errors();
502 : throw event_dispatcher_initialization_error("failed establishing a secure BIO connection with server, handshake failed."
503 : " Often such failures to process SSL is because the SSL Hello message is missing the SNI (Server Name In)."
504 0 : " See the tcp_bio_options::set_sni().");
505 : }
506 :
507 : // verify that the peer certificate was signed by a
508 : // recognized root authority
509 : //
510 0 : if(SSL_get_peer_certificate(ssl) == nullptr)
511 : {
512 0 : detail::bio_log_errors();
513 0 : throw event_dispatcher_initialization_error("peer failed presenting a certificate for security verification");
514 : }
515 :
516 : // XXX: check that the call below is similar to the example
517 : // usage of SSL_CTX_set_verify() which checks the name
518 : // of the certificate, etc.
519 : //
520 0 : if(SSL_get_verify_result(ssl) != X509_V_OK)
521 : {
522 0 : if(mode != mode_t::MODE_SECURE)
523 : {
524 0 : detail::bio_log_errors();
525 0 : throw event_dispatcher_initialization_error("peer certificate could not be verified");
526 : }
527 0 : SNAP_LOG_WARNING
528 0 : << "connecting with SSL but certificate verification failed."
529 : << SNAP_LOG_SEND;
530 : }
531 :
532 : // it worked, save the results
533 : //
534 0 : f_impl->f_ssl_ctx.swap(ssl_ctx);
535 0 : f_impl->f_bio.swap(bio);
536 :
537 : // secure connection ready
538 : //
539 0 : char const * cipher_name(SSL_get_cipher(ssl));
540 0 : int cipher_bits(0);
541 0 : SSL_get_cipher_bits(ssl, &cipher_bits);
542 0 : SNAP_LOG_DEBUG
543 0 : << "connected with SSL cipher \""
544 : << cipher_name
545 0 : << "\" representing "
546 : << cipher_bits
547 : << " bits of encryption."
548 0 : << SNAP_LOG_SEND;
549 : }
550 0 : break;
551 :
552 0 : case mode_t::MODE_PLAIN:
553 : {
554 : // create a plain BIO connection
555 : //
556 0 : std::shared_ptr<BIO> bio; // use reset(), see SNAP-507
557 0 : bio.reset(BIO_new(BIO_s_connect()), detail::bio_deleter);
558 0 : if(!bio)
559 : {
560 0 : detail::bio_log_errors();
561 0 : throw event_dispatcher_initialization_error("failed initializing a BIO object");
562 : }
563 :
564 : #pragma GCC diagnostic push
565 : #pragma GCC diagnostic ignored "-Wold-style-cast"
566 0 : BIO_set_conn_hostname(bio.get(), const_cast<char *>(addr.c_str()));
567 0 : BIO_set_conn_port(bio.get(), const_cast<char *>(std::to_string(port).c_str()));
568 : #pragma GCC diagnostic pop
569 :
570 : // connect to the server (open the socket)
571 : //
572 0 : if(BIO_do_connect(bio.get()) <= 0)
573 : {
574 0 : detail::bio_log_errors();
575 0 : throw event_dispatcher_initialization_error("failed connecting BIO object to server");
576 : }
577 :
578 : // it worked, save the results
579 : //
580 0 : f_impl->f_bio.swap(bio);
581 :
582 : // plain connection ready
583 : }
584 0 : break;
585 :
586 : }
587 :
588 0 : if(opt.get_keepalive())
589 : {
590 : // retrieve the socket (we are still in the constructor so avoid
591 : // calling other functions...)
592 : //
593 0 : int socket(-1);
594 : #pragma GCC diagnostic push
595 : #pragma GCC diagnostic ignored "-Wold-style-cast"
596 0 : BIO_get_fd(f_impl->f_bio.get(), &socket);
597 : #pragma GCC diagnostic pop
598 0 : if(socket >= 0)
599 : {
600 : // if this call fails, we ignore the error, but still log the event
601 : //
602 0 : int optval(1);
603 0 : socklen_t const optlen(sizeof(optval));
604 0 : if(setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) != 0)
605 : {
606 0 : SNAP_LOG_WARNING
607 0 : << "an error occurred trying to mark client socket with SO_KEEPALIVE."
608 : << SNAP_LOG_SEND;
609 : }
610 : }
611 : }
612 0 : }
613 :
614 :
615 : /** \brief Create a BIO client object from an actual BIO pointer.
616 : *
617 : * This function is called by the server whenever it accepts a new BIO
618 : * connection. The server then can return the tcp_bio_client object instead
619 : * of a BIO object.
620 : *
621 : * The BIO is saved directly in the f_impl class (the server is given access.)
622 : */
623 0 : tcp_bio_client::tcp_bio_client()
624 0 : : f_impl(std::make_shared<detail::tcp_bio_client_impl>())
625 : {
626 0 : }
627 :
628 :
629 : /** \brief Clean up the BIO client object.
630 : *
631 : * This function cleans up the BIO client object by freeing the SSL_CTX
632 : * and the BIO objects.
633 : */
634 0 : tcp_bio_client::~tcp_bio_client()
635 : {
636 : // f_bio and f_ssl_ctx are allocated using shared pointers with
637 : // a deleter so we have nothing to do here.
638 0 : }
639 :
640 :
641 : /** \brief Close the connection.
642 : *
643 : * This function closes the connection by losing the f_bio object.
644 : *
645 : * As we are at it, we also lose the SSL context since we are not going
646 : * to use it anymore either.
647 : */
648 0 : void tcp_bio_client::close()
649 : {
650 0 : f_impl->f_bio.reset();
651 0 : f_impl->f_ssl_ctx.reset();
652 0 : }
653 :
654 :
655 : /** \brief Get the socket descriptor.
656 : *
657 : * This function returns the TCP client socket descriptor. This can be
658 : * used to change the descriptor behavior (i.e. make it non-blocking for
659 : * example.)
660 : *
661 : * \note
662 : * If the socket was closed, then the function returns -1.
663 : *
664 : * \warning
665 : * This socket is generally managed by the BIO library and thus it may
666 : * create unwanted side effects to change the socket under the feet of
667 : * the BIO library...
668 : *
669 : * \return The socket descriptor.
670 : */
671 0 : int tcp_bio_client::get_socket() const
672 : {
673 0 : if(f_impl->f_bio)
674 : {
675 0 : int c;
676 : #pragma GCC diagnostic push
677 : #pragma GCC diagnostic ignored "-Wold-style-cast"
678 0 : BIO_get_fd(f_impl->f_bio.get(), &c);
679 : #pragma GCC diagnostic pop
680 0 : return c;
681 : }
682 :
683 0 : return -1;
684 : }
685 :
686 :
687 : /** \brief Get the TCP client port.
688 : *
689 : * This function returns the port used when creating the TCP client.
690 : * Note that this is the port the server is listening to and not the port
691 : * the TCP client is currently connected to.
692 : *
693 : * \note
694 : * If the connection was closed, return -1.
695 : *
696 : * \return The TCP client port.
697 : */
698 0 : int tcp_bio_client::get_port() const
699 : {
700 0 : if(f_impl->f_bio)
701 : {
702 0 : return std::atoi(BIO_get_conn_port(f_impl->f_bio.get()));
703 : }
704 :
705 0 : return -1;
706 : }
707 :
708 :
709 : /** \brief Get the TCP server address.
710 : *
711 : * This function returns the address used when creating the TCP address as is.
712 : * Note that this is the address of the server where the client is connected
713 : * and not the address where the client is running (although it may be the
714 : * same.)
715 : *
716 : * Use the get_client_addr() function to retrieve the client's TCP address.
717 : *
718 : * \note
719 : * If the connection was closed, this function returns "".
720 : *
721 : * \return The TCP client address.
722 : */
723 0 : std::string tcp_bio_client::get_addr() const
724 : {
725 0 : if(f_impl->f_bio)
726 : {
727 0 : return BIO_get_conn_hostname(f_impl->f_bio.get());
728 : }
729 :
730 0 : return "";
731 : }
732 :
733 :
734 : /** \brief Get the TCP client port.
735 : *
736 : * This function retrieve the port of the client (used on your computer).
737 : * This is retrieved from the socket using the getsockname() function.
738 : *
739 : * \return The port or -1 if it cannot be determined.
740 : */
741 0 : int tcp_bio_client::get_client_port() const
742 : {
743 : // get_socket() returns -1 if f_bio is nullptr
744 : //
745 0 : int const s(get_socket());
746 0 : if(s < 0)
747 : {
748 0 : return -1;
749 : }
750 :
751 0 : sockaddr addr;
752 0 : socklen_t len(sizeof(addr));
753 0 : int const r(getsockname(s, &addr, &len));
754 0 : if(r != 0)
755 : {
756 0 : return -1;
757 : }
758 : // Note: I know the port is at the exact same location in both
759 : // structures in Linux but it could change on other Unices
760 0 : switch(addr.sa_family)
761 : {
762 0 : case AF_INET:
763 : // IPv4
764 0 : return reinterpret_cast<sockaddr_in *>(&addr)->sin_port;
765 :
766 0 : case AF_INET6:
767 : // IPv6
768 0 : return reinterpret_cast<sockaddr_in6 *>(&addr)->sin6_port;
769 :
770 0 : default:
771 0 : return -1;
772 :
773 : }
774 : snap::NOT_REACHED();
775 : }
776 :
777 :
778 : /** \brief Get the TCP client address.
779 : *
780 : * This function retrieve the IP address of the client (your computer).
781 : * This is retrieved from the socket using the getsockname() function.
782 : *
783 : * \note
784 : * The function returns an empty string if the connection was lost
785 : * or purposefully closed.
786 : *
787 : * \return The IP address as a string.
788 : */
789 0 : std::string tcp_bio_client::get_client_addr() const
790 : {
791 : // the socket may be invalid, i.e. f_bio may have been deallocated.
792 : //
793 0 : int const s(get_socket());
794 0 : if(s < 0)
795 : {
796 0 : return std::string();
797 : }
798 :
799 0 : sockaddr addr;
800 0 : socklen_t len(sizeof(addr));
801 0 : int const r(getsockname(s, &addr, &len));
802 0 : if(r != 0)
803 : {
804 0 : throw event_dispatcher_runtime_error("failed reading address");
805 : }
806 0 : char buf[BUFSIZ];
807 0 : switch(addr.sa_family)
808 : {
809 0 : case AF_INET:
810 : // TODO: verify that 'r' >= sizeof(something)
811 0 : inet_ntop(AF_INET, &reinterpret_cast<struct sockaddr_in *>(&addr)->sin_addr, buf, sizeof(buf));
812 0 : break;
813 :
814 0 : case AF_INET6:
815 : // TODO: verify that 'r' >= sizeof(something)
816 0 : inet_ntop(AF_INET6, &reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_addr, buf, sizeof(buf));
817 0 : break;
818 :
819 0 : default:
820 0 : throw event_dispatcher_runtime_error("unknown address family");
821 :
822 : }
823 0 : return buf;
824 : }
825 :
826 :
827 : /** \brief Read data from the socket.
828 : *
829 : * A TCP socket is a stream type of socket and one can read data from it
830 : * as if it were a regular file. This function reads \p size bytes and
831 : * returns. The function returns early if the server closes the connection.
832 : *
833 : * If your socket is blocking, \p size should be exactly what you are
834 : * expecting or this function will block forever or until the server
835 : * closes the connection.
836 : *
837 : * The function returns -1 if an error occurs. The error is available in
838 : * errno as expected in the POSIX interface.
839 : *
840 : * \note
841 : * If the connection was closed, this function returns -1.
842 : *
843 : * \warning
844 : * When the function returns zero, it is likely that the server closed
845 : * the connection. It may also be that the buffer was empty and that
846 : * the BIO decided to return early. Since we use a blocking mechanism
847 : * by default, that should not happen.
848 : *
849 : * \todo
850 : * At this point, I do not know for sure whether errno is properly set
851 : * or not. It is not unlikely that the BIO library does not keep a clean
852 : * errno error since they have their own error management.
853 : *
854 : * \param[in,out] buf The buffer where the data is read.
855 : * \param[in] size The size of the buffer.
856 : *
857 : * \return The number of bytes read from the socket, or -1 on errors.
858 : *
859 : * \sa read_line()
860 : * \sa write()
861 : */
862 0 : int tcp_bio_client::read(char * buf, size_t size)
863 : {
864 0 : if(!f_impl->f_bio)
865 : {
866 0 : errno = EBADF;
867 0 : return -1;
868 : }
869 :
870 0 : int const r(static_cast<int>(BIO_read(f_impl->f_bio.get(), buf, size)));
871 0 : if(r <= -2)
872 : {
873 : // the BIO is not implemented
874 : //
875 0 : detail::bio_log_errors();
876 0 : errno = EIO;
877 0 : return -1;
878 : }
879 0 : if(r == -1 || r == 0)
880 : {
881 0 : if(BIO_should_retry(f_impl->f_bio.get()))
882 : {
883 0 : errno = EAGAIN;
884 0 : return 0;
885 : }
886 : // did we reach the "end of the file"? i.e. did the server
887 : // close our connection? (this better replicates what a
888 : // normal socket does when reading from a closed socket)
889 : //
890 0 : if(BIO_eof(f_impl->f_bio.get()))
891 : {
892 0 : return 0;
893 : }
894 0 : if(r != 0)
895 : {
896 : // the BIO generated an error
897 0 : detail::bio_log_errors();
898 0 : errno = EIO;
899 0 : return -1;
900 : }
901 : }
902 0 : return r;
903 : }
904 :
905 :
906 : /** \brief Read one line.
907 : *
908 : * This function reads one line from the current location up to the next
909 : * '\\n' character. We do not have any special handling of the '\\r'
910 : * character.
911 : *
912 : * The function may return 0 (an empty string) when the server closes
913 : * the connection.
914 : *
915 : * \note
916 : * If the connection was closed then this function returns -1.
917 : *
918 : * \warning
919 : * A return value of zero can mean "empty line" and not end of file. It
920 : * is up to you to know whether your protocol allows for empty lines or
921 : * not. If so, you may not be able to make use of this function.
922 : *
923 : * \param[out] line The resulting line read from the server. The function
924 : * first clears the contents.
925 : *
926 : * \return The number of bytes read from the socket, or -1 on errors.
927 : * If the function returns 0 or more, then the \p line parameter
928 : * represents the characters read on the network without the '\n'.
929 : *
930 : * \sa read()
931 : */
932 0 : int tcp_bio_client::read_line(std::string & line)
933 : {
934 0 : line.clear();
935 0 : int len(0);
936 : for(;;)
937 : {
938 0 : char c;
939 0 : int r(read(&c, sizeof(c)));
940 0 : if(r <= 0)
941 : {
942 0 : return len == 0 && r < 0 ? -1 : len;
943 : }
944 0 : if(c == '\n')
945 : {
946 0 : return len;
947 : }
948 0 : ++len;
949 0 : line += c;
950 0 : }
951 : }
952 :
953 :
954 : /** \brief Write data to the socket.
955 : *
956 : * A BIO socket is a stream type of socket and one can write data to it
957 : * as if it were a regular file. This function writes \p size bytes to
958 : * the socket and then returns. This function returns early if the server
959 : * closes the connection.
960 : *
961 : * If your socket is not blocking, less than \p size bytes may be written
962 : * to the socket. In that case you are responsible for calling the function
963 : * again to write the remainder of the buffer until the function returns
964 : * a number of bytes written equal to \p size.
965 : *
966 : * The function returns -1 if an error occurs. The error is available in
967 : * errno as expected in the POSIX interface.
968 : *
969 : * \note
970 : * If the connection was closed, return -1.
971 : *
972 : * \todo
973 : * At this point, I do not know for sure whether errno is properly set
974 : * or not. It is not unlikely that the BIO library does not keep a clean
975 : * errno error since they have their own error management.
976 : *
977 : * \param[in] buf The buffer with the data to send over the socket.
978 : * \param[in] size The number of bytes in buffer to send over the socket.
979 : *
980 : * \return The number of bytes that were actually accepted by the socket
981 : * or -1 if an error occurs.
982 : *
983 : * \sa read()
984 : */
985 0 : int tcp_bio_client::write(char const * buf, size_t size)
986 : {
987 : #ifdef _DEBUG
988 : // This write is useful when developing APIs against 3rd party
989 : // servers, otherwise, it's just too much debug
990 : //SNAP_LOG_TRACE
991 : // << "tcp_bio_client::write(): buf="
992 : // << buf
993 : // << ", size="
994 : // << size
995 : // << SNAP_LOG_SEND;
996 : #endif
997 0 : if(!f_impl->f_bio)
998 : {
999 0 : errno = EBADF;
1000 0 : return -1;
1001 : }
1002 :
1003 0 : int const r(static_cast<int>(BIO_write(f_impl->f_bio.get(), buf, size)));
1004 0 : if(r <= -2)
1005 : {
1006 : // the BIO is not implemented
1007 0 : detail::bio_log_errors();
1008 0 : errno = EIO;
1009 0 : return -1;
1010 : }
1011 0 : if(r == -1 || r == 0)
1012 : {
1013 0 : if(BIO_should_retry(f_impl->f_bio.get()))
1014 : {
1015 0 : errno = EAGAIN;
1016 0 : return 0;
1017 : }
1018 : // the BIO generated an error (TBD should we check BIO_eof() too?)
1019 0 : detail::bio_log_errors();
1020 0 : errno = EIO;
1021 0 : return -1;
1022 : }
1023 0 : BIO_flush(f_impl->f_bio.get());
1024 0 : return r;
1025 : }
1026 :
1027 :
1028 :
1029 6 : } // namespace ed
1030 : // vim: ts=4 sw=4 et
|