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