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 4 certificates in the chain otherwise fail
365 : // (this is not a very strong security feature though)
366 : //
367 0 : SSL_CTX_set_verify_depth(ssl_ctx.get(), opt.get_verification_depth());
368 :
369 : // make sure SSL v2/3 is not used, also compression in SSL is
370 : // known to have security issues
371 : //
372 0 : SSL_CTX_set_options(ssl_ctx.get(), opt.get_ssl_options());
373 :
374 : // limit the number of ciphers the connection can use
375 0 : if(mode == mode_t::MODE_SECURE)
376 : {
377 : // this is used by local connections and we get a very strong
378 : // algorithm anyway, but at this point I do not know why it
379 : // does not work with the limited list below...
380 : //
381 : // TODO: test with adding DH support in the server then
382 : // maybe (probably) that the "HIGH" will work for
383 : // this entry too...
384 : //
385 0 : SSL_CTX_set_cipher_list(ssl_ctx.get(), "ALL");
386 : }
387 : else
388 : {
389 0 : SSL_CTX_set_cipher_list(ssl_ctx.get(), "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4");
390 : }
391 :
392 : // load root certificates (correct path for Ubuntu?)
393 : // TODO: allow client to set the path to certificates
394 0 : if(SSL_CTX_load_verify_locations(ssl_ctx.get(), nullptr, "/etc/ssl/certs") != 1)
395 : {
396 0 : detail::bio_log_errors();
397 0 : throw event_dispatcher_initialization_error("failed loading verification certificates in an SSL_CTX object");
398 : }
399 : //SSL_CTX_set_msg_callback(ssl_ctx.get(), ssl_trace);
400 : //SSL_CTX_set_msg_callback_arg(ssl_ctx.get(), this);
401 :
402 : // create a BIO connected to SSL ciphers
403 : //
404 0 : std::shared_ptr<BIO> bio;
405 0 : bio.reset(BIO_new_ssl_connect(ssl_ctx.get()), detail::bio_deleter);
406 0 : if(!bio)
407 : {
408 0 : detail::bio_log_errors();
409 0 : throw event_dispatcher_initialization_error("failed initializing a BIO object");
410 : }
411 :
412 : // verify that the connection worked
413 : //
414 0 : SSL * ssl(nullptr);
415 : #pragma GCC diagnostic push
416 : #pragma GCC diagnostic ignored "-Wold-style-cast"
417 0 : BIO_get_ssl(bio.get(), &ssl);
418 : #pragma GCC diagnostic pop
419 0 : if(ssl == nullptr)
420 : {
421 : // TBD: does this mean we would have a plain connection?
422 0 : detail::bio_log_errors();
423 0 : throw event_dispatcher_initialization_error("failed retrieving the SSL contact from BIO object");
424 : }
425 :
426 : // allow automatic retries in case the connection somehow needs
427 : // an SSL renegotiation (maybe we should turn that off for cases
428 : // where we connect to a secure payment gateway?)
429 : //
430 0 : SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
431 :
432 : // setup the Server Name Indication (SNI)
433 : //
434 0 : bool using_sni(false);
435 0 : if(opt.get_sni())
436 : {
437 0 : std::string host(opt.get_host());
438 : in6_addr ignore;
439 0 : if(host.empty()
440 0 : && inet_pton(AF_INET, addr.c_str(), &ignore) == 0 // must fail
441 0 : && inet_pton(AF_INET6, addr.c_str(), &ignore) == 0) // must fail
442 : {
443 : // addr is not an IP address written as is,
444 : // it must be a hostname
445 : //
446 0 : host = addr;
447 : }
448 0 : if(!host.empty())
449 : {
450 : #pragma GCC diagnostic push
451 : #pragma GCC diagnostic ignored "-Wold-style-cast"
452 : // not only old style cast (but it's C, so expected)
453 : // but they want a non-constant pointer!?
454 : //
455 0 : SSL_set_tlsext_host_name(ssl, const_cast<char *>(host.c_str()));
456 : #pragma GCC diagnostic pop
457 0 : using_sni = true;
458 : }
459 : }
460 :
461 : // TODO: other SSL initialization?
462 :
463 : #pragma GCC diagnostic push
464 : #pragma GCC diagnostic ignored "-Wold-style-cast"
465 0 : BIO_set_conn_hostname(bio.get(), const_cast<char *>(addr.c_str()));
466 0 : BIO_set_conn_port(bio.get(), const_cast<char *>(std::to_string(port).c_str()));
467 : #pragma GCC diagnostic pop
468 :
469 : // connect to the server (open the socket)
470 : //
471 0 : if(BIO_do_connect(bio.get()) <= 0)
472 : {
473 0 : if(!using_sni)
474 : {
475 0 : SNAP_LOG_WARNING
476 : << "the SNI feature is turned off,"
477 : " often failure to connect with SSL is because the"
478 : " SSL Hello message is missing the SNI (Server Name In)."
479 0 : " See the tcp_bio_client::options::set_sni()."
480 : << SNAP_LOG_SEND;
481 : }
482 0 : detail::bio_log_errors();
483 0 : throw event_dispatcher_initialization_error("SSL BIO_do_connect() failed connecting BIO object to server");
484 : }
485 :
486 : // encryption handshake
487 : //
488 0 : if(BIO_do_handshake(bio.get()) != 1)
489 : {
490 0 : if(!using_sni)
491 : {
492 0 : SNAP_LOG_WARNING
493 : << "the SNI feature is turned off,"
494 : " often failure to connect with SSL is because the"
495 : " SSL Hello message is missing the SNI (Server Name In)."
496 0 : " See the tcp_bio_client::options::set_sni()."
497 : << SNAP_LOG_SEND;
498 : }
499 0 : detail::bio_log_errors();
500 : throw event_dispatcher_initialization_error("failed establishing a secure BIO connection with server, handshake failed."
501 : " Often such failures to process SSL is because the SSL Hello message is missing the SNI (Server Name In)."
502 0 : " See the tcp_bio_client::options::set_sni().");
503 : }
504 :
505 : // verify that the peer certificate was signed by a
506 : // recognized root authority
507 : //
508 0 : if(SSL_get_peer_certificate(ssl) == nullptr)
509 : {
510 0 : detail::bio_log_errors();
511 0 : throw event_dispatcher_initialization_error("peer failed presenting a certificate for security verification");
512 : }
513 :
514 : // XXX: check that the call below is similar to the example
515 : // usage of SSL_CTX_set_verify() which checks the name
516 : // of the certificate, etc.
517 : //
518 0 : if(SSL_get_verify_result(ssl) != X509_V_OK)
519 : {
520 0 : if(mode != mode_t::MODE_SECURE)
521 : {
522 0 : detail::bio_log_errors();
523 0 : throw event_dispatcher_initialization_error("peer certificate could not be verified");
524 : }
525 0 : SNAP_LOG_WARNING
526 0 : << "connecting with SSL but certificate verification failed."
527 : << SNAP_LOG_SEND;
528 : }
529 :
530 : // it worked, save the results
531 : //
532 0 : f_impl->f_ssl_ctx.swap(ssl_ctx);
533 0 : f_impl->f_bio.swap(bio);
534 :
535 : // secure connection ready
536 : //
537 0 : char const * cipher_name(SSL_get_cipher(ssl));
538 0 : int cipher_bits(0);
539 0 : SSL_get_cipher_bits(ssl, &cipher_bits);
540 0 : SNAP_LOG_DEBUG
541 0 : << "connected with SSL cipher \""
542 : << cipher_name
543 0 : << "\" representing "
544 : << cipher_bits
545 : << " bits of encryption."
546 0 : << SNAP_LOG_SEND;
547 : }
548 0 : break;
549 :
550 0 : case mode_t::MODE_PLAIN:
551 : {
552 : // create a plain BIO connection
553 : //
554 0 : std::shared_ptr<BIO> bio; // use reset(), see SNAP-507
555 0 : bio.reset(BIO_new(BIO_s_connect()), detail::bio_deleter);
556 0 : if(!bio)
557 : {
558 0 : detail::bio_log_errors();
559 0 : throw event_dispatcher_initialization_error("failed initializing a BIO object");
560 : }
561 :
562 : #pragma GCC diagnostic push
563 : #pragma GCC diagnostic ignored "-Wold-style-cast"
564 0 : BIO_set_conn_hostname(bio.get(), const_cast<char *>(addr.c_str()));
565 0 : BIO_set_conn_port(bio.get(), const_cast<char *>(std::to_string(port).c_str()));
566 : #pragma GCC diagnostic pop
567 :
568 : // connect to the server (open the socket)
569 : //
570 0 : if(BIO_do_connect(bio.get()) <= 0)
571 : {
572 0 : detail::bio_log_errors();
573 0 : throw event_dispatcher_initialization_error("failed connecting BIO object to server");
574 : }
575 :
576 : // it worked, save the results
577 : //
578 0 : f_impl->f_bio.swap(bio);
579 :
580 : // plain connection ready
581 : }
582 0 : break;
583 :
584 : }
585 :
586 0 : if(opt.get_keepalive())
587 : {
588 : // retrieve the socket (we are still in the constructor so avoid
589 : // calling other functions...)
590 : //
591 0 : int socket(-1);
592 : #pragma GCC diagnostic push
593 : #pragma GCC diagnostic ignored "-Wold-style-cast"
594 0 : BIO_get_fd(f_impl->f_bio.get(), &socket);
595 : #pragma GCC diagnostic pop
596 0 : if(socket >= 0)
597 : {
598 : // if this call fails, we ignore the error, but still log the event
599 : //
600 0 : int optval(1);
601 0 : socklen_t const optlen(sizeof(optval));
602 0 : if(setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) != 0)
603 : {
604 0 : SNAP_LOG_WARNING
605 0 : << "an error occurred trying to mark client socket with SO_KEEPALIVE."
606 : << SNAP_LOG_SEND;
607 : }
608 : }
609 : }
610 0 : }
611 :
612 :
613 : /** \brief Create a BIO client object from an actual BIO pointer.
614 : *
615 : * This function is called by the server whenever it accepts a new BIO
616 : * connection. The server then can return the tcp_bio_client object instead
617 : * of a BIO object.
618 : *
619 : * The BIO is saved directly in the f_impl class (the server is given access.)
620 : */
621 0 : tcp_bio_client::tcp_bio_client()
622 0 : : f_impl(std::make_shared<detail::tcp_bio_client_impl>())
623 : {
624 0 : }
625 :
626 :
627 : /** \brief Clean up the BIO client object.
628 : *
629 : * This function cleans up the BIO client object by freeing the SSL_CTX
630 : * and the BIO objects.
631 : */
632 0 : tcp_bio_client::~tcp_bio_client()
633 : {
634 : // f_bio and f_ssl_ctx are allocated using shared pointers with
635 : // a deleter so we have nothing to do here.
636 0 : }
637 :
638 :
639 : /** \brief Close the connection.
640 : *
641 : * This function closes the connection by losing the f_bio object.
642 : *
643 : * As we are at it, we also lose the SSL context since we are not going
644 : * to use it anymore either.
645 : */
646 0 : void tcp_bio_client::close()
647 : {
648 0 : f_impl->f_bio.reset();
649 0 : f_impl->f_ssl_ctx.reset();
650 0 : }
651 :
652 :
653 : /** \brief Get the socket descriptor.
654 : *
655 : * This function returns the TCP client socket descriptor. This can be
656 : * used to change the descriptor behavior (i.e. make it non-blocking for
657 : * example.)
658 : *
659 : * \note
660 : * If the socket was closed, then the function returns -1.
661 : *
662 : * \warning
663 : * This socket is generally managed by the BIO library and thus it may
664 : * create unwanted side effects to change the socket under the feet of
665 : * the BIO library...
666 : *
667 : * \return The socket descriptor.
668 : */
669 0 : int tcp_bio_client::get_socket() const
670 : {
671 0 : if(f_impl->f_bio)
672 : {
673 : int c;
674 : #pragma GCC diagnostic push
675 : #pragma GCC diagnostic ignored "-Wold-style-cast"
676 0 : BIO_get_fd(f_impl->f_bio.get(), &c);
677 : #pragma GCC diagnostic pop
678 0 : return c;
679 : }
680 :
681 0 : return -1;
682 : }
683 :
684 :
685 : /** \brief Get the TCP client port.
686 : *
687 : * This function returns the port used when creating the TCP client.
688 : * Note that this is the port the server is listening to and not the port
689 : * the TCP client is currently connected to.
690 : *
691 : * \note
692 : * If the connection was closed, return -1.
693 : *
694 : * \return The TCP client port.
695 : */
696 0 : int tcp_bio_client::get_port() const
697 : {
698 0 : if(f_impl->f_bio)
699 : {
700 0 : return std::atoi(BIO_get_conn_port(f_impl->f_bio.get()));
701 : }
702 :
703 0 : return -1;
704 : }
705 :
706 :
707 : /** \brief Get the TCP server address.
708 : *
709 : * This function returns the address used when creating the TCP address as is.
710 : * Note that this is the address of the server where the client is connected
711 : * and not the address where the client is running (although it may be the
712 : * same.)
713 : *
714 : * Use the get_client_addr() function to retrieve the client's TCP address.
715 : *
716 : * \note
717 : * If the connection was closed, this function returns "".
718 : *
719 : * \return The TCP client address.
720 : */
721 0 : std::string tcp_bio_client::get_addr() const
722 : {
723 0 : if(f_impl->f_bio)
724 : {
725 0 : return BIO_get_conn_hostname(f_impl->f_bio.get());
726 : }
727 :
728 0 : return "";
729 : }
730 :
731 :
732 : /** \brief Get the TCP client port.
733 : *
734 : * This function retrieve the port of the client (used on your computer).
735 : * This is retrieved from the socket using the getsockname() function.
736 : *
737 : * \return The port or -1 if it cannot be determined.
738 : */
739 0 : int tcp_bio_client::get_client_port() const
740 : {
741 : // get_socket() returns -1 if f_bio is nullptr
742 : //
743 0 : int const s(get_socket());
744 0 : if(s < 0)
745 : {
746 0 : return -1;
747 : }
748 :
749 : sockaddr addr;
750 0 : socklen_t len(sizeof(addr));
751 0 : int const r(getsockname(s, &addr, &len));
752 0 : if(r != 0)
753 : {
754 0 : return -1;
755 : }
756 : // Note: I know the port is at the exact same location in both
757 : // structures in Linux but it could change on other Unices
758 0 : switch(addr.sa_family)
759 : {
760 0 : case AF_INET:
761 : // IPv4
762 0 : return reinterpret_cast<sockaddr_in *>(&addr)->sin_port;
763 :
764 0 : case AF_INET6:
765 : // IPv6
766 0 : return reinterpret_cast<sockaddr_in6 *>(&addr)->sin6_port;
767 :
768 0 : default:
769 0 : return -1;
770 :
771 : }
772 : snap::NOT_REACHED();
773 : }
774 :
775 :
776 : /** \brief Get the TCP client address.
777 : *
778 : * This function retrieve the IP address of the client (your computer).
779 : * This is retrieved from the socket using the getsockname() function.
780 : *
781 : * \note
782 : * The function returns an empty string if the connection was lost
783 : * or purposefully closed.
784 : *
785 : * \return The IP address as a string.
786 : */
787 0 : std::string tcp_bio_client::get_client_addr() const
788 : {
789 : // the socket may be invalid, i.e. f_bio may have been deallocated.
790 : //
791 0 : int const s(get_socket());
792 0 : if(s < 0)
793 : {
794 0 : return std::string();
795 : }
796 :
797 : sockaddr addr;
798 0 : socklen_t len(sizeof(addr));
799 0 : int const r(getsockname(s, &addr, &len));
800 0 : if(r != 0)
801 : {
802 0 : throw event_dispatcher_runtime_error("failed reading address");
803 : }
804 : char buf[BUFSIZ];
805 0 : switch(addr.sa_family)
806 : {
807 0 : case AF_INET:
808 : // TODO: verify that 'r' >= sizeof(something)
809 0 : inet_ntop(AF_INET, &reinterpret_cast<struct sockaddr_in *>(&addr)->sin_addr, buf, sizeof(buf));
810 0 : break;
811 :
812 0 : case AF_INET6:
813 : // TODO: verify that 'r' >= sizeof(something)
814 0 : inet_ntop(AF_INET6, &reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_addr, buf, sizeof(buf));
815 0 : break;
816 :
817 0 : default:
818 0 : throw event_dispatcher_runtime_error("unknown address family");
819 :
820 : }
821 0 : return buf;
822 : }
823 :
824 :
825 : /** \brief Read data from the socket.
826 : *
827 : * A TCP socket is a stream type of socket and one can read data from it
828 : * as if it were a regular file. This function reads \p size bytes and
829 : * returns. The function returns early if the server closes the connection.
830 : *
831 : * If your socket is blocking, \p size should be exactly what you are
832 : * expecting or this function will block forever or until the server
833 : * closes the connection.
834 : *
835 : * The function returns -1 if an error occurs. The error is available in
836 : * errno as expected in the POSIX interface.
837 : *
838 : * \note
839 : * If the connection was closed, this function returns -1.
840 : *
841 : * \warning
842 : * When the function returns zero, it is likely that the server closed
843 : * the connection. It may also be that the buffer was empty and that
844 : * the BIO decided to return early. Since we use a blocking mechanism
845 : * by default, that should not happen.
846 : *
847 : * \todo
848 : * At this point, I do not know for sure whether errno is properly set
849 : * or not. It is not unlikely that the BIO library does not keep a clean
850 : * errno error since they have their own error management.
851 : *
852 : * \param[in,out] buf The buffer where the data is read.
853 : * \param[in] size The size of the buffer.
854 : *
855 : * \return The number of bytes read from the socket, or -1 on errors.
856 : *
857 : * \sa read_line()
858 : * \sa write()
859 : */
860 0 : int tcp_bio_client::read(char * buf, size_t size)
861 : {
862 0 : if(!f_impl->f_bio)
863 : {
864 0 : errno = EBADF;
865 0 : return -1;
866 : }
867 :
868 0 : int const r(static_cast<int>(BIO_read(f_impl->f_bio.get(), buf, size)));
869 0 : if(r <= -2)
870 : {
871 : // the BIO is not implemented
872 : //
873 0 : detail::bio_log_errors();
874 0 : errno = EIO;
875 0 : return -1;
876 : }
877 0 : if(r == -1 || r == 0)
878 : {
879 0 : if(BIO_should_retry(f_impl->f_bio.get()))
880 : {
881 0 : errno = EAGAIN;
882 0 : return 0;
883 : }
884 : // did we reach the "end of the file"? i.e. did the server
885 : // close our connection? (this better replicates what a
886 : // normal socket does when reading from a closed socket)
887 : //
888 0 : if(BIO_eof(f_impl->f_bio.get()))
889 : {
890 0 : return 0;
891 : }
892 0 : if(r != 0)
893 : {
894 : // the BIO generated an error
895 0 : detail::bio_log_errors();
896 0 : errno = EIO;
897 0 : return -1;
898 : }
899 : }
900 0 : return r;
901 : }
902 :
903 :
904 : /** \brief Read one line.
905 : *
906 : * This function reads one line from the current location up to the next
907 : * '\\n' character. We do not have any special handling of the '\\r'
908 : * character.
909 : *
910 : * The function may return 0 (an empty string) when the server closes
911 : * the connection.
912 : *
913 : * \note
914 : * If the connection was closed then this function returns -1.
915 : *
916 : * \warning
917 : * A return value of zero can mean "empty line" and not end of file. It
918 : * is up to you to know whether your protocol allows for empty lines or
919 : * not. If so, you may not be able to make use of this function.
920 : *
921 : * \param[out] line The resulting line read from the server. The function
922 : * first clears the contents.
923 : *
924 : * \return The number of bytes read from the socket, or -1 on errors.
925 : * If the function returns 0 or more, then the \p line parameter
926 : * represents the characters read on the network without the '\n'.
927 : *
928 : * \sa read()
929 : */
930 0 : int tcp_bio_client::read_line(std::string & line)
931 : {
932 0 : line.clear();
933 0 : int len(0);
934 : for(;;)
935 : {
936 : char c;
937 0 : int r(read(&c, sizeof(c)));
938 0 : if(r <= 0)
939 : {
940 0 : return len == 0 && r < 0 ? -1 : len;
941 : }
942 0 : if(c == '\n')
943 : {
944 0 : return len;
945 : }
946 0 : ++len;
947 0 : line += c;
948 0 : }
949 : }
950 :
951 :
952 : /** \brief Write data to the socket.
953 : *
954 : * A BIO socket is a stream type of socket and one can write data to it
955 : * as if it were a regular file. This function writes \p size bytes to
956 : * the socket and then returns. This function returns early if the server
957 : * closes the connection.
958 : *
959 : * If your socket is not blocking, less than \p size bytes may be written
960 : * to the socket. In that case you are responsible for calling the function
961 : * again to write the remainder of the buffer until the function returns
962 : * a number of bytes written equal to \p size.
963 : *
964 : * The function returns -1 if an error occurs. The error is available in
965 : * errno as expected in the POSIX interface.
966 : *
967 : * \note
968 : * If the connection was closed, return -1.
969 : *
970 : * \todo
971 : * At this point, I do not know for sure whether errno is properly set
972 : * or not. It is not unlikely that the BIO library does not keep a clean
973 : * errno error since they have their own error management.
974 : *
975 : * \param[in] buf The buffer with the data to send over the socket.
976 : * \param[in] size The number of bytes in buffer to send over the socket.
977 : *
978 : * \return The number of bytes that were actually accepted by the socket
979 : * or -1 if an error occurs.
980 : *
981 : * \sa read()
982 : */
983 0 : int tcp_bio_client::write(char const * buf, size_t size)
984 : {
985 : #ifdef _DEBUG
986 : // This write is useful when developing APIs against 3rd party
987 : // servers, otherwise, it's just too much debug
988 : //SNAP_LOG_TRACE
989 : // << "tcp_bio_client::write(): buf="
990 : // << buf
991 : // << ", size="
992 : // << size
993 : // << SNAP_LOG_SEND;
994 : #endif
995 0 : if(!f_impl->f_bio)
996 : {
997 0 : errno = EBADF;
998 0 : return -1;
999 : }
1000 :
1001 0 : int const r(static_cast<int>(BIO_write(f_impl->f_bio.get(), buf, size)));
1002 0 : if(r <= -2)
1003 : {
1004 : // the BIO is not implemented
1005 0 : detail::bio_log_errors();
1006 0 : errno = EIO;
1007 0 : return -1;
1008 : }
1009 0 : if(r == -1 || r == 0)
1010 : {
1011 0 : if(BIO_should_retry(f_impl->f_bio.get()))
1012 : {
1013 0 : errno = EAGAIN;
1014 0 : return 0;
1015 : }
1016 : // the BIO generated an error (TBD should we check BIO_eof() too?)
1017 0 : detail::bio_log_errors();
1018 0 : errno = EIO;
1019 0 : return -1;
1020 : }
1021 0 : BIO_flush(f_impl->f_bio.get());
1022 0 : return r;
1023 : }
1024 :
1025 :
1026 :
1027 6 : } // namespace ed
1028 : // vim: ts=4 sw=4 et
|