4#include <randolf/rline>
5#include <randolf/rring>
6#include <randolf/rring_bam>
7#include <randolf/rsocket_io>
8#include <randolf/rsocket_sni>
9#include <randolf/rtools>
10#include <randolf/sockaddr_dl.h>
12#include <algorithm> // std::min and std::max
14#include <bit> // std::endian
15#include <cstdarg> // std::va_list
16#include <cstring> // std::strlen
17#include <ctime> // std::strftime
18#include <exception> // std::exception
19#include <filesystem> // std::filesystem::temp_directory_path
20#include <initializer_list>
21#include <iostream> // std::put_date
22#include <map> // std::map
23#include <memory> // std::shared_ptr
24#include <mutex> // std::mutex
27//#include <sstream> // std::ostringstream
28#include <unordered_map> // std::unordered_map
34#include <string.h> // strerror()
39#include <linux/fs.h> // Flags for ioctl()
41#include <net/if.h> // ifreq structure used by bind()
43#include <netinet/icmp6.h>
44#include <netinet/in.h>
46#include <netpacket/packet.h> // struct sockaddr_ll
48#include <openssl/err.h>
49#include <openssl/ossl_typ.h>
50#include <openssl/ssl.h>
53#include <sys/socket.h>
54#include <sys/stat.h> // fchmod()
55#include <sys/time.h> // TIMEVAL_TO_TIMESPEC macro
59static_assert((sizeof(__time_t) * CHAR_BIT) >= 64, "64-bit __time_t is required");
63 /*======================================================================*//**
65 This @ref rsocket class provides an easy-to-use and thoroughy-implemented
66 object-oriented socket I/O interface for C++, intended to make socket I/O
67 programming (with or without TLS encryption) easier and more enjoyable.
69 Here's a short list of benefits that are helpful in developing high quality
70 code that's consistent-and-reliable, and improves overall productivity:
72 - eliminating the need to repeatedly write blocks of code that check for
73 errors, by throwing exceptions instead (see @ref randolf::rex::rex class
74 for details and the long list of exceptions that are supported)
75 - eliminating the need to track socket descriptors
76 - eliminating the need to handle encrypted I/O separately (most functions)
77 - eliminating the need to manage memory for many common structures used to
78 interface with socket options, etc.
79 - eliminating the need to record socket I/O statistics with every call to
80 underlying socket I/O functions (see @ref randolf::rsocket_io for
82 - text-line reading/writing with an adapative approach (invented by
83 Randolf Richardson in the 1980s for a custom BBS software project) to
84 automatically detect EoL (End-of-Line) character sequences (unless the
85 developer provides a specific sequence via an @ref eol method) that can
86 determine whether an endpoint is sending Linux/UNIX (standard), MacOS,
87 DOS CR/LF, or even broken LF/CR (reverse of DOS) EoL sequences
88 - transparent support for encryption with many additional features,
89 including STARTTLS, ingress/egress policy enforcement, and SNI
90 - eliminating the complexity of handling events with poll(), select(), and
91 related functions (see the @ref randolf::rsocket_mux class for details)
92 - providing a variety of other useful features that make it easier to
93 communicate with socket endpoints, such as receiving/sending an entire
94 structure via a single call to the new-and-specialized @ref recv_struct
95 or @ref send_struct methods, respectively
97 An rsocket is either the endpoint that our underlying socket will connect to,
98 or it's the server daemon that our underying socket will listen() to and
99 accept() [inbound] connections from.
103 Using the C interface, the programming must check for errors by testing the
104 response codes (most of which are consistent, with a few subtle outliers),
105 which leads to a lot of additional error-checking code with the potential for
106 unintended errors (a.k.a., bugs). This style is necessary in C, but with C++
107 the way to handle errors is with exceptions, so I created this rsocket class
108 to handle all these tedious details behind-the-scenes and, for any socket
109 errors, to generate exceptions so that source code can be greatly simplified
110 (and, as a result, also easier to read and review).
112 Pre-allocating buffers is also handled internally, which is particularly
113 useful when making repeated calls to recv() and related methods. These
114 methods return `std::shared_ptr<structure>` (a C++ smart pointer that aids
115 in the prevention of resource leaks) or `std::vector<char>` (resized to the
116 actual number of bytes received) or `std::string` as appropriate, which
117 eliminates the need to track @c size_t separately.
120 Lower-case letter "r" is regularly used in partial example code to represent
121 an instantiated rsocket object.
123 An ASCIIZ string is a C-string (char* array) that includes a terminating null
124 (0) character at the end.
126 EoS (End of Stream) is used instead of EoF (End of File) because sockets
127 normally represent streams rather than files, which communicates more clearly
128 the intention that using the @c eos() method involves the semantics that come
129 with reading a stream rather than a file.
131 EoL (End of Line) is typically used with the line-receiving methods, which
132 are configured and utilized primarily (and almost exclusively) by the @c eol,
133 @c recvline, and @c recv_rline methods.
135 The following custom qualifiers are incorporated into headings by Doxygen
136 alongside method titles throughout the documentation:
138 - @c POSIX denotes a method that is based on POSIX functions by the same
139 name and don't deviate significantly from the POSIX function arguments
140 (intended to be helpful to developers transitioning to/from rsocket or
141 working on source code that utilizes @ref rsocket and POSIX functions)
143 - @c TLS denotes that a method works properly with TLS-encrypted sockets
144 (most of the POSIX functions have been made to work properly with TLS,
145 but for the few rare cases of functions that can't be made to work with
146 TLS an effort has also been made to mention this using Doxygen's
147 "warning" sections in addition to omitting the TLS qualifier)
149 @par Getting started with a few simple examples
151 This is an example of connecting to an HTTP server, using the "GET" command
152 to request the home page (using HTTP/1.0), then receiving-and-displaying the
153 resulting web page's contents via STDOUT (or sending an error message to
154 STDERR). Finally, we exit with an EXIT_SUCCESS (or EXIT_FAILURE) code.
157 #include <iostream> // std::cout, std::cerr, std::endl, etc.
158 #include <randolf/rex>
159 #include <randolf/rsocket>
161 int main(int argc, char *argv[]) {
163 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
164 r.connect("www.example.com", 80);
165 r.sendline("GET / HTTP/1.0");
166 r.sendline("Host: www.example.com");
167 r.sendline("Connection: close");
170 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
171 } while (!r.eos()); // -x- while data -x-
173 } catch (const randolf::rex::xALL& e) {
174 std::cerr << "Socket exception: " << e.what() << std::endl;
176 } catch (const std::exception& e) {
177 std::cerr << "Other exception: " << e.what() << std::endl;
181 } // -x- int main -x-
184 Parameter stacking is supported (with methods that return @c rsocket*); in
185 this example, notice that semicolons (";") are omittted and "r." is replaced
186 with "." (when compared with the above):
189 #include <iostream> // std::cout, std::cerr, std::endl, etc.
190 #include <randolf/rex>
191 #include <randolf/rsocket>
193 int main(int argc, char *argv[]) {
195 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
196 r.connect("www.example.com", 80)
197 .sendline("GET / HTTP/1.0")
198 .sendline("Host: www.example.com")
199 .sendline("Connection: close")
202 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
203 } while (!r.eos()); // -x- while data -x-
205 } catch (const randolf::rex::xALL& e) {
206 std::cerr << "Socket exception: " << e.what() << std::endl;
208 } catch (const std::exception& e) {
209 std::cerr << "Other exception: " << e.what() << std::endl;
213 } // -x- int main -x-
218 This is meant to be a comprehensive socket class for C++, which is intended
219 to make socket I/O coding easier for developers with some key features:
221 - easy conversion from C-style POSIX sockets due to API consistency
222 - transparent TLS support (OpenSSL dependency)
223 - keys and certificate chains can also be loaded from memory
224 - SNI support (see the @ref rsocket_sni class for more information)
225 - underlying socket handle is accessible (via the @ref socket_fd() method)
226 - socket options are easier to get and to set
227 - sensible support for multipath protocol (see @ref mptcp)
228 - sensible support for future or unknown socket options
229 - errors are presented as ~100 separate exception classes: @ref rex::rex
230 - one parent exception class makes it easier to catch all socket errors
231 - a few exceptions groups are also provided to catch groups of errors
232 - new buffers are returned as std::shared_ptr's to eliminate memory leaks
233 - constructors with sensible defaults help to simplify coding
234 - documentation includes code samples (with @c \#include lines as needed)
235 - debug output with helpful output (and option to set output file handle)
236 - low-overhead is considered (this is why there's a bit more overloading)
237 - thread-safety is noted where it is absolutely available (if any caution
238 is warranted, it will also be noted)
239 - can send ASCIIZ strings (a.k.a., C-strings) without needing to specify
241 - can send @c std::string (which tracks its own string length)
242 - each socket can optionally have a @ref name arbitrarily assigned (e.g.,
243 by an algorithm, or even populated with the connecting user's name, or
244 whatever purpose the development goals find it useful for)
246 Additional features that are not part of the typical POSIX standard, but
247 deserve special mention because they are needed so often:
249 - easy access to internal I/O counters (see @ref rsocket_io for details)
250 - your rsocket_io structure can be updated automatically by rsocket's
251 destructor after underlying socket is closed (see @c net_io_final()
253 - printf(), printfline(), vprintf(), vprintfline() // Formatted strings
254 - with automatic EoL sequence substitution (and/or the addition of)
255 - recv_asciiz(), send_asciiz() // ASCIIZ string I/O operations
256 - recv_byte() , send_byte() // 8-bit byte operations (LSB/MSB is N/A)
257 - recv_struct(), send_struct() // Multi-byte operations
258 - recv_uint16(), send_uint16() // 16-bit operations with LSB/MSB variants
259 - recv_uint32(), send_uint32() // 32-bit operations with LSB/MSB variants
260 - recv_uint64(), send_uint64() // 64-bit operations with LSB/MSB variants
261 - recvline(), recv_rline(), sendline() // ASCII text line I/O operations
262 - class-wide configurable newline sequence (defaults to @e autodetect)
263 - @ref rsocket_group class for grouping rsockets (and automatic inclusion
264 by accept() and accept4() methods; this may be a different group from
265 whichever group the parent rsocket is in, if it's even in one)
266 - option to send data to all rsocket objects in the rsocket_group
267 - with support from the rsocket_mux class (for multiplexing operations)
268 - automatic naming policies (possibly like net_io() formatting style)
270 Some advanced features are planned that exceed what the basic socket I/O
271 functions provide, but are also needed:
273 - recv_uint128()/send_uint128() // 128-bit operations w/ LSB/MSB variants
274 - recv_uint(n)/send_uint(n) where "n" specifies the number of bits (which
275 must be a multiple of 8), with LSB/MSB variants
276 - auto-detection of inbound TLS connection (this is turned off by default)
277 - This is not the same as STARTTLS (an application-level command, for
278 which the @ref tls_do_handshake() method will likely be used)
279 - simple timing tracking options using timing_start() and timing_stop()
280 methods, the results of which can be retrieved with timing_get() or a
281 similarly-named group of methods
283 Other features that are not a high priority:
285 - internal support for portability to Microsoft Windows, which is a major
286 undertaking that I know will be time-consuming since Windows Sockets
287 exhibit some nuanced behaviours and are not consistent with POSIX
288 sockets APIs that are used by Linux, UNIX, MacOS, and pretty much all
289 other Operating Systems. Because of this, MS-Windows portability just
290 isn't a high priority for me (without sufficient demand and sufficient
291 funding so I can commit my time without missing mortgage payments,
292 student loan payments {for my kids}, various living expenses, etc.).
295 The @c __time_t structure must be 64-bit (or larger). This becomes espcially
296 important for the @ref recvline method, in addition to Year 2038 compliance.
300 I use the term "ASCIIZ string" to indicate an array of characters that's
301 terminated by a 0 (a.k.a., null). Although this is very much the same as a
302 C-string, the difference is that in many API functions a C-string must often
303 be accompanied by its length value. When referring to an ASCIIZ string, I'm
304 intentionally indicating that the length of the string is not needed because
305 the string is null-terminated. (This term was also commonly used in assembly
306 language programming in the 1970s, 1980s, and 1990s, and as far as I know is
307 still used by machine language programmers today.)
309 UTF-8 works without any problems as it is backward-compatible to 8-bit ASCII,
310 and because @c std::string uses 8-bit bytes to store strings internally. Do
311 keep in mind that the manipulation of UTF-8 substrings will require working
312 with UTF-8 codepoints that may consume one or more bytes, which is beyond the
313 scope of the impartial (to UTF-8 and ASCII) functionality that this rsocket
316 UTF-8 newline codepoints NEL (c2 85), LS (e2 80 a8), and PS (e2 80 a9) garner
317 no special handling at this time - unlike CR/LF (0d/0a) - and are merely
318 treated as non-newline codepoints. There is a possibility of adding support
319 for this in the future, but additional research and planning is required to
320 make sure this works properly. What is most likely is that some UTF-8 flags
321 will be added to support each of these (which will probably be disabled by
322 default) that will be integrated into the readline() methods. This also
323 depends on how widely used these particular codepoints are, and pending
324 further research to determine whether these really are supposed to be used
325 functionally as newlines...
327 So far, there are two UTF-8 codepoints that absolutely are not functional,
328 yet which a small number of people have mistakenly assumed are:
329 - <sup>C</sup><sub>R</sub> = superscript "C" with subscript "R" (e2 90 9d)
330 - <sup>N</sup><sub>L</sub> = superscript "N" with subscript "L" (e2 90 a4)
332 The special characters above are intended to represent the Carriage-Return
333 and New-Line respectively in documentation such as ASCII character reference
334 charts, which were used mostly by IBM in the 1970s and 1980s for instruction
335 manuals (and in other documentation), and also on a few keyboard overlays.
339 I created this class to make it easier to write internet server daemons. I
340 started out using C-style socket functions (because C++ doesn't come with a
341 socket class), but I found that I didn't enjoy mixing something as important
342 and detailed as socket I/O in a procedural way into the object-oriented
343 paradigm that C++ provides.
345 After looking for existing solutions (none of which served as comprehensive
346 replacements for socket I/O), I embarked on creating the rsocket class, and
347 then I began to understand why this probably hadn't been done -- it's a
348 massive undertaking, primarily because there are a lot of functions that are
349 needed to handle socket I/O. Further, [at the time of this writing] the @c
350 sockaddr_storage structure wasn't as widely used as it should be, and so
351 information about it tended to be scarce, incomplete, or incorrect (further
352 research, and diving down into some pretty deep "rabbit holes," was required
353 to understand this properly, which was worthwhile because it resulted in
354 having transparent support for IPv4 and IPv6 without breaking backward
355 compatibility for code expecting specific structures).
357 Moving error codes into exceptions is also a major effort because they are
358 diverse and plentiful, and there are so many errors that can occur at various
359 stages for many different reasons. There are also a few outlier functions
360 that require slightly different approaches to error handling due to subtly
361 different rules for handling their errors, and so the exception-generation
362 wasn't as straight-forward as one might optimistically expect, but this is
363 one of the many benefits of the object-oriented programming pardigm because
364 handling edge cases internally results in a consistent error-handling
365 interface using exceptions that also simplifies the source code. (Need to
366 handle a specific set of conditions? Catch the relevant exceptions for those
367 cases in an inner set of exceptions, and just catch all the others in a more
368 general way without the added complexity of repeatedly checking for errors
369 every step along the way.)
371 So, I dedicated time to make this work, and with the intention of making it
372 an open source project once I got it into a state that's ready for the
373 general public. This required putting my other C++ projects on hold, which
374 was fine because they didn't have strict deadlines and using this socket
375 class in them will speed up development in the long-term anyway, so it's
376 clearly worth the effort to me ... and I sincerely hope that my efforts will
377 be helpful to others too.
379 My background in programming began when I was a young child, teaching myself
380 BASIC and then machine language (when I found BASIC to be too limited) before
381 moving on to other languages like Perl and Java many years later. Eventually
382 I circled around to C (which I chose to learn the hard way by writing some
383 PostgreSQL extensions) and then C++ a few years after that. I have a lot of
384 experience with socket communications, including fully-featured DNS resolver
385 library code in machine language for Novell's NetWare that used C calling
386 conventions and supported varargs, which worked well for the few developers
387 who needed or wanted it.
390 - 2022-Nov-09 v1.00 Initial version
391 - 2022-Nov-26 v1.00 Moved exception handling to a separate class
392 - 2022-Nov-28 v1.00 Completed readline/send functionality
393 - 2022-Dec-03 v1.00 Added endianness transparency
394 - 2022-Dec-04 v1.00 Added printf() support
395 - 2022-Dec-24 v1.00 Added socket MUXing
396 - 2023-Feb-22 v1.00 Added TLS/SSL support
397 - 2023-Mar-10 v1.00 Added TLS-SNI support
398 - 2023-Apr-19 v1.00 Added TLS certificate loading from memory
399 - 2023-May-24 v1.00 Added support for clang++ compilation
400 - 2023-Jun-09 v1.00 Improvements to dynamic internal read buffering
401 - 2023-Oct-31 v1.00 Improvements to various classes
402 - 2024-Feb-21 v1.00 Added is_buffered() method
403 - 2024-Mar-31 v1.00 Completed @ref recvline (implemented a work-around for
404 a subtle SSL_peek failure to extract additional data
405 when a user at an end-point is communicating with
406 "icanon" mode enabled)
407 - 2024-May-24 v1.00 Changed source code to accomodate @c clang++ compiler
408 - 2024-Jun-04 v1.00 Added @c POSIX and @c TLS qualifiers to all applicable
409 methods (Doxygen incorporates into the documentation)
410 - 2024-Oct-23 v1.00 Various minor improvements to the documentation since
412 - 2024-Nov-05 v1.00 Added eol_consumed_seq() method
413 - 2024-Nov-10 v1.00 Added discard() method
414 - 2024-Nov-17 v1.00 Added recv_rline() method
415 - 2024-Nov-19 v1.00 Added recv_as_string() method
416 - 2024-Nov-22 v1.00 Added discard_line() method
417 - 2024-Nov-28 v1.00 Switched internal ring buffer to a proper ring buffer
418 that's now available in the rring class (which was made
419 initially for this purpose)
420 - 2024-Dec-08 v1.00 Improved shutdown() method to handle SSL shutdown fully
421 and added a parameter to prevent calling SSL_shutdown()
422 - 2024-Dec-10 v1.00 Changed eos() method to use faster ioctl POSIX function
423 and OpenSSL's SSL_has_pending function (with TLS)
424 - 2024-Dec-10 v1.00 Added RECVLINE_FLAGS enum to expand the functionality
425 of how the recvline() and recv_rline methods deal with
426 data in specific scenarios, which helps to satisify the
427 needs of specific advanced data processing scenarios
428 - 2024-Dec-23 v1.00 Updated calls to methods that were renamed today in the
430 - 2024-Dec-28 v1.00 Improvements to internal handling of SNI in all four
431 accept() methods along with the SNI callback function,
432 and added @ref is_tls_sni_has_name() method; <s>also
433 added the @c TLS_SNI_PROMISCUOUS flag</s>
434 - 2024-Dec-30 v1.00 Added the setsockopt() method without a value (and uses
435 a zero-length value internally)
436 - 2024-Dec-31 v1.00 Removed SSL_BIO handle generation calls along with all
437 references to it because we don't need it since we're
438 already handling our own buffering directly and working
439 with OpenSSL's raw API functions directly (the extra
440 layer of abstraction through BIO therefore doesn't
441 benefit what we're doing, and also adds slight overhead
442 that will become noticeable on extremely busy systems)
443 - 2025-Jan-05 v1.00 Added send_rline() method
444 - 2025-Jan-07 v1.00 Made major improvements to the @c recv(), @c recvline()
445 and @c recv_rilne methods, and added an internal class
446 that utilizes RAII to temporarily change the timeout
447 (for the @c recvline and @c recv_rline methods), then
448 finally restores the previous timeout, and added debug
449 output support accordingly; also made major
450 improvements to all recv() and related methods in how
451 they interact with the internal ring buffers (the code
452 is more efficient and a lot simpler)
453 - 2025-Jan-08 v1.00 Updated the @ref timeout methods by adding @c direction
454 (@c SO_RCVTIMEO {default} and @c SO_SNDTIMEO options),
455 and changed @c seconds from type @c int to type @c long
456 - 2025-Jan-18 v1.00 Improved internal memory handling, optimized discard()
457 and discard() line method loops, and simplified the
458 family() method, on top of various minor improvements
459 throughout this entire class during the past week
460 - 2025-Jan-23 v1.00 Performance optimizations
461 - 2025-Jan-30 v1.00 Added support for IPPROTO_MPTCP (whether to continue to
462 use IPPROTO_MPTCP instead of IPPROTO_TCP for the "ai"
463 family of POSIX functions that don't support MPTCP on
464 the majority of Operating Systems at this time; this
465 default may change in the future as MPTCP support from
466 this aspect is expected to improve over time)
467 - 2025-Feb-03 v1.00 Increased use of references and pointers; and renamed
468 two of the timeout() and timeout_recvline() methods to
469 get_timeout() and get_timeout_recvline(), respectively,
470 and a variety of other methods to @c get_ prefixed
471 methods to allow for greater flexibility in setting
472 default values in the API; also added @c shutdown()
473 call tracking so the desstructor can take care of this
474 step to prevent suble resource leaks if an open rsocket
475 object is deleted prematurely
476 - 2025-Feb-04 v1.00 Improvements to documentation (various)
477 - 2025-Feb-05 v1.00 Added @c accept_up() and @c accept4_up() methods that
478 return new rsockets in an @c std::unique_ptr object;
479 added direct support for @c ip_mreqn structure for
480 getsockopt_ip_mreqn and setsockopt methods (along with
481 the accompanying @c debug method to provide specialized
482 output for these two methods)
483 - 2025-Feb-09 v1.00 Added @ref recv_append_to() methods; and changed most
484 methods that return data wrapped in @c std::shared_ptr
485 to return data wrapped in @c std::unique_ptr instead
486 because this has a lot less overhead, and converting
487 from unique_ptr to shared_ptr is trivial, but the other
488 way around is not directly supported
489 - 2025-Feb-10 v1.00 Added the @ref recv_rline_append_to() method and the
490 @ref recvline_append_to() method
491 - 2025-Feb-11 v1.00 Improved both robustness and efficiency of the internal
492 __recvline method that all recvline() methods depend on
493 - 2025-Feb-12 v1.00 Further optimizing of the internal __recvline() method;
494 and added the @c RECVLINE_LIMIT_DISCARD flag option to
495 provide greater control over line-reading behaviour in
496 the event of a data overflow
497 - 2025-Feb-16 v1.00 Added and implemented the @ref TLS_NO_READ_AHEAD flag
498 - 2025-Feb-17 v1.00 Updated @ref net_io and related methods to include
499 support for the @c spare_rx and @c spare_tx statistics
501 @author Randolf Richardson
502 *///=========================================================================
505 // --------------------------------------------------------------------------
506 // The rsocket_group class needs access to some of our internal variables.
507 // --------------------------------------------------------------------------
508 friend class rsocket_group; // Grant the rsocket_group class access to our internals
510 // --------------------------------------------------------------------------
512 // --------------------------------------------------------------------------
513 int __socket_fd = 0; // Raw socket handle
514 int __socket_type = 0; // Socket type (e.g., SOCK_STREAM)
515 int __socket_protocol = 0; // Socket protocol (e.g., AF_INET)
516 struct sockaddr_storage* __socket_addr = (struct sockaddr_storage*)::malloc(sizeof(sockaddr_storage)); // Initialize to all elements to their default values
517 socklen_t __socket_addr_size = sizeof(sockaddr_storage); // We need to point to this (and it might be modified)
518 int __socket_backlog = SOMAXCONN; // Default backlog is 4096 on Linux, and 128 on older systems
520 // --------------------------------------------------------------------------
521 // Socket flags (internal, but with get-methods to provide read-only access).
522 // --------------------------------------------------------------------------
523 std::atomic_bool __socket_open = false; // Socket is open by way of socket() function
524 std::atomic_bool __socket_connected = false; // Socket is connected
525 bool __socket_mptcp_flag = true; // Whether IPPROTO_MPTCP will be substituted with IPPROTO_TCP with certain functions that don't support MPTCP
526 bool __socket_got_shutdown = false; // Whether shutdown() was called (also used by destructor to ensure proper shutdown on sockets that weren't closed)
528 // --------------------------------------------------------------------------
529 // TLS flags and variables.
530 // --------------------------------------------------------------------------
531 std::atomic_bool __tls = false; // Indicates whether socket I/O is encrypted with OpenSSL
532 SSL_CTX* __tls_ctx = nullptr; // TLS context (nullptr == no encryption initialized)
533 rsocket_sni* __tls_sni = nullptr; // SNI maps
534 bool __tls_sni_has_name = false; // Set to TRUE only if SNI received a hostname
535 bool __tls_sni_match = false; // Set to TRUE only if SNI matched in the SNI callback
536 SSL* __tls_fd = nullptr; // TLS connection structure (nullptr == no connection)
537 bool __tls_exclusive = false; // See enum TLS_FLAGS::TLS_EXCLUSIVE
538 bool __tls_egress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_EGRESS
539 bool __tls_ingress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_INGRESS
540 bool __tls_server_mode = true; // See enum TLS_FLAGS::TLS_SERVER
541 bool __tls_no_read_ahead = false; // See enum TLS_FLAGS::TLS_NO_READ_AHEAD
542 inline static std::atomic_int __fn_counter = 0; // Used in generating unique-and-threadsafe temporary filenames
543 const rsocket* __rtemplate = nullptr; // Weak pointer to rsocket that was a template for this rsocket
545 // --------------------------------------------------------------------------
546 // Keep track of test for whether this host stores integers using big endian.
547 // --------------------------------------------------------------------------
548 inline static const bool __endian_is_msb = htons(42) == 42; // TRUE==MSB / FALSE==LSB
550 // --------------------------------------------------------------------------
551 // General variables. Atomic variables are used for variables as needed to
552 // support operations in a thread-safe manner.
553 // --------------------------------------------------------------------------
554 std::string __name; // Arbitrary name of this rsocket (used by some applications)
555 std::string __name_sni; // SNI hostname (only used when tls_sni is configured)
556 std::atomic_bool __debug = false;
557 std::atomic<std::FILE*> __debug_fd = stderr;
558 std::string __debug_prefix = "rsocket-debug";
560 // --------------------------------------------------------------------------
561 // Dynamic buffer expansion for extra-long lines resolves problems with the
562 // default of 4,096 bytes for the read-ahead policy in Linux sockets, so some
563 // automatic expansion is handled by the randolf::rring class (which was
564 // originally created to simplify most of coding for the ring buffer in this
565 // randolf::rsocket class).
566 // --------------------------------------------------------------------------
567 #define RSOCKET_BUFFER_SIZE 8192 // Hard-coded default buffer size
568 size_t __buffer_size = RSOCKET_BUFFER_SIZE; // Buffer size used by recv() when a buffer size isn't provided
569 rring* __buffer = nullptr; // Ring buffer; instantiated by calling recvline() or recv_rline()
570 rring_bam* __buffer_bam = nullptr; // Map of rring's memory blocks
572 // --------------------------------------------------------------------------
573 // EoL sequence variables.
574 // --------------------------------------------------------------------------
575 inline static const char __CR = (char)13; // CTRL-M / ASCII 13 (0Dh) / "\r" / Used by eol() methods
576 inline static const char __LF = (char)10; // CTRL-J / ASCII 10 (0Ah) / "\n" / Used by eol() methods
577 static constexpr const char* __CRLF = "\x0d\x0a"; // Used by eol() methods
578 std::string __eol; // Used by recvline() method; maintained by eol() methods
579 std::string __eol_consumed_seq; // The EoL sequence that was successfully received by the most recent use of the recvline() method
580 bool __eol_adoption = true; // Whether to adopt the dynamically detected EoL sequence when __eol is empty (the default)
581 std::string __eol_out = std::string(__CRLF); // Used by sendline() method; maintained by eol() methods; also used by sendline() method
582 bool __eol_fix_printf = true; // Whether to replace "\n" sequence in printf() methods to EoL sequence
584 // --------------------------------------------------------------------------
585 // Fine-tuning for __recvline(). This is used with nanosleep().
587 // To test, also use commands in canonical mode, like this:
588 // stty -icanon && openssl s_client host:port
589 // stty -icanon && netcat host port
590 // stty -icanon && telnet host port
591 // --------------------------------------------------------------------------
592 long __recvline_timeout = 0; // Number of seconds (0 = unlimited; maximum for "long" equates to approximately 292 billion years since the UNIX/Linux epoch)
594 // --------------------------------------------------------------------------
595 // Statistical variables.
596 // --------------------------------------------------------------------------
597 rsocket_io* __io_final_addr = nullptr; // Used by ~rsocket() destructor, net_io_final(), and net_io_update()
598 std::atomic_ulong __bytes_rx = 0; // Total number of bytes received
599 std::atomic_ulong __bytes_tx = 0; // Total number of bytes transmitted
600 std::atomic_ulong __crypt_rx = 0; // Total number of encrypted bytes recevied
601 std::atomic_ulong __crypt_tx = 0; // Total number of encrypted bytes transmitted
604 /*======================================================================*//**
606 Optional flags used with rsocket's @ref recvline() and @ref recv_rline()
607 methods to specify relevant text-line reading policies/semantics, most of
608 which are primarily aimed at adding flexibility to ease the implementation of
609 text-based line-oriented protocols.
611 These flags provide access to advanced behaviours that most applications and
612 libraries probably won't need since most line-reading is generally pretty
615 Both the @ref recvline and @ref recv_rline methods support all of these flags
616 so as to yield the same expected results, which also makes it easier for
617 developers to switch between these two ways of receiving lines of text from
618 endpoints that are automated in a script-like fashion or individuals using a
619 keyboard to send lines and/or keystrokes with more natural timing.
622 *///=========================================================================
623 enum RECVLINE_FLAGS: int {
625 /*----------------------------------------------------------------------*//**
626 The RECVLINE_DEFAULT flag isn't necessary, but it's included here for
627 completeness as it accomodates programming styles that prefer to emphasize
628 when defaults are being relied upon.
629 *///-------------------------------------------------------------------------
630 RECVLINE_DEFAULT = 0,
632 /*----------------------------------------------------------------------*//**
633 The RECVLINE_NO_DISCARD_ON_OVERFLOW flag prevents data from being discarded
634 when an @ref randolf::rex::xEOVERFLOW exception is thrown (which is caused
635 when a line of text exceeds the specified maximum line length).
638 The @c MSG_PEEK flag also prevents the automatic discarding of data,
639 regardless of whether the @ref randolf::rex::xEOVERFLOW is thrown, thus it
640 differs from this flag in multiple ways that vary with how and why it's used.
643 Data will need to be consumed in a different way, such as by attempting to
644 read into a larger buffer than what had been allocated, or by receiving
645 portions or discarding some or all that remains.
647 By default, when the @ref randolf::rex::xEOVERFLOW exception is thrown, all
648 pending data (up to and including the upcoming EoL sequence, or the EoS,
649 whichever comes first) is discarded.
652 Data is never discarded in the event of a timeout.
654 @see RECVLINE_LIMIT_DISCARD
655 *///-------------------------------------------------------------------------
656 RECVLINE_NO_DISCARD_ON_OVERFLOW = 1,
658 /*----------------------------------------------------------------------*//**
659 The RECVLINE_PARTIAL flag receives an incomplete line of text and returns it
660 instead of throwing the @ref randolf::rex::xEOVERFLOW exception, and causes
661 the @ref eol_consumed_seq() method to return an empty string (which indicates
662 that no EoL sequence was consumed).
663 @see eol_consumed_seq()
664 *///-------------------------------------------------------------------------
665 RECVLINE_PARTIAL = 2,
667 /*----------------------------------------------------------------------*//**
668 The RECVLINE_LIMIT_DISCARD flag instructs @ref recvline(), @ref recv_rline(),
669 and other line-receiving methods to not exceed the maximum length specified
670 if an EoL sequence was not detected when discarding data.
672 By default, when an EoL sequence is not detected the final discard will
673 consume all the data beyond the maximum length until either EoS (End of
674 Stream) is encountered, or when an EoL sequence is detected (before EoS).
677 Data is never discarded in the event of a timeout.
679 This flag is particularly useful for implementing mechanisms like the BDAT
680 command in the SMTP protocol, which specifies the maximum number of bytes (or
681 characters) that can be received in via each BDAT CHUNK -- discarding data
682 beyond a BDAT CHUNK would consume the next SMTP command (which is most likely
683 to be the next BDAT command).
685 @see RECVLINE_NO_DISCARD_ON_OVERFLOW
686 *///-------------------------------------------------------------------------
687 RECVLINE_LIMIT_DISCARD = 4,
689 }; // -x- enum RECVLINE_FLAGS -x-
691 /*======================================================================*//**
693 Optional flags used with various methods to determine whether they will throw
694 an @ref randolf::rex::xETIMEDOUT exception or merely return a @c nullptr, an
695 empty set, or a 0 (zero) when a timeout duration elapses.
697 If you're not sure of whether to use @ref TIMEOUT_EMPTY, @ref TIMEOUT_NULL,
698 or @ref TIMEOUT_ZERO in your code, then I suggest using @ref TIMEOUT_NULL
699 since @c NULL will likely be the most recognizable in most code reviews.
702 You'll know when this is an option because the method will support this.
703 *///=========================================================================
704 enum TIMEOUT_BEHAVIOUR: bool {
706 /*----------------------------------------------------------------------*//**
707 Indicate that an @ref randolf::rex::xETIMEDOUT exception should be thrown
708 when the timeout duration elapses.
709 *///-------------------------------------------------------------------------
710 TIMEOUT_EXCEPTION = true,
712 /*----------------------------------------------------------------------*//**
713 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
714 when the timeout duration elapses.
715 *///-------------------------------------------------------------------------
716 TIMEOUT_EMPTY = false,
718 /*----------------------------------------------------------------------*//**
719 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
720 when the timeout duration elapses.
721 *///-------------------------------------------------------------------------
722 TIMEOUT_NULL = false,
724 /*----------------------------------------------------------------------*//**
725 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
726 when the timeout duration elapses.
727 *///-------------------------------------------------------------------------
728 TIMEOUT_ZERO = false,
730 }; // -x- enum TIMEOUT_BEHAVIOUR -x-
732 /*======================================================================*//**
734 Optional flags used with rsocket's @ref tls() and @ref tls_ctx() methods to
735 specify relevant policies/semantics.
736 *///=========================================================================
737 enum TLS_FLAGS: int {
739 /*----------------------------------------------------------------------*//**
740 The TLS_DEFAULT flag isn't necessary, but it's included here for completeness
741 as it accomodates programming styles that prefer to emphasize when defaults
742 are being relied upon.
743 *///-------------------------------------------------------------------------
746 /*----------------------------------------------------------------------*//**
747 Only encrypted connections are permitted, initially, and all attempts to
748 begin with unencrypted connections will consistently fail.
750 Encrypted connections must begin with a cryptographic handshake packet, or
751 else the connection will be rejected as due to being reasonably assumed to be
754 If this flag isn't set, an application-level mechanism can be used to upgrade
755 to TLS encryption (this is common with connections that begin unencrypted,
756 and issue a proprietary command {such as the @c STARTTLS command in SMTP} to
757 upgrade to TLS later on {of which the @ref tls() method is used to affect an
758 ingress; see the @ref TLS_NO_INGRESS flag for additional information}).
760 Creating an exclusively @c unencrypted connection is accomplished by not
763 @see TLS_NO_EGRESS to prevent support for application-level downgrades to
764 unencrypted connections
765 *///-------------------------------------------------------------------------
768 /*----------------------------------------------------------------------*//**
769 Mid-stream upgrades to encrypted connections are not permitted (e.g., via
770 application-level initiations like the @c STARTTLS command as seen in SMTP),
771 which will also cause calls to the @ref tls() method to fail fast after plain
772 non-encrypted data has already been sent or received.
774 This flag is not necessary when the @ref TLS_EXCLUSIVE flag is set.
775 @see is_tls_ingress_okay()
776 @see tls_do_handshake()
778 *///-------------------------------------------------------------------------
781 /*----------------------------------------------------------------------*//**
782 Mid-stream downgrades to unencrypted connections are not permitted (e.g., via
783 application-level initiations like a hypothetical @c STOPTLS command as seen
784 in FTPS), which will also cause calls to the @ref tls() method to fail fast
785 after encrypted data has already been sent or received.
787 This flag may be combined with the @ref TLS_EXCLUSIVE flag to prevent a
788 connection from being downgraded programatically.
790 Although egress to an unencrypted connection doesn't occur automatically
791 (since egress can only be affected programatically to support commands at the
792 application level), this flag is useful to prevent third-party code from
793 downgrading an encrypted @ref rsocket to unencrypted.
795 Supporting unencrypted communications is strongly discouraged over public
796 networks (e.g., the internet) because unencrypted streams are trivially
797 susceptible to man-in-the-middle attacks that can alter the contents of the
798 data in both directions (which is a particularly dangerous prospect for
799 sending/receiving sensitive information).
800 @see is_tls_egress_okay()
802 *///-------------------------------------------------------------------------
805 /*----------------------------------------------------------------------*//**
806 This is a convenience flag that provides an option for developers to be more
807 clear in their use of the @ref tls() and @ref tls_ctx() methods to indicate
808 intent to rely on what is already the default.
809 @see tls_do_handshake()
811 *///-------------------------------------------------------------------------
814 /*----------------------------------------------------------------------*//**
815 Indicates that this rsocket will be for a server daemon, and to initialize a
816 new TLS context (when one isn't being provided) using OpenSSL's
817 @c TLS_server_method() function instead of OpenSSL's @c TLS_client_method()
818 function (the latter is the default because most code is anticipated to be
822 Setting the CLIENT/SERVER flag incorrectly will typically cause the following
823 error that's difficult to track down, which is usually triggered by calling
824 @ref accept, @ref accept4(), or @ref connect(), and so I hope that including
825 this error message here in this documentation will be helpful:
827 error:140C5042:SSL routines:ssl_undefined_function:called a function you should not call
830 The absence of this flag has the same effect as specifying the @ref
833 *///-------------------------------------------------------------------------
836 /*----------------------------------------------------------------------*//**
837 Skip enabling read-ahead mode when setting up the context in @ref tls_ctx()
838 and related methods. Methods that don't configure read-ahead mode using the
839 @c SSL_CTX_set_read_ahead() or @c SSL_set_read_ahead() methods will ignore
840 this flag if it's present. (The default is to enable read-ahead mode, which
841 is particularly helpful with the @ref recvline and @ref recv_rline methods.)
843 This flag is provided for scenarios where read-head mode should not be
846 *///-------------------------------------------------------------------------
847 TLS_NO_READ_AHEAD = 16,
849 }; // -x- enum TLS_FLAGS -x-
852 /*======================================================================*//**
853 Return Code check, and throws an rsocket-specific exception if an error
854 occurred, which is indicated by @c -1 (a lot of example code shows @c < @c 1
855 comparisons, but we specifically test for @c -1 because the documentation
856 clearly states @c -1. This is important because a system that can support an
857 extremely high number of socket handles might, in theory, assign handles with
858 values that get interpreted as negative integers, and so less-than-zero tests
859 would result in dropped packets or dropped sockets (any such socket code that
860 allocates such handles obviously must not ever allocate @c -1 since this
861 would definitely be misinterpreted as an error).
863 If rc is not @c -1, then it is simply returned as is.
865 If n is nonzero and rc is 0, then n will override errno. (This option is
866 provided to accomodate the few socket library functions that return values
867 that are never errors, and expect the developer to rely on other means of
868 detecting whether an error occurred. This is an example of where Object
869 Oriented Programming is helpful in making things better.)
870 @returns Original value of @c rc (if no exceptions were thrown)
871 *///=========================================================================
872 const int __rc_check(
875 /// Override @c errno (if not 0)
877 /// Exception handling flags (see @ref rex::REX_FLAGS for details)
878 const int flags = rex::rex::REX_FLAGS::REX_DEFAULT) {
879 if (rc == -1 || (rc == 0 && n != 0)) {
880 const int socket_errno = n == 0 ? errno : n; // If "n" is not zero, use it instead
881 if (__debug) debug("__rc_check(" + std::to_string(rc) + ", " + std::to_string(n) + ", " + std::to_string(flags) + ") throwing exception (errno=" + std::to_string(socket_errno) + ")"); // TODO: Remove this
882 randolf::rex::mk_exception("", socket_errno, flags); // This function doesn't return (this code ends here)
885 } // -x- int __rc_check -x-
887 /*======================================================================*//**
888 Similar to @ref __rc_check(), but specialized for handling OpenSSL return
890 *///=========================================================================
891 const int __rc_check_tls(
892 /// Return code (from OpenSSL's API functions)
895 if (__debug) debug("__rc_check_tls(" + std::to_string(rc) + ") throwing exception"); // TODO: Remove this
896 randolf::rex::mk_exception("", SSL_get_error(__tls_fd, rc), rex::rex::REX_FLAGS::REX_TLS); // This function doesn't return (this code ends here)
899 } // -x- int __rc_check -x-
901 /*======================================================================*//**
902 Internal function that opens the socket. This is used by the constructors
903 and their accompanying socket() methods.
905 Throws randolf::rex::xEALREADY exception if the socket is already open to
906 prevent multiple calls to @c open, which can be particularly problematic if
907 any of the additional function calls specify different parameters -- this
908 helps developer(s) to avoid unexpected results if they're inadvertently using
909 the same rsocket object when they intend to use different rsocket objects.
910 *///=========================================================================
912 /// Communication domain; usually one of:@n
914 /// AF_INET6 (IPv6)@n
915 /// AF_UNIX (UNIX domain sockets)
917 /// Communication semantics; usually one of:@n
918 /// SOCK_STREAM (common for TCP)@n
919 /// SOCK_DGRAM (common for UDP)
921 /// Network protocol; usually one of:@n
922 /// IPPROTO_TCP / IPPROTO_MPTCP@n
925 const int protocol) {
926 if (__debug) debug("socket(" + std::to_string(family)
927 + ", " + std::to_string(type)
928 + ", " + std::to_string(protocol)
931 // --------------------------------------------------------------------------
933 // --------------------------------------------------------------------------
934 if (__socket_open) throw randolf::rex::xEALREADY("EALREADY: Socket is already open"); // Socket has already been opened
935 if (family == AF_UNSPEC) throw randolf::rex::xEAFNOSUPPORT("EAFNOSUPPORT: AF_UNSPEC family (SO_DOMAIN) is not supported by socket()");
937 // --------------------------------------------------------------------------
938 // Build minimum parts of __socket_addr structure.
939 // --------------------------------------------------------------------------
940 __socket_addr->ss_family = family;
941 __socket_type = type;
942 __socket_protocol = protocol;
944 // --------------------------------------------------------------------------
945 // Create new socket handle, and save it, then set internal variable to
946 // indicate that the socket is open.
947 // --------------------------------------------------------------------------
948 __socket_fd = __rc_check(::socket(__socket_addr->ss_family, __socket_type, __socket_protocol));
949 __socket_open = true;
951 } // -x- void __socket -x-
954 /*======================================================================*//**
956 Instantiate an empty rsocket without actually opening a socket, and therefore
957 also without throwing any exceptions (useful in header-file definitions).
960 Instantiating an empty rsocket is particularly useful for header-file
961 definitions since exceptions can't be handled outside of subroutines, and
962 it's also useful for enabling debug() mode @em before setting the socket's
963 configuration with one of the socket() methods; for example:
965 randolf::rsocket r; // Empty rsocket (empty / incomplete initialization)
966 r.debug(true); // Enable debug mode
967 r.socket(...); // Required to complete rsocket initialization
972 The built-in defaults, when not provided, are as follows ("family" is also
973 known as the "communication domain"):
974 - @c family = AF_INET
975 - @c type = SOCK_STREAM
976 - @c protocol = PF_UNSPEC
978 You will need to use one of the socket(...) methods to specify socket details
979 after defining rsocket objects with empty constructors so that you can catch
980 runtime exceptions. (This also provides you with an option to enable debug
981 mode during runtime prior to attempting to open an rsocket.)
986 #include <iostream> // std::cout, std::cerr, std::endl, etc.
987 #include <randolf/rex>
988 #include <randolf/rsocket>
990 randolf::rsocket r; // <-- Empty rsocket initialization (no exceptions)
992 int main(int argc, char *argv[]) {
994 r.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Required to complete rsocket initialization
995 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
996 // ... other socket I/O operations
998 } catch (const randolf::rex::xALL& e) {
999 std::cerr << "Socket exception: " << e.what() << std::endl;
1000 return EXIT_FAILURE;
1001 } catch (const std::exception& e) {
1002 std::cerr << "Other exception: " << e.what() << std::endl;
1003 return EXIT_FAILURE;
1005 return EXIT_SUCCESS;
1006 } // -x- int main -x-
1011 @see socket_family()
1013 @see socket_protocol()
1016 *///=========================================================================
1017 rsocket() noexcept {} // -x- constructor rsocket -x-
1019 /*======================================================================*//**
1021 Instantiate an rsocket based on a minimal subset of the settings in the
1022 specified rsocket (using it as a template), without actually opening a
1023 socket, and therefore also without throwing any exceptions.
1026 This constructor does not suffice as a full clone()-like operation, and is
1027 minimal because it's used internally by the @ref accept() and @ref accept4()
1030 Details that are absorbed from the template/source rsocket (which eliminates
1031 the need to assign, set, and configure various parameters (TLS and TLS SNI
1032 parameters will be copied in a passive way by default):
1033 - Socket family (SO_DOMAIN)
1034 - Socket type (SO_TYPE)
1035 - Socket protocol (SO_PROTOCOL)
1036 - Socket protocol special internal handling provision for @c IPPROTO_MPTCP
1037 - TLS details (status, context {which includes loaded certificates},
1038 policies specified with @ref TLS_FLAGS, etc.)
1040 - TLS context (you'll still need to call `tls_ctx(r->tls_ctx())`)
1041 - TLS SNI map (you'll still need to call `tls_sni(r->tls_sni())`)
1044 The TLS Context will not be initialized because it needs a real socket to
1045 draw from. If using TLS, you'll need to use the @ref tls() method after
1046 the underlying socket has been initiated.
1048 When @c flag_create_socket is set to FALSE, no exceptions will be thrown.
1053 @see socket_family()
1055 @see socket_protocol()
1058 *///=========================================================================
1060 /// Source rsocket object to use as a template to absorb settings from
1061 const rsocket* rtemplate,
1062 /// TRUE = create a new socket handle (default)@n
1063 /// FALSE = don't create a new socket because a new one will be assigned or
1064 /// created later (all variants of the @ref accept() methods do this)
1065 const bool flag_create_socket = true) {
1067 // --------------------------------------------------------------------------
1068 // General socket variables.
1069 // --------------------------------------------------------------------------
1070 __rtemplate = rtemplate; // Used by SNI callback (TODO)
1071 if (flag_create_socket) {
1072 __socket(rtemplate->__socket_addr->ss_family,
1073 rtemplate->__socket_type,
1074 rtemplate->__socket_protocol);
1075 } else { // !flag_create_socket
1076 __socket_addr->ss_family = rtemplate->__socket_addr->ss_family;
1077 __socket_type = rtemplate->__socket_type;
1078 __socket_protocol = rtemplate->__socket_protocol;
1079 } // -x- if flag_create_socket -x-
1080 __socket_mptcp_flag = rtemplate->__socket_mptcp_flag;
1081 __name = rtemplate->__name;
1083 // --------------------------------------------------------------------------
1084 // TLS and SNI settings, but not whether TLS is enabled or other settings
1085 // since this is at a pre-handshake stage since this constructor is mostly
1086 // used by the accept() and accept4() methods before accepting a connection.
1087 // --------------------------------------------------------------------------
1088 __tls_ctx = rtemplate->__tls_ctx;
1089 __tls_sni = rtemplate->__tls_sni;
1091 // --------------------------------------------------------------------------
1093 // --------------------------------------------------------------------------
1094 __eol.assign( rtemplate->__eol); // Copy source std::string's contents (not a reference)
1095 __eol_fix_printf = rtemplate->__eol_fix_printf; // Dynamic printf modification policy
1096 __eol_adoption = rtemplate->__eol_adoption; // EoL adoption policy
1098 } // -x- constructor rsocket -x-
1100 /*======================================================================*//**
1102 Instantiate an rsocket with IP/host address and [optional] port number.
1104 This is either the endpoint that our underlying socket will be connecting to,
1105 or it's the local address of the server daemon that our socket will listen()
1106 to and accept() inbound connections from.
1109 The built-in defaults, when not provided, are as follows ("family" is also
1110 known as the "communication domain"):
1111 - @c family = AF_INET
1112 - @c type = SOCK_STREAM
1113 - @c protocol = PF_UNSPEC
1115 The socket() methods do the same work as the constructors with matching
1116 arguments, and are provided as convenience methods intended to augment
1117 empty rsocket constructors used in header files, but do require an address to
1118 be specified (for protocols that need port numbers, such as TCP or UDP, a
1119 "port" number also needs to be specified since the default port 0 will result
1120 in the dynamic allocation of a port number by the system).
1122 For UNIX domain sockets use family AF_UNIX, type SOCK_STREAM, and protocol
1123 IPPROTO_IP when instantiating or opening an rsocket.
1128 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1129 #include <randolf/rex>
1130 #include <randolf/rsocket>
1132 int main(int argc, char *argv[]) {
1134 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Same as socket() method
1135 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
1136 r.bind("127.0.0.1", 32768);
1137 // ... other socket I/O operations
1139 } catch (const randolf::rex::xALL& e) {
1140 std::cerr << "Socket exception: " << e.what() << std::endl;
1141 return EXIT_FAILURE;
1142 } catch (const std::exception& e) {
1143 std::cerr << "Other exception: " << e.what() << std::endl;
1144 return EXIT_FAILURE;
1146 return EXIT_SUCCESS;
1147 } // -x- int main -x-
1150 When using @c IPPROTO_MPTCP a special internal handling provision is used to
1151 automatically prevent errors resulting from the use of POSIX functions that
1152 don't support MPTCP by substituting only the @c IPPROTO_MPTCP parameter with
1153 the @c IPPROTO_TCP parameter. (This substitution does not occur with any
1156 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
1157 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
1158 @throws randolf::rex::xEINVAL Protocal family invalid or not available
1159 @throws randolf::rex::xEINVAL Invalid flags in type
1160 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1161 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1162 @throws randolf::rex::xENOBUFS Insufficient memory
1163 @throws randolf::rex::xENOMEM Insufficient memory
1164 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1165 supported within the specified family (a.k.a., communication domain)
1170 *///=========================================================================
1172 /// Communication domain; usually one of:@n
1173 /// AF_INET (IPv4)@n
1174 /// AF_INET6 (IPv6)@n
1175 /// AF_UNIX (UNIX domain sockets)
1177 /// Communication semantics; usually one of:@n
1178 /// SOCK_STREAM (common for TCP)@n
1179 /// SOCK_DGRAM (common for UDP)
1180 const int type = SOCK_STREAM,
1181 /// Network protocol; usually one of:@n
1182 /// IPPROTO_TCP / IPPROTO_MPTCP@n
1185 /// PF_UNSPEC (auto-detect)
1186 const int protocol = PF_UNSPEC) {
1187 __socket(family, type, protocol);
1188 } // -x- constructor rsocket -x-
1190 /*======================================================================*//**
1192 Destructor, which closes any underlying sockets, frees any TLS structures
1193 that were allocated by OpenSSL, and performs any other necessary clean-up
1194 before finally copying the I/O statistics to a designated structure (if one
1195 was specified with the @ref net_io_final() method).
1197 Developers should take care to check that the @ref rsocket_io::is_final flag
1198 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
1199 since there's no guarantee that the destructor will necessarily be executed
1200 in a timely manner (this flag is set last, after all other statistics are
1201 copied into the structure while in a mutex-locked state).
1203 The order in which various resources are deleted, freed, shutdown, and closed
1204 is important, particularly when TLS is enabled and SNI is used, otherwise the
1205 result can be segmentation faults (which might not show up until later, such
1206 as after returning from @c main depending on how multi-threaded applications
1207 interact with the Operating System; while @c rsocket is designed with threads
1208 in mind, it's also designed to be neutral in this respect so as to provide
1209 the same consistency to both thread-based and non-thread-based applications).
1212 *///=========================================================================
1213 ~rsocket() noexcept {
1215 // --------------------------------------------------------------------------
1217 // --------------------------------------------------------------------------
1219 debug("destructor(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1221 debug(false); // Disable debug mode (might trigger extra clean-up in future)
1222 } // -x- if _debug -x-
1224 // --------------------------------------------------------------------------
1225 // Perform shutdown() before close() if shutdown didn't occur. If we don't
1226 // do this, closing an open socket that wasn't shutdown can leak resources
1227 // on some Operating Systems, and will leak OpenSSL resources.
1228 // --------------------------------------------------------------------------
1230 if (!__socket_got_shutdown && __socket_open) shutdown(SHUT_RDWR);
1231 } catch (const randolf::rex::xENOTCONN& e) {
1232 // Do nothing, although it would be nice to prevent this (delete rsocket after a bind() failiure to trigger this exception)
1235 // --------------------------------------------------------------------------
1236 // Free memory and resources and close handles that need to be freed/closed.
1237 // --------------------------------------------------------------------------
1238 if (__tls_sni != nullptr) tls_sni(nullptr); // Remove any SNI callbacks // WE SHOULDN'T NEED THIS, BUT OpenSSL LEAKS ONCE IN A WHILE IF WE DON'T DO IT
1239 if (__tls_fd != nullptr) SSL_free(__tls_fd); // OpenSSL resource clean-up
1240 //if (__tls_ctx != nullptr) SSL_CTX_free(__tls_ctx); // This causes high CPU on non-encrypted connections
1241 if (__socket_fd != 0) ::close(__socket_fd); // POSIX close() is ensured with ::close()
1242 if (__socket_addr != nullptr) ::free(__socket_addr); // Socket address structure
1243 if (__rtemplate != nullptr) __rtemplate = nullptr; // Clear reference to rsocket template (this is for security; it doesn't actually free or delete anything)
1245 // --------------------------------------------------------------------------
1246 // Copy statistics to final location. (We do this last in case there's an
1247 // issue with locking; there shouldn't be, but if there is then at least
1248 // we're not inadvertently hanging on to any other resources at this point
1249 // that need to be closed and freed.)
1250 // --------------------------------------------------------------------------
1251 if (__io_final_addr != nullptr) {
1252 __io_final_addr->lock();
1253 __io_final_addr->bytes_rx = __bytes_rx;
1254 __io_final_addr->bytes_tx = __bytes_tx;
1255 __io_final_addr->bytes_sx = __tls ? 0 : (__buffer != nullptr ? __buffer->get_utilized() : 0); // <bytes in buffer; are these lost bytes that should be subtracted from __bytes_rx?>
1256 __io_final_addr->crypt_rx = __crypt_rx;
1257 __io_final_addr->crypt_tx = __crypt_tx;
1258 __io_final_addr->crypt_sx = __tls ? (__buffer != nullptr ? __buffer->get_utilized() : 0) : 0; // <bytes in buffer; are these lost bytes that should be subtracted from __crypt_rx?>
1259 __io_final_addr->is_final = true;
1260 __io_final_addr->unlock();
1261 } // -x- if __io_final_addr -x-
1263 // --------------------------------------------------------------------------
1264 // Free memory and resources and close handles that need to be freed/closed.
1265 // --------------------------------------------------------------------------
1266 if (__buffer_bam != nullptr) ::free(__buffer_bam); // Release the rring_bam structure (C)
1267 if (__buffer != nullptr) delete __buffer; // Release the rring buffer (C++)
1269 } // -x- destructor ~rsocket -x-
1271 /*======================================================================*//**
1273 Accept new [inbound] socket connections. (This is typically used in a loop.)
1276 The resulting rsocket object is created before the actual call to the @c
1279 For faster initial connections, disabling the Nagle Algorithm (which was
1280 originally introduced to the Standards Track in 1984 by John Nagle as a
1281 solution that actually solved a lot of network congestion problems by adding
1282 a 200ms internal timeout in TCP packet assembly that made it possible to
1283 reduce the total quantity of packets being transmitted by combining multiple
1284 small portions of data, within this 200ms timeframe, into a single packet
1285 instead of sending multiple packets in rapid succession) will likely improve
1286 responsiveness from a direct end-user experience perspective. To disable the
1287 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1290 #include <netinet/tcp.h> // TCP_NODELAY
1292 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1293 randolf::rsocket* c = r->accept();
1295 This particular socket option will also be carried through automatically by
1296 the network stack and therefore also be set on the resulting client socket,
1297 so it's more efficient to set it this way.
1299 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1300 socket option on the resulting client socket, although this takes more work
1301 despite being used only where it's needed:
1303 #include <netinet/tcp.h> // TCP_CORK
1305 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1306 c->sendline("Welcome");
1307 c->sendline("=======");
1308 c->send("Login name: ");
1309 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1311 Upon the end of the packet-assembly phase, which is communicated to the
1312 network stack by setting the @c TCP_CORK socket option to @c false, the
1313 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1314 which effectively serves as a temporary bypass of the Nagle Algorithm
1315 (this is probably better overall for optimizing internet traffic for the
1316 internet as a whole if your application doesn't need the Nagle Algorithm
1317 disabled for everything).
1320 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1321 select(), and accept()/accept4() when using multiple sockets.
1324 To prevent resource leaks, the resulting rsocket needs to be deleted after
1325 it's no longer needed.
1327 @throws randolf::rex::xEBADF The underlying socket is not open
1328 @throws randolf::rex::xECONNABORTED The connection was aborted
1329 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1330 part of the user address space
1331 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1332 @throws randolf::rex::xEHOSTUNREACH No route to host
1333 @throws randolf::rex::xEINTR Interrupted by a signal
1334 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1335 length of the address is invalid
1336 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1337 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1338 @throws randolf::rex::xENETUNREACH No route to network
1339 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1340 @throws randolf::rex::xENOBUFS Insufficient memory
1341 @throws randolf::rex::xENOMEM Insufficient memory
1342 @throws randolf::rex::xENONET Requested host is not reachable on the network
1343 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1345 @throws randolf::rex::xENOSR System ran out of stream resources
1346 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1347 doesn't refer to a socket
1348 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1349 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1350 @throws randolf::rex::xEPROTO Protocol error
1351 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1352 supported within the specified family (a.k.a., communication domain)
1353 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1354 a system call is restartable and can be intercepted-and-redirected
1355 (there is no need to catch this exception unless you are an advanced
1356 developer, in which case you likely still won't need to catch it in
1358 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1359 supported within the specified family (a.k.a., communication domain)
1360 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1361 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1362 no inbound connections are waiting
1364 @returns Reference to new rsocket object representing the connection received
1371 @see setsockopt(int, int, int)
1374 *///=========================================================================
1376 if (__debug) debug("accept(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1379 // --------------------------------------------------------------------------
1380 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1381 // new incoming connection.
1382 // --------------------------------------------------------------------------
1383 rsocket* r = new rsocket(this, false);
1385 // --------------------------------------------------------------------------
1386 // Wait for incoming connection, and update internal flags in client rsocket.
1387 // --------------------------------------------------------------------------
1388 r->__socket_fd = ::accept(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size);
1389 if (r->__socket_fd == -1) {
1390 delete r; // Memory management
1391 __rc_check(-1); // This part throws the exception
1393 r->__socket_open = true;
1394 r->__socket_connected = true;
1396 // --------------------------------------------------------------------------
1397 // Perform TLS accept() as well, but only if TLS is enabled.
1399 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1400 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1401 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1402 // --------------------------------------------------------------------------
1404 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress | __tls_no_read_ahead); // | __tls_server_mode);
1405 r->tls(true); // Make sure __tls_fd is configured correctly
1406 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1407 __rc_check_tls(SSL_accept(r->__tls_fd));
1408 } // -x- if __tls -x-
1411 } // -x- rsocket* accept -x-
1413 /*======================================================================*//**
1415 Accept new [inbound] socket connections. (This is typically used in a loop.)
1418 The resulting rsocket object is created before the actual call to the @c
1421 For faster initial connections, disabling the Nagle Algorithm (which was
1422 originally introduced to the Standards Track in 1984 by John Nagle as a
1423 solution that actually solved a lot of network congestion problems by adding
1424 a 200ms internal timeout in TCP packet assembly that made it possible to
1425 reduce the total quantity of packets being transmitted by combining multiple
1426 small portions of data, within this 200ms timeframe, into a single packet
1427 instead of sending multiple packets in rapid succession) will likely improve
1428 responsiveness from a direct end-user experience perspective. To disable the
1429 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1432 #include <netinet/tcp.h> // TCP_NODELAY
1434 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1435 std::shared_ptr<randolf::rsocket> c = r->accept_sp();
1437 This particular socket option will also be carried through automatically by
1438 the network stack and therefore also be set on the resulting client socket,
1439 so it's more efficient to set it this way.
1441 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1442 socket option on the resulting client socket, although this takes more work
1443 despite being used only where it's needed:
1445 #include <netinet/tcp.h> // TCP_CORK
1447 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1448 c->sendline("Welcome");
1449 c->sendline("=======");
1450 c->send("Login name: ");
1451 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1453 Upon the end of the packet-assembly phase, which is communicated to the
1454 network stack by setting the @c TCP_CORK socket option to @c false, the
1455 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1456 which effectively serves as a temporary bypass of the Nagle Algorithm
1457 (this is probably better overall for optimizing internet traffic for the
1458 internet as a whole if your application doesn't need the Nagle Algorithm
1459 disabled for everything).
1462 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1463 select(), and accept()/accept4() when using multiple sockets.
1466 The resulting rsocket is wrapped in a @c std::shared_ptr (a C++ smart
1467 pointer that aids in the prevention of resource leaks). Internally, the
1468 `shared pointer` is created using @c std::make_shared because it's more
1469 efficient since it performs only one heap-allocation instead of two (that
1470 accounts for both the control block and the instantiated object's data),
1471 and because it's more robust due to slightly better exception safety.
1473 @throws randolf::rex::xEBADF The underlying socket is not open
1474 @throws randolf::rex::xECONNABORTED The connection was aborted
1475 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1476 part of the user address space
1477 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1478 @throws randolf::rex::xEHOSTUNREACH No route to host
1479 @throws randolf::rex::xEINTR Interrupted by a signal
1480 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1481 length of the address is invalid
1482 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1483 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1484 @throws randolf::rex::xENETUNREACH No route to network
1485 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1486 @throws randolf::rex::xENOBUFS Insufficient memory
1487 @throws randolf::rex::xENOMEM Insufficient memory
1488 @throws randolf::rex::xENONET Requested host is not reachable on the network
1489 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1491 @throws randolf::rex::xENOSR System ran out of stream resources
1492 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1493 doesn't refer to a socket
1494 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1495 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1496 @throws randolf::rex::xEPROTO Protocol error
1497 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1498 supported within the specified family (a.k.a., communication domain)
1499 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1500 a system call is restartable and can be intercepted-and-redirected
1501 (there is no need to catch this exception unless you are an advanced
1502 developer, in which case you likely still won't need to catch it in
1504 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1505 supported within the specified family (a.k.a., communication domain)
1506 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1507 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1508 no inbound connections are waiting
1510 @returns Newly-created rsocket object representing the connection received,
1511 wrapped in std::shared_ptr (a C++ smart pointer)
1518 @see setsockopt(int, int, int)
1520 *///=========================================================================
1521 std::shared_ptr<rsocket> accept_sp() {
1522 if (__debug) debug("accept_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1525 // --------------------------------------------------------------------------
1526 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1527 // new incoming connection.
1528 // --------------------------------------------------------------------------
1529 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1531 // --------------------------------------------------------------------------
1532 // Wait for incoming connection, and update internal flags in client rsocket.
1533 // --------------------------------------------------------------------------
1534 r->__socket_fd = __rc_check(::accept(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size));
1535 r->__socket_open = true;
1536 r->__socket_connected = true;
1538 // --------------------------------------------------------------------------
1539 // Perform TLS accept() as well, but only if TLS is enabled.
1541 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1542 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1543 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1544 // --------------------------------------------------------------------------
1545 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1546 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress | __tls_no_read_ahead); // | __tls_server_mode);
1547 r->tls(true); // Make sure __tls_fd is configured correctly
1548 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1549 __rc_check_tls(SSL_accept(r->__tls_fd));
1550 } // -x- if __tls -x-
1553 } // -x- std::shared_ptr<rsocket> accept_sp -x-
1555 /*======================================================================*//**
1557 Accept new [inbound] socket connections. (This is typically used in a loop.)
1560 The resulting rsocket object is created before the actual call to the @c
1563 For faster initial connections, disabling the Nagle Algorithm (which was
1564 originally introduced to the Standards Track in 1984 by John Nagle as a
1565 solution that actually solved a lot of network congestion problems by adding
1566 a 200ms internal timeout in TCP packet assembly that made it possible to
1567 reduce the total quantity of packets being transmitted by combining multiple
1568 small portions of data, within this 200ms timeframe, into a single packet
1569 instead of sending multiple packets in rapid succession) will likely improve
1570 responsiveness from a direct end-user experience perspective. To disable the
1571 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1574 #include <netinet/tcp.h> // TCP_NODELAY
1576 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1577 std::unique_ptr<randolf::rsocket> c = r->accept_sp();
1579 This particular socket option will also be carried through automatically by
1580 the network stack and therefore also be set on the resulting client socket,
1581 so it's more efficient to set it this way.
1583 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1584 socket option on the resulting client socket, although this takes more work
1585 despite being used only where it's needed:
1587 #include <netinet/tcp.h> // TCP_CORK
1589 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1590 c->sendline("Welcome");
1591 c->sendline("=======");
1592 c->send("Login name: ");
1593 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1595 Upon the end of the packet-assembly phase, which is communicated to the
1596 network stack by setting the @c TCP_CORK socket option to @c false, the
1597 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1598 which effectively serves as a temporary bypass of the Nagle Algorithm
1599 (this is probably better overall for optimizing internet traffic for the
1600 internet as a whole if your application doesn't need the Nagle Algorithm
1601 disabled for everything).
1604 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1605 select(), and accept()/accept4() when using multiple sockets.
1608 The resulting rsocket is wrapped in a @c std::unique_ptr (a C++ smart
1609 pointer that aids in the prevention of resource leaks). Internally, the
1610 `unique pointer` is created using @c std::make_unique because it's more
1611 efficient since it performs only one heap-allocation instead of two (that
1612 accounts for both the control block and the instantiated object's data), and
1613 because it's more robust due to slightly better exception safety.
1615 @throws randolf::rex::xEBADF The underlying socket is not open
1616 @throws randolf::rex::xECONNABORTED The connection was aborted
1617 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1618 part of the user address space
1619 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1620 @throws randolf::rex::xEHOSTUNREACH No route to host
1621 @throws randolf::rex::xEINTR Interrupted by a signal
1622 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1623 length of the address is invalid
1624 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1625 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1626 @throws randolf::rex::xENETUNREACH No route to network
1627 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1628 @throws randolf::rex::xENOBUFS Insufficient memory
1629 @throws randolf::rex::xENOMEM Insufficient memory
1630 @throws randolf::rex::xENONET Requested host is not reachable on the network
1631 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1633 @throws randolf::rex::xENOSR System ran out of stream resources
1634 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1635 doesn't refer to a socket
1636 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1637 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1638 @throws randolf::rex::xEPROTO Protocol error
1639 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1640 supported within the specified family (a.k.a., communication domain)
1641 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1642 a system call is restartable and can be intercepted-and-redirected
1643 (there is no need to catch this exception unless you are an advanced
1644 developer, in which case you likely still won't need to catch it in
1646 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1647 supported within the specified family (a.k.a., communication domain)
1648 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1649 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1650 no inbound connections are waiting
1652 @returns Newly-created rsocket object representing the connection received,
1653 wrapped in std::unique_ptr (a C++ smart pointer)
1660 @see setsockopt(int, int, int)
1662 *///=========================================================================
1663 std::unique_ptr<rsocket> accept_up() {
1664 if (__debug) debug("accept_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1667 // --------------------------------------------------------------------------
1668 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1669 // new incoming connection.
1670 // --------------------------------------------------------------------------
1671 std::unique_ptr<rsocket> r = std::make_unique<rsocket>(this, false);
1673 // --------------------------------------------------------------------------
1674 // Wait for incoming connection, and update internal flags in client rsocket.
1675 // --------------------------------------------------------------------------
1676 r->__socket_fd = __rc_check(::accept(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size));
1677 r->__socket_open = true;
1678 r->__socket_connected = true;
1680 // --------------------------------------------------------------------------
1681 // Perform TLS accept() as well, but only if TLS is enabled.
1683 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1684 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1685 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1686 // --------------------------------------------------------------------------
1687 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1688 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress | __tls_no_read_ahead); // | __tls_server_mode);
1689 r->tls(true); // Make sure __tls_fd is configured correctly
1690 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1691 __rc_check_tls(SSL_accept(r->__tls_fd));
1692 } // -x- if __tls -x-
1695 } // -x- std::unique_ptr<rsocket> accept_up -x-
1697 /*======================================================================*//**
1699 Accept new [inbound] socket connections, with socket flags specified. (This
1700 is typically used in a loop.)
1703 The resulting rsocket object is created before the actual call to the @c
1706 For faster initial connections, disabling the Nagle Algorithm (which was
1707 originally introduced to the Standards Track in 1984 by John Nagle as a
1708 solution that actually solved a lot of network congestion problems by adding
1709 a 200ms internal timeout in TCP packet assembly that made it possible to
1710 reduce the total quantity of packets being transmitted by combining multiple
1711 small portions of data, within this 200ms timeframe, into a single packet
1712 instead of sending multiple packets in rapid succession) will likely improve
1713 responsiveness from a direct end-user experience perspective. To disable the
1714 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1717 #include <netinet/tcp.h> // TCP_NODELAY
1719 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1720 randolf::rsocket* c = r->accept4();
1722 This particular socket option will also be carried through automatically by
1723 the network stack and therefore also be set on the resulting client socket,
1724 so it's more efficient to set it this way.
1726 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1727 socket option on the resulting client socket, although this takes more work
1728 despite being used only where it's needed:
1730 #include <netinet/tcp.h> // TCP_CORK
1732 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1733 c->sendline("Welcome");
1734 c->sendline("=======");
1735 c->send("Login name: ");
1736 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1738 Upon the end of the packet-assembly phase, which is communicated to the
1739 network stack by setting the @c TCP_CORK socket option to @c false, the
1740 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1741 which effectively serves as a temporary bypass of the Nagle Algorithm
1742 (this is probably better overall for optimizing internet traffic for the
1743 internet as a whole if your application doesn't need the Nagle Algorithm
1744 disabled for everything).
1747 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1748 select(), and accept()/accept4() when using multiple sockets.
1751 To prevent resource leaks, the resulting rsocket needs to be deleted after
1752 it's no longer needed.
1754 @throws randolf::rex::xEBADF The underlying socket is not open
1755 @throws randolf::rex::xECONNABORTED The connection was aborted
1756 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1757 part of the user address space
1758 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1759 @throws randolf::rex::xEHOSTUNREACH No route to host
1760 @throws randolf::rex::xEINTR Interrupted by a signal
1761 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1762 length of the address is invalid
1763 @throws randolf::rex::xEINVAL Invalid value in flags
1764 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1765 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1766 @throws randolf::rex::xENETUNREACH No route to network
1767 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1768 @throws randolf::rex::xENOBUFS Insufficient memory
1769 @throws randolf::rex::xENOMEM Insufficient memory
1770 @throws randolf::rex::xENONET Requested host is not reachable on the network
1771 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1773 @throws randolf::rex::xENOSR System ran out of stream resources
1774 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1775 doesn't refer to a socket
1776 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1777 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1778 @throws randolf::rex::xEPROTO Protocol error
1779 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1780 supported within the specified family (a.k.a., communication domain)
1781 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1782 a system call is restartable and can be intercepted-and-redirected
1783 (there is no need to catch this exception unless you are an advanced
1784 developer, in which case you likely still won't need to catch it in
1786 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1787 supported within the specified family (a.k.a., communication domain)
1788 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1789 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1790 no inbound connections are waiting
1792 @returns Newly-created rsocket object representing the connection received
1799 @see setsockopt(int, int, int)
1802 *///=========================================================================
1806 const int posix_flags = 0) {
1807 if (__debug) debug("accept4(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1808 + ", " + std::to_string(posix_flags)
1811 // --------------------------------------------------------------------------
1812 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1813 // new incoming connection.
1814 // --------------------------------------------------------------------------
1815 rsocket* r = new rsocket(this, false);
1817 // --------------------------------------------------------------------------
1818 // Wait for incoming connection, and update internal flags in client rsocket.
1819 // --------------------------------------------------------------------------
1820 r->__socket_fd = ::accept4(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size, posix_flags);
1821 if (r->__socket_fd == -1) {
1822 delete r; // Memory management
1823 __rc_check(-1); // This part throws the exception
1825 r->__socket_open = true;
1826 r->__socket_connected = true;
1828 // --------------------------------------------------------------------------
1829 // Perform TLS accept() as well, but only if TLS is enabled.
1831 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1832 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1833 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1834 // --------------------------------------------------------------------------
1835 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1836 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress | __tls_no_read_ahead); // | __tls_server_mode);
1837 r->tls(true); // Make sure __tls_fd is configured correctly
1838 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1839 __rc_check_tls(SSL_accept(r->__tls_fd));
1840 } // -x- if __tls -x-
1843 } // -x- rsocket* accept4 -x-
1845 /*======================================================================*//**
1847 Accept new [inbound] socket connections, with socket flags specified. (This
1848 is typically used in a loop.)
1851 The resulting rsocket object is created before the actual call to the @c
1854 For faster initial connections, disabling the Nagle Algorithm (which was
1855 originally introduced to the Standards Track in 1984 by John Nagle as a
1856 solution that actually solved a lot of network congestion problems by adding
1857 a 200ms internal timeout in TCP packet assembly that made it possible to
1858 reduce the total quantity of packets being transmitted by combining multiple
1859 small portions of data, within this 200ms timeframe, into a single packet
1860 instead of sending multiple packets in rapid succession) will likely improve
1861 responsiveness from a direct end-user experience perspective. To disable the
1862 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1865 #include <netinet/tcp.h> // TCP_NODELAY
1867 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1868 std::shared_ptr<randolf::rsocket> c = r->accept4_sp();
1870 This particular socket option will also be carried through automatically by
1871 the network stack and therefore also be set on the resulting client socket,
1872 so it's more efficient to set it this way.
1874 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1875 socket option on the resulting client socket, although this takes more work
1876 despite being used only where it's needed:
1878 #include <netinet/tcp.h> // TCP_CORK
1880 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1881 c->sendline("Welcome");
1882 c->sendline("=======");
1883 c->send("Login name: ");
1884 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1886 Upon the end of the packet-assembly phase, which is communicated to the
1887 network stack by setting the @c TCP_CORK socket option to @c false, the
1888 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1889 which effectively serves as a temporary bypass of the Nagle Algorithm
1890 (this is probably better overall for optimizing internet traffic for the
1891 internet as a whole if your application doesn't need the Nagle Algorithm
1892 disabled for everything).
1895 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1896 select(), and accept()/accept4() when using multiple sockets.
1899 The resulting rsocket is wrapped in a @c std::shared_ptr (a C++ smart
1900 pointer that aids in the prevention of resource leaks). Internally, the
1901 `shared pointer` is created using @c std::make_shared because it's more
1902 efficient since it performs only one heap-allocation instead of two (that
1903 accounts for both the control block and the instantiated object's data),
1904 and because it's more robust due to slightly better exception safety.
1906 @throws randolf::rex::xEBADF The underlying socket is not open
1907 @throws randolf::rex::xECONNABORTED The connection was aborted
1908 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1909 part of the user address space
1910 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1911 @throws randolf::rex::xEHOSTUNREACH No route to host
1912 @throws randolf::rex::xEINTR Interrupted by a signal
1913 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1914 length of the address is invalid
1915 @throws randolf::rex::xEINVAL Invalid value in flags
1916 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1917 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1918 @throws randolf::rex::xENETUNREACH No route to network
1919 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1920 @throws randolf::rex::xENOBUFS Insufficient memory
1921 @throws randolf::rex::xENOMEM Insufficient memory
1922 @throws randolf::rex::xENONET Requested host is not reachable on the network
1923 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1925 @throws randolf::rex::xENOSR System ran out of stream resources
1926 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1927 doesn't refer to a socket
1928 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1929 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1930 @throws randolf::rex::xEPROTO Protocol error
1931 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1932 supported within the specified family (a.k.a., communication domain)
1933 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1934 a system call is restartable and can be intercepted-and-redirected
1935 (there is no need to catch this exception unless you are an advanced
1936 developer, in which case you likely still won't need to catch it in
1938 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1939 supported within the specified family (a.k.a., communication domain)
1940 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1941 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1942 no inbound connections are waiting
1944 @returns Newly-created rsocket object representing the connection received,
1945 wrapped in std::shared_ptr (a C++ smart pointer)
1952 @see setsockopt(int, int, int)
1954 *///=========================================================================
1955 std::shared_ptr<rsocket> accept4_sp(
1958 const int posix_flags = 0) {
1959 if (__debug) debug("accept4_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1960 + ", " + std::to_string(posix_flags)
1963 // --------------------------------------------------------------------------
1964 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1965 // new incoming connection.
1966 // --------------------------------------------------------------------------
1967 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1969 // --------------------------------------------------------------------------
1970 // Wait for incoming connection, and update internal flags in client rsocket.
1971 // --------------------------------------------------------------------------
1972 r->__socket_fd = __rc_check(::accept4(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size, posix_flags));
1973 r->__socket_open = true;
1974 r->__socket_connected = true;
1976 // --------------------------------------------------------------------------
1977 // Perform TLS accept() as well, but only if TLS is enabled.
1979 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1980 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1981 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1982 // --------------------------------------------------------------------------
1983 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1984 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress | __tls_no_read_ahead); // | __tls_server_mode);
1985 r->tls(true); // Make sure __tls_fd is configured correctly
1986 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1987 __rc_check_tls(SSL_accept(r->__tls_fd));
1988 } // -x- if __tls -x-
1989// if (__tls_ctx != nullptr) { r->tls(true); r->__tls = (bool)__tls; } // Make sure __tls_fd is allocated appropriately
1992 } // -x- std::shared_ptr<rsocket> accept4_sp -x-
1994 /*======================================================================*//**
1996 Accept new [inbound] socket connections, with socket flags specified. (This
1997 is typically used in a loop.)
2000 The resulting rsocket object is created before the actual call to the @c
2003 For faster initial connections, disabling the Nagle Algorithm (which was
2004 originally introduced to the Standards Track in 1984 by John Nagle as a
2005 solution that actually solved a lot of network congestion problems by adding
2006 a 200ms internal timeout in TCP packet assembly that made it possible to
2007 reduce the total quantity of packets being transmitted by combining multiple
2008 small portions of data, within this 200ms timeframe, into a single packet
2009 instead of sending multiple packets in rapid succession) will likely improve
2010 responsiveness from a direct end-user experience perspective. To disable the
2011 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
2014 #include <netinet/tcp.h> // TCP_NODELAY
2016 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
2017 std::unique_ptr<randolf::rsocket> c = r->accept4_sp();
2019 This particular socket option will also be carried through automatically by
2020 the network stack and therefore also be set on the resulting client socket,
2021 so it's more efficient to set it this way.
2023 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
2024 socket option on the resulting client socket, although this takes more work
2025 despite being used only where it's needed:
2027 #include <netinet/tcp.h> // TCP_CORK
2029 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
2030 c->sendline("Welcome");
2031 c->sendline("=======");
2032 c->send("Login name: ");
2033 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
2035 Upon the end of the packet-assembly phase, which is communicated to the
2036 network stack by setting the @c TCP_CORK socket option to @c false, the
2037 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
2038 which effectively serves as a temporary bypass of the Nagle Algorithm
2039 (this is probably better overall for optimizing internet traffic for the
2040 internet as a whole if your application doesn't need the Nagle Algorithm
2041 disabled for everything).
2044 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
2045 select(), and accept()/accept4() when using multiple sockets.
2048 The resulting rsocket is wrapped in a @c std::unique_ptr (a C++ smart
2049 pointer that aids in the prevention of resource leaks). Internally, the
2050 `unique pointer` is created using @c std::make_unique because it's more
2051 efficient since it performs only one heap-allocation instead of two (that
2052 accounts for both the control block and the instantiated object's data), and
2053 because it's more robust due to slightly better exception safety.
2055 @throws randolf::rex::xEBADF The underlying socket is not open
2056 @throws randolf::rex::xECONNABORTED The connection was aborted
2057 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2058 part of the user address space
2059 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
2060 @throws randolf::rex::xEHOSTUNREACH No route to host
2061 @throws randolf::rex::xEINTR Interrupted by a signal
2062 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
2063 length of the address is invalid
2064 @throws randolf::rex::xEINVAL Invalid value in flags
2065 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
2066 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
2067 @throws randolf::rex::xENETUNREACH No route to network
2068 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
2069 @throws randolf::rex::xENOBUFS Insufficient memory
2070 @throws randolf::rex::xENOMEM Insufficient memory
2071 @throws randolf::rex::xENONET Requested host is not reachable on the network
2072 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
2074 @throws randolf::rex::xENOSR System ran out of stream resources
2075 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2076 doesn't refer to a socket
2077 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
2078 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
2079 @throws randolf::rex::xEPROTO Protocol error
2080 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
2081 supported within the specified family (a.k.a., communication domain)
2082 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
2083 a system call is restartable and can be intercepted-and-redirected
2084 (there is no need to catch this exception unless you are an advanced
2085 developer, in which case you likely still won't need to catch it in
2087 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
2088 supported within the specified family (a.k.a., communication domain)
2089 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
2090 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
2091 no inbound connections are waiting
2093 @returns Newly-created rsocket object representing the connection received,
2094 wrapped in std::unique_ptr (a C++ smart pointer)
2101 @see setsockopt(int, int, int)
2103 *///=========================================================================
2104 std::unique_ptr<rsocket> accept4_up(
2107 const int posix_flags = 0) {
2108 if (__debug) debug("accept4_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2109 + ", " + std::to_string(posix_flags)
2112 // --------------------------------------------------------------------------
2113 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
2114 // new incoming connection.
2115 // --------------------------------------------------------------------------
2116 std::unique_ptr<rsocket> r = std::make_unique<rsocket>(this, false);
2118 // --------------------------------------------------------------------------
2119 // Wait for incoming connection, and update internal flags in client rsocket.
2120 // --------------------------------------------------------------------------
2121 r->__socket_fd = __rc_check(::accept4(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size, posix_flags));
2122 r->__socket_open = true;
2123 r->__socket_connected = true;
2125 // --------------------------------------------------------------------------
2126 // Perform TLS accept() as well, but only if TLS is enabled.
2128 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
2129 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
2130 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
2131 // --------------------------------------------------------------------------
2132 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
2133 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress | __tls_no_read_ahead); // | __tls_server_mode);
2134 r->tls(true); // Make sure __tls_fd is configured correctly
2135 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
2136 __rc_check_tls(SSL_accept(r->__tls_fd));
2137 } // -x- if __tls -x-
2138// if (__tls_ctx != nullptr) { r->tls(true); r->__tls = (bool)__tls; } // Make sure __tls_fd is allocated appropriately
2141 } // -x- std::unique_ptr<rsocket> accept4_up -x-
2143 /*======================================================================*//**
2145 Override the default @ref listen backlog for this rsocket.
2148 The default backlog is `SOMAXCONN` (4096 on Linux, and 128 on older systems).
2150 @returns The same rsocket object so as to facilitate stacking
2154 *///=========================================================================
2156 /// Backlog queue size (0 = use SOMAXCONN, which is the system default of
2157 /// 4096 on Linux, and 128 on older systems)
2158 const int backlog) noexcept {
2159 __socket_backlog = backlog == 0 ? SOMAXCONN : backlog;
2161 } // -x- rsocket& backlog -x-
2163 /*======================================================================*//**
2165 Bind this socket to the specified network address (and port number if the
2166 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
2167 used for server deamons, but can also be used to control the address from
2168 which the local host will make an outbound connection via the @ref connect()
2169 method (this bound address is the address from which the endpoint will
2170 recieve your connection).
2172 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
2173 (or 1023 on some Operating Systems that test only for below 1024) in
2174 the absence of elevated access or the absence of a capability flag
2176 @throws randolf::rex::xEACCES If binding to an interface on systems that
2177 require elevated access for direct interface binding in absence of
2178 said elevated access
2179 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
2180 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
2181 address is not available on any local interface
2182 @throws randolf::rex::xEBADF The underlying socket is not open
2183 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2184 part of the user address space
2185 @throws randolf::rex::xEINVAL Socket is already bound
2186 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
2187 valid for this socket's family (a.k.a., communication domain)
2188 @throws randolf::rex::xENOMEM Insufficient memory
2189 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2190 doesn't refer to a socket
2192 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
2193 @throws randolf::rex::xEACCES Write permission or search permission denied
2194 on a component of the path prefix (@c AF_UNIX family)
2195 @throws randolf::rex::xELOOP Too many symbolic links encountered while
2196 resolving address (@c AF_UNIX family)
2197 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
2198 @throws randolf::rex::xENOENT One of the path's directory components doesn't
2199 exist (@c AF_UNIX family)
2200 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
2201 directory (@c AF_UNIX family)
2202 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
2204 @returns The same rsocket object so as to facilitate stacking
2209 @see bind(std::string, int)
2214 *///=========================================================================
2216 /// Socket address structure
2217 const struct sockaddr* addr,
2218 /// Length of socket structure
2219 const socklen_t addrlen = sizeof(sockaddr)) {
2220 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2222 + ", size=" + std::to_string(addrlen)
2224 if (addrlen > sizeof(sockaddr_storage)) randolf::rex::mk_exception("addlen is too large for bind()", EINVAL);
2225 __rc_check(::bind(__socket_fd, addr, addrlen));
2226 std::memcpy(__socket_addr, addr, addrlen); // No errors were returned by bind(), so now we'll copy addr to __socket_addr
2228 } // -x- rsocket& bind -x-
2230 //===========================================================================
2232 // TODO: Document usage of IP_BIND_ADDRESS_NO_PORT to support connecting
2233 // from a specific IP address without losing ephemeral port selection.
2234 // Notes: https://idea.popcount.org/2014-04-03-bind-before-connect/
2236 // TODO: Support AF_BLUETOOTH addresses
2238 // https://man7.org/linux/man-pages/man7/address_families.7.html
2240 // TODO: Support AF_IPX addresses
2242 // https://man7.org/linux/man-pages/man7/address_families.7.html
2244 // TODO: Support AF_APPLETALK addresses
2245 // Reference: https://man7.org/linux/man-pages/man7/ddp.7.html
2247 // TODO: Support AF_PACKET addresses
2248 // Reference: https://man7.org/linux/man-pages/man7/packet.7.html
2250 // TODO: Support AF_X25 addresses
2251 // Reference: https://man7.org/linux/man-pages/man7/x25.7.html
2253 // TODO: Support AF_NETLINK addresses
2254 // Reference: https://man7.org/linux/man-pages/man7/netlink.7.html
2256 /*======================================================================*//**
2258 Bind this socket to the specified network address (and port number if the
2259 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
2260 used for server deamons, but can also be used to control the address from
2261 which the local host will make an outbound connection via the @ref connect()
2262 method (this bound address is address from which the endpoint will recieve
2265 In addition to the standard IPv4 (AF_INET family) and IPv6 (AF_INET6 family)
2266 support, rsocket also supports a few other binding options in a manner that
2267 makes it easy to utilize, without having to handle special implementation
2268 details (because they're handled behind-the-scenese by this rsocket class).
2270 The address string supports a number of different notations and formats,
2271 which are documented, hereunder, with examples:
2274 - Network interfaces
2275 - UNIX domain sockets
2278 @par IPv4 address notations (address family @c AF_INET)
2279 Takes the form of @c x.x.x.x where each "x" represents an octet in the range
2280 of @c 0 through @c 255 (e.g., @c 127.0.0.1).
2282 Under a proper implenetation of TCP/IP, an IPv4 address can be abbreviated
2283 to fewer octets. The following examples demonstrate this (an unabbreviated
2284 IPv4 address is included for completeness):
2285 - @c 0.0.0.1 may be abbreviated to @c 1
2286 - @c 4.0.0.1 may be abbrevaited to @c 4.1
2287 - @c 4.3.0.1 may be abbreviated to @c 4.3.1
2288 - @c 4.3.2.1 is not abbreviated (this is the most common usage)
2290 @par Example of binding to IPv4 localhost
2293 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2294 #include <randolf/rex>
2295 #include <randolf/rsocket>
2297 int main(int argc, char *argv[]) {
2299 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
2300 r.bind("127.0.0.1", 32768); // <-- You are here
2301 // ... other socket I/O operations
2303 } catch (const randolf::rex::xEACCES& e) {
2304 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
2305 return EXIT_FAILURE;
2306 } catch (const randolf::rex::xALL& e) {
2307 std::cerr << "Socket exception: " << e.what() << std::endl;
2308 return EXIT_FAILURE;
2309 } catch (const std::exception& e) {
2310 std::cerr << "Other exception: " << e.what() << std::endl;
2311 return EXIT_FAILURE;
2313 return EXIT_SUCCESS;
2314 } // -x- int main -x-
2318 Specifying the IP address of @c 0.0.0.0 will bind to all IPv4 addresses that
2319 are assigned to all of the host machine's network interfaces with IPv4
2322 Specifying the IP address of @c 127.0.0.1 (localhost) is useful for serving
2323 only those applications that are running on the local host and use an IPv4
2324 socket to communicate.
2327 @par IPv6 address notations (address family @c AF_INET6)
2328 Takes the form of @c x:x:x:x:x:x:x:x where each "x" represents a segment in
2329 the range of @c 0 through @c ffff in hexadecimal (e.g., `0:0:0:0:0:0:0:1`),
2330 or `x:x:x:x:x:x:y.y.y.y` where `y.y.y.y` represents an [unabbreviated] IPv4
2331 address (which merely replaces the last two IPv6 segments).
2333 Under a proper implenetation of TCP/IP, an IPv6 address can be abbreviated
2334 to fewer segments by using a sequence of two colons (`::`) to indicate that
2335 the undefined segments are @c 0 (this abbreviation can only be used once,
2336 and may represent segments at the beginning or end, or anywhere in between).
2337 The following examples demonstrate this (an unabbreviated IPv6 address is
2338 included for completeness):
2339 - `0:0:0:0:0:0:0:1` may be abbreviated to `::1`
2340 - `0:0:0:0:0:0:2:1` may be abbreviated to `::2:1`
2341 - `8:0:0:0:0:0:2:1` may be abbreviated to `8::2:1`
2342 - `8:7:0:0:0:0:2:1` may be abbreviated to `8:7::2:1`
2343 - `8:7:0:0:0:0:0:0` may be abbreviated to `8:7::`
2344 - `8:0:0:0:0:0:0:0` may be abbreviated to `8::`
2345 - `8:7:6:5:4:3:2:1` is not abbreviated
2347 @par Example of binding to IPv6 localhost
2350 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2351 #include <randolf/rex>
2352 #include <randolf/rsocket>
2354 int main(int argc, char *argv[]) {
2356 randolf::rsocket r(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
2357 r.bind("::1", 32768); // <-- You are here
2358 // ... other socket I/O operations
2360 } catch (const randolf::rex::xEACCES& e) {
2361 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
2362 return EXIT_FAILURE;
2363 } catch (const randolf::rex::xALL& e) {
2364 std::cerr << "Socket exception: " << e.what() << std::endl;
2365 return EXIT_FAILURE;
2366 } catch (const std::exception& e) {
2367 std::cerr << "Other exception: " << e.what() << std::endl;
2368 return EXIT_FAILURE;
2370 return EXIT_SUCCESS;
2371 } // -x- int main -x-
2375 Specifying the IP address of @c :: will bind to all IPv6 addresses that are
2376 assigned to all of the host machine's network interfaces with IPv6 bindings.
2378 Specifying the IP address of @c ::1 (localhost) is useful for serving only
2379 those applications that are running on the local host and use an IPv6 socket
2383 @par Interface address notation (address families @c AF_INET and @c AF_INET6)
2384 Takes the form of @c if=name where "name" represents the name of a local
2387 Interface binding is useful when binding to interfaces that aren't configured
2388 with a static IP address because they were dymamically configured via the
2389 Dynamic Host Configuration Protocol (DHCP) or Stateless Address Configuration
2390 (SLAAC), or the configuration was changed manually by an administrator and
2391 you don't want your software to handle such changes gracefully, even if the
2392 new IP address is within the scope of an entirely different CIDR. (Changing
2393 between the IPv4 and IPv6 addresses is not supported, however, due to the
2394 fundamental differences between these two address families that includes
2395 differences beyond that of IP address format, although under a proper
2396 implementation of TCP/IP the binding should survive such changes when the IP
2397 address is reverted to the initial IP address family of the bound interface.)
2399 Examples of interfaces include:
2400 - `if=lo` typical for localhost virtual network adapter
2401 - `if=bond0` typical for the first bonded virtual network adapter (used in
2402 failover and load-balancing network configurations)
2403 - `if=br0` typical for the first bridge virtual network adapter
2404 - `if=eth0` typical for the first ethernet network adapter
2405 - `if=tap0` typical for the first virtual layer 2 ethernet switch (used by
2406 VPNs to extend a remote network into the local network stack)
2407 - `if=tun0` typical for the first virtual layer 3 ethernet tunnel (used by
2408 VPNs to provide access to a remote network)
2409 - `if=wlo1` typical for the first wireless network adapter
2411 @par Example of binding to interface localhost
2414 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2415 #include <randolf/rex>
2416 #include <randolf/rsocket>
2418 int main(int argc, char *argv[]) {
2420 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
2421 r.bind("if=lo", 32768); // <-- You are here
2422 // ... other socket I/O operations
2424 } catch (const randolf::rex::xEACCES& e) {
2425 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
2426 return EXIT_FAILURE;
2427 } catch (const randolf::rex::xALL& e) {
2428 std::cerr << "Socket exception: " << e.what() << std::endl;
2429 return EXIT_FAILURE;
2430 } catch (const std::exception& e) {
2431 std::cerr << "Other exception: " << e.what() << std::endl;
2432 return EXIT_FAILURE;
2434 return EXIT_SUCCESS;
2435 } // -x- int main -x-
2439 This is not a standard feature of the POSIX bind() function. This rsocket
2440 class uses the setsockopt() function behind-the-scenes to configure (enable)
2441 the @c SO_BINDTODEVICE option, and then the bind() function is called with
2442 the IPv4 address @c 0.0.0.0 if the underlying socket was initialized to the
2443 @c AF_INET family, or the IPv6 address @c :: if the underlying socket was
2444 initialized to the @c AF_INET6 family.
2446 Specifying the interface address of `if=lo` (localhost) is useful for serving
2447 only those applications that are running on the local host and use an IPv4 or
2448 IPv6 socket to communicate.
2451 @par UNIX Domain Sockets address-notation (address family @c AF_UNIX)
2452 Takes the form of @c /path where "/path" represents an absolute path on the
2453 local file system (the path can also be relative, in which case it doesn't
2454 begin with a slash (`/`) character, but extra care is needed to ensure that
2455 the path will actually be at its expected location).
2457 @par Example of binding to UNIX domain sockets
2460 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2461 #include <randolf/rex>
2462 #include <randolf/rsocket>
2464 int main(int argc, char *argv[]) {
2466 randolf::rsocket r(AF_UNIX, SOCK_STREAM);
2467 r.bind("/var/run/rsocket-test-socket.tmp"); // <-- You are here
2468 // ... other socket I/O operations
2470 } catch (const randolf::rex::xEACCES& e) {
2471 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
2472 return EXIT_FAILURE;
2473 } catch (const randolf::rex::xALL& e) {
2474 std::cerr << "Socket exception: " << e.what() << std::endl;
2475 return EXIT_FAILURE;
2476 } catch (const std::exception& e) {
2477 std::cerr << "Other exception: " << e.what() << std::endl;
2478 return EXIT_FAILURE;
2480 return EXIT_SUCCESS;
2481 } // -x- int main -x-
2485 Normal socket I/O functionality is used to exchange data with another process
2486 that can open the same UNIX domain socket (normally on the same host,
2487 although it may not be outside the realm of possibility for future Linux
2488 kernels to support UNIX domain sockets on remote hosts).
2491 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
2492 (or 1023 on some Operating Systems that test only for below 1024) in
2493 the absence of elevated access or the absence of a capability flag
2495 @throws randolf::rex::xEACCES If binding to an interface on systems that
2496 require elevated access for direct interface binding in absence of
2497 said elevated access
2498 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
2499 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
2500 address is not available on any local interface
2501 @throws randolf::rex::xEBADF The underlying socket is not open
2502 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2503 part of the user address space
2504 @throws randolf::rex::xEINVAL Socket is already bound
2505 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
2506 valid for this socket's family (a.k.a., communication domain)
2507 @throws randolf::rex::xENOMEM Insufficient memory
2508 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2509 doesn't refer to a socket
2511 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
2512 @throws randolf::rex::xEACCES Write permission or search permission denied
2513 on a component of the path prefix (@c AF_UNIX family)
2514 @throws randolf::rex::xELOOP Too many symbolic links encountered while
2515 resolving address (@c AF_UNIX family)
2516 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
2517 @throws randolf::rex::xENOENT One of the path's directory components doesn't
2518 exist (@c AF_UNIX family)
2519 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
2520 directory (@c AF_UNIX family)
2521 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
2523 @returns The same rsocket object so as to facilitate stacking
2528 @see bind(const struct sockaddr*, const socklen_t)
2532 *///=========================================================================
2534 /// IPv4 address, IPv6 address, hostname, UNIX domain sockets path, or specialized variant (see notes, above)
2535 const std::string& address,
2536 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
2537 const int port = 0) {
2538 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2540 + " port=" + std::to_string(port) + (port == 0 ? " (emphemeral or not-applicable)" : "")
2541 + ", size=" + std::to_string(__socket_addr_size)
2544//std::cout << " address: " << address << std::endl;
2545 if (address.starts_with("if=")) { // Bind to interface
2546// TODO: Use family() to simplify this section of code
2548 if (address.size() - 3 >= sizeof(ifr.ifr_name)) {} // TODO: Throw exception because ASCIIZ name will be too long
2549 address.copy(ifr.ifr_name, address.size() - 3, 3); // Copy address, excluding the "if=" portion
2550 __rc_check(::setsockopt(__socket_fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)));
2551// TODO: Possibly also add support for SO_BINDTOIFINDEX (perhaps only if "if=" begins with a number and only after SO_BINDTODEVICE fails?)
2552 // TODO: Figure out how to use INADDR_ANY instead of 0 or :: (assuming this is even possible)
2553 // TODO: Test IPv6 more (telnet doesn't seem to be able to connect, and continues to work on IPv4 address)
2554// __socket_addr = *mk_sockaddr_storage(__socket_addr.ss_family == AF_INET6 ? "::" : "0", port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_mptcp_flag && __socket_protocol == IPPROTO_MPTCP ? IPPROTO_TCP : __socket_protocol});
2555 addrinfo ai{0, __socket_addr->ss_family, __socket_type, __socket_mptcp_flag && __socket_protocol == IPPROTO_MPTCP ? IPPROTO_TCP : __socket_protocol};
2556 struct sockaddr_storage* sa = mk_sockaddr_storage(__socket_addr->ss_family == AF_INET6 ? "::" : "0", port, &ai);
2557 ::free((void*)__socket_addr);
2559 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
2560// TODO: Figure out whether to use (sockaddr*)&__socket_addr or (sockaddr*)__socket_addr
2561// } else if (address.starts_with("if_cidr=")) { // Bind to interface based on matching CIDR
2562// // REMINDER: Update static family() method as well
2563// getifaddrs: https://man7.org/linux/man-pages/man3/getifaddrs.3.html
2564// } else if (address.starts_with("if_mac=")) { // Bind to interface based on its physical/hardware machine address
2565// // REMINDER: Update static family() method as well
2566// // Resolve interface name, and then just do what if= does (above)
2569// __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_mptcp_flag && __socket_protocol == IPPROTO_MPTCP ? IPPROTO_TCP : __socket_protocol});
2570 addrinfo ai{0, __socket_addr->ss_family, __socket_type, __socket_mptcp_flag && __socket_protocol == IPPROTO_MPTCP ? IPPROTO_TCP : __socket_protocol};
2571 struct sockaddr_storage* sa = mk_sockaddr_storage(address, port, &ai);
2572 ::free((void*)__socket_addr);
2574// TODO: Figure out whether to use (sockaddr*)&__socket_addr or (sockaddr*)__socket_addr
2575// __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
2576 __rc_check(::bind(__socket_fd, (sockaddr*)__socket_addr, __socket_addr_size));
2577// __rc_check(::bind(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
2578 } // -x- if address.starts_with -x-
2581 } // -x- rsocket& bind -x-
2583 /*======================================================================*//**
2585 Override the default buffer size (typically 8,192 bytes) used by the various
2586 @ref recv() methods.
2588 If resetting to the compiled-in default, use the buffer_size_reset() method
2589 instead of setting the value directly. This ensures that future versions,
2590 with a different compiled-in default, will be reset to the compiled-in value.
2591 @exception std::overflow_error If the new size exceeds the amount of data
2592 that's already present in the ring buffer
2593 @returns The same rsocket object so as to facilitate stacking
2594 @see buffer_size_reset()
2595 @see get_buffer_size()
2597 *///=========================================================================
2598 rsocket& buffer_size(
2599 /// Size of the new buffer (in bytes)
2600 const size_t nbytes) {
2601 __buffer_size = nbytes;
2602 if (__debug) debug("buffer_size(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2603 + " nbytes=" + std::to_string(nbytes)
2605 if (__buffer != nullptr) __buffer->set_size(nbytes);
2607 } // -x- rsocket& buffer_size -x-
2609 /*======================================================================*//**
2611 Reset the default buffer size (typically 8,192 bytes) used by the various
2612 @ref recv() methods.
2614 This method is preferred for resetting to the compiled-in default instead of
2615 setting the value directly. This ensures that future versions, with a
2616 different compiled-in default, will be reset to the compiled-in value.
2617 @exception std::overflow_error If the new size exceeds the amount of data
2618 that's already present in the ring buffer
2619 @returns The same rsocket object so as to facilitate stacking
2620 @see buffer_size(const size_t nbytes)
2621 @see get_buffer_size()
2623 *///=========================================================================
2624 rsocket& buffer_size_reset() {
2625 __buffer_size = RSOCKET_BUFFER_SIZE;
2626 if (__debug) debug("buffer_size_reset(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2627 + " nbytes=" + std::to_string(RSOCKET_BUFFER_SIZE)
2629 if (__buffer != nullptr) __buffer->set_size(__buffer_size);
2631 } // -x- rsocket& buffer_size_reset -x-
2633 //===========================================================================
2635 // TODO: If family is AF_UNIX then also unlink the domain socket file.
2637 /*======================================================================*//**
2639 Close this rsocket. (If this rsocket was already closed, then calling this
2640 method additional times will have no effect, and will not cause exceptions to
2643 This method may throw exceptions in some circumstances, which is extremely
2644 rare. If you prefer to close without having to contend with any exceptions
2645 (e.g., while calling close() from within a destructor), the close_passive()
2646 method will return an integer indicating success/failure instead of throwing
2647 an exception, and then you'll have to handle errno manually (which is useful
2648 in destructors because any error can merely be handled procedurally).
2650 @throws randolf::rex::xEBADF The underlying socket is not open
2651 @throws randolf::rex::xEINTR Interrupted by a signal
2652 @throws randolf::rex::xEIO An I/O error occurred
2654 @returns The same rsocket object so as to facilitate stacking
2658 *///=========================================================================
2660 if (__socket_open) {
2661 __rc_check(::close(__socket_fd));
2662 __socket_open = false;
2663 } // -x- if __socket_open -x-
2665 } // -x- rsocket& close -x-
2667 /*======================================================================*//**
2669 Close this rsocket without throwing any exceptions (an error code is returned
2670 instead, which is useful while calling close_passive() from within a
2672 @returns 0 = success
2673 @returns -1 = error (errno will be set accordingly)
2676 *///=========================================================================
2677 int close_passive() noexcept {
2678 if (__socket_open) {
2679 int rc = ::close(__socket_fd);
2680 if (rc == 0) __socket_open = false;
2682 } // -x- if __socket_open -x-
2683 return 0; // Indicate success (because the socket was previously closed successfully)
2684 } // -x- int close_passive -x-
2686 /*======================================================================*//**
2688 Connect this socket to a specific endpoint (which may differ from this
2689 rsocket's address that was previously configured by the @ref bind() method).
2691 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
2692 @throws randolf::rex::xEADDRNOTAVAIL No ephemeral ports are available for
2693 assignment to unbound socket
2694 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
2695 @throws randolf::rex::xEAGAIN Insufficient entries in the routing cache
2696 @throws randolf::rex::xEALREADY Previous connection attempt not completed on
2698 @throws randolf::rex::xEBADF The underlying socket is not open
2699 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
2701 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2702 part of the user address space
2703 @throws randolf::rex::xEINPROGRESS Connection cannot be completed immediately
2704 on nonblocking socket (@c AF_UNIX family fails with xEAGAIN instead)
2705 @throws randolf::rex::xEINTR Interrupted by a signal
2706 @throws randolf::rex::xEISCONN Socket is already connected
2707 @throws randolf::rex::xENETUNREACH No route to network
2708 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2709 doesn't refer to a socket
2710 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
2711 @throws randolf::rex::xEPERM Can't connect to a broadcast address when the
2712 socket's broadcast flag isn't enabled
2713 @throws randolf::rex::xEPROTOTYPE Socket type doesn't support the requested
2714 communications protocol (e.g., connecting a UNIX domain socket of
2715 type @c SOCK_STREAM to an endpoint expecting type @c SOCK_DGRAM)
2716 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
2718 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
2719 @throws randolf::rex::xEACCES Write permission or search permission denied
2720 on a component of the path prefix (@c AF_UNIX family)
2721 @throws randolf::rex::xEAGAIN Connection cannot be completed immediately on
2722 nonblocking socket (@c AF_UNIX family)
2724 @returns The same rsocket object so as to facilitate stacking
2725 @see tls_do_handshake()
2728 *///=========================================================================
2730 /// IPv4 address, IPv6 address, hostname, or UNIX domain sockets path
2731 const std::string& address,
2732 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
2733 const int port = 0) {
2734 if (__debug) debug("connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2736 + " (port=" + std::to_string(port) + ")"
2737 + ", " + std::to_string(__socket_addr_size)
2740 addrinfo ai{0, __socket_addr->ss_family, __socket_type, __socket_mptcp_flag && __socket_protocol == IPPROTO_MPTCP ? IPPROTO_TCP : __socket_protocol};
2741 struct sockaddr_storage* sa = mk_sockaddr_storage(address, port, &ai);
2742 ::free((void*)__socket_addr); // Free before re-assigning new pointer
2745 __rc_check(::connect(__socket_fd, (sockaddr*)__socket_addr, __socket_addr_size), 0, rex::rex::REX_FLAGS::REX_ALT_EAGAIN);
2746 __socket_connected = true;
2748 // --------------------------------------------------------------------------
2750 // --------------------------------------------------------------------------
2752 if (__debug) debug("tls_connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2754 __rc_check_tls(SSL_connect(__tls_fd));
2755 } // -x- if __tls -x-
2758 } // -x- rsocket& connect -x-
2760 /*======================================================================*//**
2762 Find out whether debug mode is enabled.
2765 This method is threadsafe.
2766 @returns TRUE = enabled
2767 @returns FALSE = not enabled
2770 *///=========================================================================
2771 bool debug() noexcept {
2773 } // -x- bool debug -x-
2775 /*======================================================================*//**
2777 Debug mode. When debug mode is enabled, output is sent to stderr by default,
2778 unless a second parameter specifies a different file handle (e.g., stdout, or
2781 debug(...) returns -1 if fd can't be written to (errno will be set in
2782 accordance with std::fprintf's behaviour since std::fprintf is used for
2783 writing debug output). Normally we'd throw an exception for such errors, but
2784 with debug is a special case because debugging needs to be as quick and
2785 convenient as possible for developers.
2787 debug(...) returns -2 if writing to stderr failed when attempting to announce
2788 that fd can't be written to (as described above).
2790 debug(...) returns -3 if some other error occurs (e.g., internal formatting
2791 error {which should not happen} or memory allocation failed, in which case
2792 there's a more serious problem that will be causing other problems elsewhere
2795 Developers may add their own debug messages by using debug(std::string),
2796 which will only be written out if debug mode is enabled. This same method is
2797 used internally, so messages will be indistinguishable from developer's
2801 This method is thread-safe.
2802 @returns 0 = success
2803 @returns -1 = error writing to stream (errno will be set accordingly)
2804 @returns -2 = error writing to stderr (errno will be set accordingly)
2805 @returns -3 = error (errno will be set accordingly)
2808 *///=========================================================================
2810 /// TRUE = enable debug mode@n
2811 /// FALSE = disable debug mode (does not close any file handles)
2812 const bool debug_flag,
2813 /// File descriptor/handle to use for debug output
2814 std::FILE* fd = stderr) noexcept {
2816 int rc = debug_fd(fd); // Attempt to change debug handle
2817 if (rc != 0) return rc; // Return error id new debug handle failed
2818 time_t tm = std::time(nullptr);
2819 std::string dt(27, 0);
2820 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2822 if (debug_flag || __debug) // Show debug message if debug is already or if we're turning it on
2823 std::fprintf(__debug_fd,
2824 "[%s] %s: Debug mode is %s\n",
2826 __debug_prefix.data(),
2827 debug_flag ? "ON" : "OFF");
2828 } catch (const std::system_error& e) { // Error writing to fd
2830 std::fprintf(stderr,
2831 "[%s] %s: error writing to stream\n",
2833 __debug_prefix.data());
2834 } catch (const std::exception& e) {
2838 } catch (const std::exception& e) {
2841 __debug = debug_flag; // Save debug flag
2843 return 0; // Indicate success (no errors)
2844 } // -x- int debug -x-
2846 /*======================================================================*//**
2848 Send the specified message as debug output (as long as debug mode is enabled;
2849 if disabled, no debug output will be sent).
2850 @returns 0 = success
2851 @returns -1 = error writing to stream (errno will be set accordingly)
2852 @returns -2 = error writing to stderr (errno will be set accordingly)
2853 @returns -3 = error (errno will be set accordingly)
2857 *///=========================================================================
2859 /// Debug message as an ASCIIZ string
2860 const char* msg) noexcept {
2861 return __debug ? __debug_out(std::string(msg)) : 0;
2862 } // -x- int debug -x-
2864 /*======================================================================*//**
2865 @copydoc debug(const char*)
2867 *///=========================================================================
2869 /// Debug message as an std::string object
2870 const std::string& msg) noexcept {
2871 return __debug ? __debug_out(msg) : 0;
2872 } // -x- int debug -x-
2874 /*======================================================================*//**
2876 Find out which file descriptor/handle is used for debug output.
2877 @returns file handle/descriptor currently used for debug output
2878 *///=========================================================================
2879 std::FILE* debug_fd() noexcept {
2881 } // Get debug-output file handle
2883 /*======================================================================*//**
2885 Specify a different file descriptor/handle to use for debug output
2886 @returns 0 = success
2887 @returns -1 = error writing to stream (errno will be set accordingly)
2888 @returns -2 = error writing to stderr (errno will be set accordingly)
2889 @returns -3 = error (errno will be set accordingly)
2890 *///=========================================================================
2892 /// File descriptor/handle to use for debug output
2893 std::FILE* fd) noexcept { // Set debug-output file handle
2894 if (__debug_fd != fd) {
2895 debug("Current debug-output file handle unset"); // For this message, we only want to send this out if debug mode is enabled
2896 __debug_fd = fd; // Save new debug-output file handle
2897 return debug("New debug-output file handle set"); // For this message, we only want to send this out if debug mode is enabled
2899 return 0; // Indicate success (no errors)
2900 } // -x- int debug_fd -x-
2902 /*======================================================================*//**
2904 Find out what the current prefix is set to that's used in debug output.
2906 This may be set at any time, including before or after enabling or disabling
2908 @returns debug prefix string
2911 *///=========================================================================
2912 std::string debug_prefix() noexcept {
2913 return __debug_prefix;
2914 } // -x- std::string debug_prefix -x-
2916 /*======================================================================*//**
2918 Change the prefix used in debug output.
2919 @returns The same rsocket object so as to facilitate stacking
2922 *///=========================================================================
2923 rsocket& debug_prefix(
2924 /// New debug prefix string
2925 const std::string& prefix) noexcept {
2926 __debug_prefix = prefix;
2928 } // -x- rsocket& debug_prefix -x-
2931 /*----------------------------------------------------------------------*//**
2933 Internal debug-output function. This doesn't check __debug flag because it's
2934 expected that the normal debug() methods are taking care of this.
2935 @returns 0 = success
2936 @returns -1 = error writing to stream (errno will be set accordingly)
2937 @returns -2 = error writing to stderr (errno will be set accordingly)
2938 @returns -3 = error (errno will be set accordingly)
2941 *///-------------------------------------------------------------------------
2943 /// Debugging message
2944 const std::string& msg) noexcept {
2946 time_t tm = std::time(nullptr);
2947 std::string dt(27, 0);
2948 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2950 std::fprintf(__debug_fd,
2953 __debug_prefix.data(),
2955 } catch (const std::system_error& e) { // Error writing to fd
2957 std::fprintf(stderr,
2958 "[%s] %s: error writing to stream\n",
2960 __debug_prefix.data());
2961 } catch (const std::exception& e) {
2965 } catch (const std::exception& e) {
2969 return 0; // Indicate success (no errors)
2970 } // -x- int __debug_out -x-
2972 // --------------------------------------------------------------------------
2973 // These are specialized internal debug output methods for presenting socket
2974 // options when they are get or set. These methods are marked as "private"
2975 // because they are really aren't useful outside of internal-use contexts.
2978 // Some of these methods are thread-safe.
2980 // These methods are thread-safe when they don't reference structures (e.g.,
2981 // integer {int}, unsigned integer {u_int}, and unsigned character {u_char}).
2983 // These methods are not necessarily thread-safe if they reference structures
2984 // unless those structures are defined with std::atomic, or they were
2985 // constructed on-the-fly as anonymous parameters.
2986 // --------------------------------------------------------------------------
2987 int __debug_sockopt(
2988 /// Name of function or method
2989 const std::string& func,
2990 /// The level at which the option resides; typically @c SOL_SOCKET
2992 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2994 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2995 + ", " + std::to_string(level)
2996 + ", " + std::to_string(option)
3000 } // -x- int __debug_sockopt -x-
3001 int __debug_sockopt(
3002 /// Name of function or method
3003 const std::string& func,
3004 /// The level at which the option resides; typically @c SOL_SOCKET
3006 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3008 /// The structure that this socket option will be set to
3010 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3011 + ", " + std::to_string(level)
3012 + ", " + std::to_string(option)
3013 + ", " + std::to_string(value)
3014 + ", size=" + std::to_string(sizeof(value))
3016 } // -x- int __debug_sockopt -x-
3017 int __debug_sockopt(
3018 /// Name of function or method
3019 const std::string& func,
3020 /// The level at which the option resides; typically @c SOL_SOCKET
3022 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3024 /// The structure that this socket option will be set to
3025 const u_int value) {
3026 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3027 + ", " + std::to_string(level)
3028 + ", " + std::to_string(option)
3029 + ", u_int{" + std::to_string(value) + "}"
3030 + ", size=" + std::to_string(sizeof(value))
3032 } // -x- int __debug_sockopt -x-
3033 int __debug_sockopt(
3034 /// Name of function or method
3035 const std::string& func,
3036 /// The level at which the option resides; typically @c SOL_SOCKET
3038 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3040 /// The structure that this socket option will be set to
3041 const u_char value) {
3042 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3043 + ", " + std::to_string(level)
3044 + ", " + std::to_string(option)
3045 + ", u_char{" + std::to_string(value) + "}"
3046 + ", size=" + std::to_string(sizeof(value))
3048 } // -x- int __debug_sockopt -x-
3049 int __debug_sockopt(
3050 /// Name of function or method
3051 const std::string& func,
3052 /// The level at which the option resides; typically @c SOL_SOCKET
3054 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3056 /// The structure that this socket option will be set to
3057 const linger* value) {
3058 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3059 + ", " + std::to_string(level)
3060 + ", " + std::to_string(option)
3061 + ", linger{l_onoff=" + std::to_string(value->l_onoff)
3062 + ", l_linger=" + std::to_string(value->l_linger) + "}"
3063 + ", " + std::to_string(sizeof(value))
3065 } // -x- int __debug_sockopt -x-
3066 int __debug_sockopt(
3067 /// Name of function or method
3068 const std::string& func,
3069 /// The level at which the option resides; typically @c SOL_SOCKET
3071 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3073 /// The structure that this socket option will be set to
3074 const timeval* value) {
3075 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3076 + ", " + std::to_string(level)
3077 + ", " + std::to_string(option)
3078 + ", timeval{tv_sec=" + std::to_string(value->tv_sec)
3079 + ", tv_usec=" + std::to_string(value->tv_usec) + "}"
3080 + ", " + std::to_string(sizeof(value))
3082 } // -x- int __debug_sockopt -x-
3083 int __debug_sockopt(
3084 /// Name of function or method
3085 const std::string& func,
3086 /// The level at which the option resides; typically @c SOL_SOCKET
3088 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3090 /// The structure that this socket option will be set to
3091 const in_addr* value) {
3092 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3093 + ", " + std::to_string(level)
3094 + ", " + std::to_string(option)
3095 + ", in_addr{" + std::to_string(value->s_addr) + "}"
3096 + ", " + std::to_string(sizeof(value))
3098 } // -x- int __debug_sockopt -x-
3099 int __debug_sockopt(
3100 /// Name of function or method
3101 const std::string& func,
3102 /// The level at which the option resides; typically @c SOL_SOCKET
3104 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3106 /// The structure that this socket option will be set to
3107 const ip_mreq* value) {
3108 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3109 + ", " + std::to_string(level)
3110 + ", " + std::to_string(option)
3111 + ", ip_mreq{addr=" + std::to_string(value->imr_multiaddr.s_addr)
3112 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
3113 + ", " + std::to_string(sizeof(value))
3115 } // -x- int __debug_sockopt -x-
3116 int __debug_sockopt(
3117 /// Name of function or method
3118 const std::string& func,
3119 /// The level at which the option resides; typically @c SOL_SOCKET
3121 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3123 /// The structure that this socket option will be set to
3124 const ip_mreq_source* value) {
3125 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3126 + ", " + std::to_string(level)
3127 + ", " + std::to_string(option)
3128 + ", ip_mreq_source{addr=" + std::to_string(value->imr_multiaddr.s_addr)
3129 + ", source=" + std::to_string(value->imr_sourceaddr.s_addr)
3130 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
3131 + ", " + std::to_string(sizeof(value))
3133 } // -x- int __debug_sockopt -x-
3134 int __debug_sockopt(
3135 /// Name of function or method
3136 const std::string& func,
3137 /// The level at which the option resides; typically @c SOL_SOCKET
3139 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3141 /// The structure that this socket option will be set to
3142 const ip_mreqn* value) {
3143 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3144 + ", " + std::to_string(level)
3145 + ", " + std::to_string(option)
3146 + ", ip_mreqn{multiaddr=" + std::to_string(value->imr_multiaddr.s_addr)
3147 + ", address=" + std::to_string(value->imr_address.s_addr) + "}"
3148 + ", ifindex=" + std::to_string(value->imr_ifindex) + "}"
3149 + ", " + std::to_string(sizeof(value))
3151 } // -x- int __debug_sockopt -x-
3152 int __debug_sockopt(
3153 /// Name of function or method
3154 const std::string& func,
3155 /// The level at which the option resides; typically @c SOL_SOCKET
3157 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3159 /// The structure that this socket option will be set to
3160 const icmp6_filter* value) {
3161 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3162 + ", " + std::to_string(level)
3163 + ", " + std::to_string(option)
3164 + ", icmp6_filter{[0]=" + std::to_string(value->icmp6_filt[0])
3165 + ", [1]=" + std::to_string(value->icmp6_filt[1])
3166 + ", [2]=" + std::to_string(value->icmp6_filt[2])
3167 + ", [3]=" + std::to_string(value->icmp6_filt[3])
3168 + ", [4]=" + std::to_string(value->icmp6_filt[4])
3169 + ", [5]=" + std::to_string(value->icmp6_filt[5])
3170 + ", [6]=" + std::to_string(value->icmp6_filt[6])
3171 + ", [7]=" + std::to_string(value->icmp6_filt[7]) + "}"
3172 + ", " + std::to_string(sizeof(value))
3174 } // -x- int __debug_sockopt -x-
3175 int __debug_sockopt(
3176 /// Name of function or method
3177 const std::string& func,
3178 /// The level at which the option resides; typically @c SOL_SOCKET
3180 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3182 /// The structure that this socket option will be set to
3183 const sockaddr_in6* value) {
3184 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3185 + ", " + std::to_string(level)
3186 + ", " + std::to_string(option)
3187 + ", sockaddr_in6{family=" + std::to_string(value->sin6_family)
3188 + ", port=" + std::to_string(value->sin6_port)
3189 + ", flowinfo=" + std::to_string(value->sin6_flowinfo)
3190 + ", addr=" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[0])
3191 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[1])
3192 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[2])
3193 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[3])
3194 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[4])
3195 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[5])
3196 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[6])
3197 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[7])
3198 + ", scope_id=" + std::to_string(value->sin6_scope_id) + "}"
3199 + ", " + std::to_string(sizeof(value))
3201 } // -x- int __debug_sockopt -x-
3202 int __debug_sockopt(
3203 /// Name of function or method
3204 const std::string& func,
3205 /// The level at which the option resides; typically @c SOL_SOCKET
3207 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3209 /// The structure that this socket option will be set to
3210 const ip6_mtuinfo* value) {
3211 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3212 + ", " + std::to_string(level)
3213 + ", " + std::to_string(option)
3214 + ", ip6_mtuinfo{addr=" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[0])
3215 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[1])
3216 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[2])
3217 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[3])
3218 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[4])
3219 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[5])
3220 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[6])
3221 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[7])
3222 + ", mtu=" + std::to_string(value->ip6m_mtu) + "}"
3223 + ", " + std::to_string(sizeof(value))
3225 } // -x- int __debug_sockopt -x-
3226 int __debug_sockopt(
3227 /// Name of function or method
3228 const std::string& func,
3229 /// The level at which the option resides; typically @c SOL_SOCKET
3231 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3233 /// The structure that this socket option will be set to
3234 const ipv6_mreq* value) {
3235 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3236 + ", " + std::to_string(level)
3237 + ", " + std::to_string(option)
3238 + ", ipv6_mreq{addr=" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[0])
3239 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[1])
3240 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[2])
3241 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[3])
3242 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[4])
3243 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[5])
3244 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[6])
3245 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[7])
3246 + ", iface=" + std::to_string(value->ipv6mr_interface) + "}"
3247 + ", " + std::to_string(sizeof(value))
3249 } // -x- int __debug_sockopt -x-
3250 int __debug_sockopt(
3251 /// Name of function or method
3252 const std::string& func,
3253 /// The level at which the option resides; typically @c SOL_SOCKET
3255 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3257 /// The structure that this socket option will be set to
3258 const group_req* value) {
3259 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3260 + ", " + std::to_string(level)
3261 + ", " + std::to_string(option)
3262 + ", group_req{iface=" + std::to_string(value->gr_interface)
3264 + ", " + std::to_string(sizeof(value))
3266 } // -x- int __debug_sockopt -x-
3267 int __debug_sockopt(
3268 /// Name of function or method
3269 const std::string& func,
3270 /// The level at which the option resides; typically @c SOL_SOCKET
3272 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3274 /// The structure that this socket option will be set to
3275 const group_source_req* value) {
3276 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3277 + ", " + std::to_string(level)
3278 + ", " + std::to_string(option)
3279 + ", group_source_req{iface=" + std::to_string(value->gsr_interface)
3280 + ", <group_source_req>}"
3281 + ", " + std::to_string(sizeof(value))
3283 } // -x- int __debug_sockopt -x-
3284 int __debug_sockopt_other(
3285 /// Name of function or method
3286 const std::string& func,
3287 /// The level at which the option resides; typically @c SOL_SOCKET
3289 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3291 /// The structure that this socket option will be set to
3292 const socklen_t size) {
3293 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3294 + ", " + std::to_string(level)
3295 + ", " + std::to_string(option)
3297 + ", " + std::to_string(size)
3299 } // -x- int __debug_sockopt_other -x-
3302 /*======================================================================*//**
3304 Discards the specified number of 8-bit bytes efficiently, and without closing
3305 the stream, and without consuming excessive quantities of memory.
3306 @exception randolf::rex::xERANGE An invalid value was specified for either
3307 the @c nbytes or @c memory_size parameter (e.g., a negative value,
3308 except for -1 with the @c nbytes parameter)
3309 @returns Number of bytes that were successfully discarded
3314 *///=========================================================================
3316 /// Number of bytes to discard@n
3317 /// 0 = use internal @ref buffer_size() @n
3318 /// -1 = all remaining data waiting to be received (in other words, this
3319 /// method will repeatedly consume all data until encountering @ref eos)
3322 /// MSG_PEEK (ignored, to prevent an endless loop)@n
3325 /// MSG_CMSG_CLOEXEC
3326 int posix_flags = 0,
3327 /// Maximum internal read size@n
3328 /// 0 = default to @ref buffer_size (when @c nbytes is larger than this size,
3329 /// multiple reads into this buffer will be utilized behind-the-scenes to
3330 /// consume the quantity of data specified by the @c nbytes parameter)
3331 const size_t memory_size = 0) {
3332 int buf_size = memory_size != 0 ? memory_size : __buffer_size;
3333 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3334 + ", " + std::to_string(nbytes)
3335 + ", " + std::to_string(posix_flags)
3336 + ", " + std::to_string(buf_size)
3339 // --------------------------------------------------------------------------
3341 // --------------------------------------------------------------------------
3342 if (nbytes == 0) nbytes = __buffer_size;
3343 else if (nbytes < -1) throw randolf::rex::xERANGE( "nbytes parameter of " + std::to_string(nbytes) + " is below -1");
3344 if (memory_size < 0) throw randolf::rex::xERANGE("memory_size parameter of " + std::to_string(memory_size) + " is below 0");
3346 // --------------------------------------------------------------------------
3347 // The MSG_PEEK flag is incompatible with this method, so we're turning it
3348 // off to prevent an endless loop. We're providing a debug message for this
3349 // in case someone's trying to track something down where they're using the
3350 // MSG_PEEK flag with a misunderstanding of the purpose of this method.
3351 // --------------------------------------------------------------------------
3352 if (posix_flags & MSG_PEEK) {
3353 posix_flags &= (~MSG_PEEK); // Remove MSG_PEEK flag if it was enabled
3354 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3355 + " ignoring incompatible MSG_PEEK flag"
3357 } // -x- if MSG_PEEK -x-
3359 // --------------------------------------------------------------------------
3360 // Internal variables.
3361 // --------------------------------------------------------------------------
3362 int discarded = 0; // Total number of bytes discarded
3365 // --------------------------------------------------------------------------
3366 // Discard loop. We are intentionally handling nbytes in two different ways
3367 // hear with a little bit more code to benefit from the trade-off of faster
3368 // overall performance.
3369 // --------------------------------------------------------------------------
3372 // --------------------------------------------------------------------------
3373 // Discard all remaining data waiting to be received.
3374 // --------------------------------------------------------------------------
3377 discarded += __recv(buf, buf_size, posix_flags);
3378 } // -x- while n -x-
3380 // --------------------------------------------------------------------------
3381 // Discard specific amount of data waiting to be received.
3382 // --------------------------------------------------------------------------
3385 int inbytes = nbytes;
3386 while (inbytes > 0 && !eos()) {
3387 int n = __recv(buf, std::min(inbytes, buf_size), posix_flags);
3390 } // -x- while nbytes -x-
3391 } // -x- if nbytes -x-
3393 // --------------------------------------------------------------------------
3394 // Ignore exception so that we can simply indicate how many bytes were
3395 // successfully received and discarded.
3396 // --------------------------------------------------------------------------
3397 } catch (const rex::xALL& e) {}
3399 // --------------------------------------------------------------------------
3400 // Erase all data to prevent data from being unexpectedly exposed through any
3401 // other memory allocations later that use some or all of the same memory.
3402 // --------------------------------------------------------------------------
3403 //for (int i = std::min(discarded, buf_size); i > 0; i--) buf[i] = '\0';
3404 memset(buf, 0, std::min(discarded, buf_size));
3407 } // -x- int discard -x-
3409 /*======================================================================*//**
3411 Discards the specified number of 8-bit bytes efficiently, but stops upon
3412 encountering an EoL sequence (which will also be discarded; to find out which
3413 EoL sequence was consumed, use the @ref eol_consumed_seq method which will
3414 also return an empty string if no EoL sequence was consumed), and without
3415 closing the stream, and without consuming excessive quantities of memory.
3417 This method is particularly useful for discarding lines that are too long,
3418 which, for example, with the return value makes it possible to optionally
3419 present a helpful error message to an end user and/or add details to an error
3420 log before continue receiving any subsequent lines.
3422 @exception randolf::rex::xERANGE An invalid value was specified for either
3423 the @c nbytes or @c memory_size parameter (e.g., a negative value,
3424 except for -1 with the @c nbytes parameter)
3425 @returns Number of bytes that were successfully discarded
3430 *///=========================================================================
3432 /// Number of bytes to discard@n
3433 /// 0 = use internal @ref buffer_size() @n
3434 /// -1 = all remaining data waiting to be received (in other words, this
3435 /// method will repeatedly consume all data until encountering an EoL
3436 /// sequence or once @ref eos is reached)
3439 /// MSG_PEEK (ignored, to prevent an endless loop)@n
3442 /// MSG_CMSG_CLOEXEC
3443 int posix_flags = 0,
3444 /// Line timeout (in seconds)@n
3445 /// 0 = no timeout (default), unless it was configured by way of the
3446 /// @ref timeout_recvline(long) method
3448 /// Maximum internal read size@n
3449 /// 0 = default to internal buffer's base buffer size (when @c nbytes is
3450 /// larger than this size, multiple reads into this buffer will be utilized
3451 /// behind-the-scenes to consume @c nbytes quantity of data)
3452 const size_t memory_size = 0) {
3453 int buf_size = memory_size != 0 ? memory_size : __buffer->get_base_size();
3454 if (__debug) debug("discard_line(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3455 + ", " + std::to_string(nbytes)
3456 + ", " + std::to_string(posix_flags)
3457 + ", " + std::to_string(buf_size)
3460 // --------------------------------------------------------------------------
3462 // --------------------------------------------------------------------------
3463 bool infinite = nbytes == -1; // Keep track of whether nbytes was -1 before changing its value
3464 if (nbytes == 0) nbytes = __buffer_size;
3465 else if (nbytes == -1) nbytes = INT_MAX;
3466 else if (nbytes < -1) throw randolf::rex::xERANGE("nbytes parameter of " + std::to_string(nbytes) + " is below -1");
3467//std::cout << "buf_size=" << buf_size << std::endl;
3469 // --------------------------------------------------------------------------
3470 // Syntax checks as part of preparation of timeout variable for faster
3471 // comparisons within line-reading loop.
3472 // --------------------------------------------------------------------------
3473 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
3474 if (timeout == 0) timeout = __recvline_timeout;
3475 timeout = timeout == 0 ? LONG_MAX : timeout + time(0);
3477 // --------------------------------------------------------------------------
3478 // The MSG_PEEK flag is incompatible with this method, so we're turning it
3479 // off to prevent an endless loop. We're providing a debug message for this
3480 // in case someone's trying to track something down where they're using the
3481 // MSG_PEEK flag with a misunderstanding of the purpose of this method.
3482 // --------------------------------------------------------------------------
3483 if (posix_flags & MSG_PEEK) {
3484 posix_flags &= (~MSG_PEEK); // Remove MSG_PEEK flag if it was enabled
3485 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3486 + " ignoring incompatible MSG_PEEK flag"
3488 } // -x- if MSG_PEEK -x-
3490 // --------------------------------------------------------------------------
3491 // Internal variables.
3492 // --------------------------------------------------------------------------
3493 int discarded = 0; // Total number of bytes discarded
3494 int buf_size_eol = buf_size + 1;
3495 char buf[buf_size_eol];
3497// buf.resize(buf_size_eol); // Pre-allocate underlying array to prevent memory corruption when __recv writes directly into it, with +1 for straddled EoL
3499 // --------------------------------------------------------------------------
3500 // Discard loop. We are intentionally handling nbytes in two different ways
3501 // hear with a little bit more code to benefit from the trade-off of faster
3502 // overall performance.
3503 // --------------------------------------------------------------------------
3506 // --------------------------------------------------------------------------
3507 // Discard all remaining data waiting to be received.
3508 // --------------------------------------------------------------------------
3509 int len_with_eol = 0;
3512 while (len_with_eol == 0 && n != 0 && !eos()) {
3513 n = __recv(buf, buf_size_eol, posix_flags | MSG_PEEK);
3515 // --------------------------------------------------------------------------
3516 // Affect an actual discard of the correct amount of data. If "len" is -1,
3517 // then just discard everything because it means that we didn't encounter an
3518 // EoL sequence in this iteration.
3519 // --------------------------------------------------------------------------
3520 int len = eol_index(buf, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
3521//std::cout << "///// n=" << n << " len=" << len << " len_with_eol=" << len_with_eol << " buf_size_eol=" << buf_size_eol << std::endl;
3522 n = __recv(buf, len > 0 ? len_with_eol : n, posix_flags); // Consume data
3523//std::cout << " n=" << n << std::endl;
3525 } // -x- while n -x-
3527 // --------------------------------------------------------------------------
3528 // Discard specific amount of data waiting to be received.
3529 // --------------------------------------------------------------------------
3531 int inbytes = nbytes;
3532 while (len_with_eol == 0 && n != 0 && inbytes > 0 && !eos()) {
3533 n = __recv(buf, std::min(inbytes + 1, buf_size_eol), posix_flags | MSG_PEEK);
3535 // --------------------------------------------------------------------------
3536 // Affect an actual discard of the correct amount of data. If "len" is -1,
3537 // then just discard everything because it means that we didn't encounter an
3538 // EoL sequence in this iteration.
3539 // --------------------------------------------------------------------------
3540 int len = eol_index(buf, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
3541//std::cout << "||||| n=" << n << " len=" << len << " len_with_eol=" << len_with_eol << " buf_size_eol=" << buf_size_eol << std::endl;
3542 n = __recv(buf, len >= 0 ? len_with_eol : n, posix_flags); // Consume data
3543//std::cout << " n=" << n << std::endl;
3546 } // -x- while nbytes -x-
3547 } // -x- if nbytes -x-
3549 // --------------------------------------------------------------------------
3550 // Ignore exception so that we can simply indicate how many bytes were
3551 // successfully received and discarded.
3552 // --------------------------------------------------------------------------
3553 } catch (const rex::xALL& e) {
3554//std::cout << "Exception: " << e.what() << std::endl;
3557//std::cout << "discarded=" << discarded << std::endl;
3559 // --------------------------------------------------------------------------
3560 // Erase all data to prevent data from being unexpectedly leaked to any other
3561 // memory allocations later that use some or all of the same memory.
3563 // Note: The +1 in the buffer size calcuation is to include a straddled EoL.
3564 // --------------------------------------------------------------------------
3565 //for (int i = std::min(discarded, buf_size); i > 0; i--) buf[i] = '\0';
3566 memset(buf, 0, std::min(discarded, buf_size));
3569 } // -x- int discard_line -x-
3571 /*======================================================================*//**
3573 Set EoL (End of Line) sequence. This sequence is used by recvline(),
3574 recv_rline(), sendline(), and related functions, and it defaults to an empty
3575 string which results in the EoL sequence being detected automatically
3578 - @c "" (empty string) = automatically detect on-the-fly (default)
3579 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
3580 - @c \\r (CR) = Carriage Return (typical for MacOS)
3581 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
3584 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
3585 IMAP4, FINGER, NICNAME/WHOIS, etc.).
3586 @returns The same rsocket object so as to facilitate stacking
3597 *///=========================================================================
3599 /// EoL sequence as an ASCIIZ string
3600 const char* eol) noexcept {
3602 __eol_out = __eol.empty() ? __CRLF : __eol;
3604 } // -x- rsocket& eol -x-
3606 /*======================================================================*//**
3608 Set EoL (End of Line) sequence. This sequence is used by recvline(),
3609 recv_rline(), sendline(), and related functions, and it defaults to an empty
3610 string which results in the EoL sequence being detected automatically
3613 - @c "" (empty string) = automatically detect on-the-fly (default)
3614 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
3615 - @c \\r (CR) = Carriage Return (typical for MacOS)
3616 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
3619 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
3620 IMAP4, FINGER, NICNAME/WHOIS, etc.).
3621 @returns The same rsocket object so as to facilitate stacking
3631 *///=========================================================================
3633 /// EoL sequence as an std::string object
3634 const std::string& eol) noexcept {
3637 } // -x- rsocket& eol -x-
3639 /*======================================================================*//**
3641 Configure EoL adoption policy for the @ref recvline() and @ref recv_rline()
3642 methods. By default, @ref rsocket is configured with the EoL adoption policy
3643 enabled alongside an empty @ref eol() sequence, which results in the default
3644 operation being that the EoL sequence automatically gets detected and updated
3645 internally upon the first use of either the @ref recvline() or
3646 @ref recv_rline method.
3648 The EoL adoption policy is only effective when the @ref eol() sequence is not
3649 defined (which is indicated by an empty EoL sequence string).
3651 The EoL sequence is updated only when the EoL sequence string is empty, and
3652 when this EoL adoption policy is enabled.
3653 @returns The same rsocket object so as to facilitate stacking
3657 @see is_eol_adoption
3663 *///=========================================================================
3664 rsocket& eol_adoption(
3665 /// TRUE = enable EoL adoption (default)@n
3666 /// FALSE = disable EoL adoption
3667 const bool flag) noexcept {
3668 __eol_adoption = flag;
3670 } // -x- rsocket& eol_adoption -x-
3672 /*======================================================================*//**
3674 Returns a String containing the EoL character sequence that was consumed by
3675 the most recent successful call to the @ref recvline() or @ref recv_rline
3676 method ("successful" in this context means that the received line was
3677 terminated by a valid EoL character sequence; otherwise the
3678 previous/unmodified value is returned).
3680 This method must not be used to determine whether the @ref recvline() method
3681 successfully consumed an EoL character sequence. The reason for this is
3682 that @ref recvline() doesn't update this string when it doesn't consume an
3683 EoL character sequence, hence interpreting its results can (and likely will)
3684 lead to false positives.
3685 @returns EoL character sequence
3692 *///=========================================================================
3693 std::string eol_consumed_seq() noexcept {
3694 return __eol_consumed_seq;
3695 } // -x- std::string eol_consumed_seq -x-
3697 /*======================================================================*//**
3699 Configure EoL substitution policy for the @ref printf(), @ref printfline(),
3700 @ref vprintf(), and @ref vprintfline() methods. By default, @ref rsocket
3701 substitutes printf's @c \\n sequence with the EoL sequence (if defined), but
3702 this method can be used to disable this behaviour.
3704 The @c \\n sequence used in the printf format string normally coincides with
3705 the local Operating System's newline standard, which is very likely different
3706 from the @ref rsocket endpoint's newline standard, and/or the newline
3707 standard of the protocol being implemented. This policy setting makes it
3708 possible to control whether to use the configured EoL sequence when sending a
3709 formatted string to the endpoint.
3710 @returns The same rsocket object so as to facilitate stacking
3713 @see is_eol_fix_printf
3719 *///=========================================================================
3720 rsocket& eol_fix_printf(
3721 /// TRUE = enable EoL substitution (default)@n
3722 /// FALSE = disable EoL substitution
3723 const bool flag) noexcept {
3724 __eol_fix_printf = flag;
3726 } // -x- rsocket& eol_fix_printf -x-
3728 /*======================================================================*//**
3730 Finds the first instance of the EoL sequence and returns its offset (which is
3731 effectively the same as the size of the text, not including the characters
3732 that the EoL sequence is comprised of).
3735 This method is specialized primarily for internal use by the @ref recvline()
3736 and @ref recv_rline() methods, but is made available here in case there's a
3737 need to check in-memory text using this rsocket's EoL detection policy.
3738 @returns Size of EoL sequence
3739 @returns -1 if EoL sequence wasn't found
3744 *///=========================================================================
3746 /// Buffer that probably contains at least one EoL sequence
3747 const std::string& buffer,
3748 /// Size of string with EoL character sequence included (will be updated by this method)
3749 int* with_eol_size) noexcept {
3751 // --------------------------------------------------------------------------
3752 // An EoL character sequence was specified, so a simple search will suffice.
3753 // --------------------------------------------------------------------------
3754 if (!__eol.empty()) {
3755//std::cout << "!__eol.empty() ------------------------------ __eol_adoption=" << (__eol_adoption ? "yes" : "no") << " eol=" << randolf::rtools::to_hex(__eol) << std::endl;
3756 int pos = buffer.find(__eol);
3758 *with_eol_size = pos + __eol.size(); // Update size of EoL sequence
3759//std::cout << "------0------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3762//std::cout << "------1------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3763 if (!__eol_adoption) return pos; // Return the -1 error if we're not adopting different EoF sequences on-the-fly
3764 } // -x- if !__eol.empty() -x-
3765//std::cout << "__eol.empty() ------------------------------ String is empty" << std::endl;
3767 // --------------------------------------------------------------------------
3768 // Automatic detection of EoL sequence, so a more flexible approach will be
3771 // Search for all four possible EoL sequences (as indicated by an empty EoL
3772 // character sequence string):
3774 // CRLF: Common for text lines with internet protocols such as SMTP, POP3,
3775 // IMAP4, HTTP, FINGER, WHOIS, etc. Also: DOS and OS/2 EoL sequence.
3777 // CR: MacOS EoL character.
3779 // LF: UNIX/Linux EoL character.
3781 // LFCR: Extremely rare, but I've encountered multiple instances where the
3782 // intended CRLF was reversed to LFCR, possibly due to a programming
3783 // error or an artifact of inappropriate/unintended endian conversion.
3784 // --------------------------------------------------------------------------
3785 int pos = buffer.find(__CR); // CR or CRLF
3786//std::cout << "------2------ <CR> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3787 if (pos == (buffer.size() - 1)) { // EoL sequence not found (this handles a specific empty-buffer edge case caused by SSL background chatter that's very rare but causes problems that are nearly impossible to debug when they do occur)
3788 *with_eol_size = pos;// + 1;
3789// *with_eol_size = 1;
3790//std::cout << "------3------ <!CR> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3792 } else if (pos >= 0) { // EoL sequence found
3793 *with_eol_size = pos + (buffer[pos + 1] == __LF ? 2 : 1); // 2=CRLF, 1=CR
3794// *with_eol_size = buffer[pos + 1] == __LF ? 2 : 1; // 2=CRLF, 1=CR
3795 if (__eol_adoption) {
3796 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
3797 __eol.resize(*with_eol_size - pos); // Truncate extra characters
3798 } // -x- if __eol_adoption -x-
3799//std::cout << "------4------ <CR/CRLF-adopted> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3800 return pos; // Either way, we're done
3803 pos = buffer.find(__LF); // LF or LFCR
3804 if (pos == (buffer.size() - 1)) { // EoL sequence not found (this handles a specific empty-buffer edge case caused by SSL background chatter that's very rare but causes problems that are nearly impossible to debug when they do occur)
3805 *with_eol_size = pos;// + 1;
3806// *with_eol_size = 1;
3807//std::cout << "------5------ <LF> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3809 } else if (pos >= 0) { // EoL sequence found
3810 *with_eol_size = pos + (buffer[pos + 1] == __CR ? 2 : 1); // 2=LFCR, 1=LF
3811// *with_eol_size = buffer[pos + 1] == __CR ? 2 : 1; // 2=LFCR, 1=LF
3812 if (__eol_adoption) {
3813 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
3814 __eol.resize(*with_eol_size - pos); // Truncate extra characters
3815 } // -x- if __eol_adoption -x-
3816//std::cout << "------6------ <LF/LFCR-adopted> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3817 return pos; // Either way, we're done
3820//std::cout << "------7------ <LF> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3822 } // -x- int eol_index -x-
3824 /*======================================================================*//**
3826 Find out if the stream is at its end and that this @ref rsocket's internal
3827 buffer (if one had been set up by the @ref recvline() method) is empty. This
3828 doesn't necessarily mean that the stream is closed; but rather that the
3829 endpoint just hasn't sent any more data (yet).
3831 If the stream isn't open, then this method will always return @c true to
3832 implicitly indicate that there's no data pending to be received.
3835 You can optionally specify a timeout with the timeout parameter, which will
3836 cause this method to wait for the specified period of time for pending data.
3838 It's better (more efficient) to use this method instead of the @c MSG_PEEK
3839 flag (with the various @c recv methods) to determine whether any data is
3840 waiting on the stream (e.g., data that's received by the sockets, but not by
3841 any @c recv methods yet) because this method is specialized in handling this
3842 particular condition and responds with an easy-to-use boolean flag.
3845 EoS is an acronym for: End of Stream
3847 @throws randolf::rex::xEBADF The underlying socket is not open
3848 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3849 part of the user address space
3850 @throws randolf::rex::xEINTR Interrupted by a signal
3851 @throws randolf::rex::xENOMEM Insufficient memory
3852 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3853 doesn't refer to a socket
3854 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
3855 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
3856 is a highly improbable chance that a timeout could still occur if the
3857 data is read by another thread before the `recv(..., MSG_PEEK)` call)
3858 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
3861 @returns TRUE = End of Stream (no buffered data, the stream @ref is_closed(),
3862 or there's no pending data, checked in this order)
3863 @returns FALSE = Pending data available
3869 *///=========================================================================
3871 /// Number of milliseconds to wait@n
3872 /// 0 = no waiting (default)
3873 const int timeout = 0) {
3875 // --------------------------------------------------------------------------
3876 // Check internal buffer first, if there is one, and return immediately if
3877 // the buffer is not empty.
3878 // --------------------------------------------------------------------------
3879 if (__buffer != nullptr && !__buffer->empty()) return false;
3881 // --------------------------------------------------------------------------
3882 // If socket is closed, then just indicate EoS to be done with it. No need
3883 // to fall through to timeouts as this is not applicable.
3884 // --------------------------------------------------------------------------
3885 if (!__socket_open) return true;
3887 // --------------------------------------------------------------------------
3888 // Start by checking whether OpenSSL has pending data, and if it doesn't (or
3889 // there's an error, which is also indicated by 0) then fall through to
3890 // checking the underlying socket using ioctl's FIONREAD...
3892 // Use ioctl's FIONREAD to determine if any data is waiting to be read. If
3893 // ioctl returns non-zero, then an error occurred which always equates to
3894 // EoS (otherwise the FIONREAD operation would have been successful).
3895 // --------------------------------------------------------------------------
3896 if (__tls && SSL_has_pending(__tls_fd) == 1) return false;
3897 int n = 0; // Number of bytes waiting (default to 0 in case of ioctl error)
3898 ioctl(__socket_fd, FIONREAD, &n); // Ignore return code because "n" will be zero if there's an error
3899 if (n > 0) return false; // Indicate !EoS because 0 bytes remaining means EoS
3901 // --------------------------------------------------------------------------
3902 // Timeout polling (if a timeout was specified).
3903 // --------------------------------------------------------------------------
3906 poll(POLLIN, timeout); // This is calling internally, and not ::poll (POSIX) directly
3907 return false; // EoS, because the "poll" method was triggered by new data
3908 } catch (const randolf::rex::xETIMEDOUT& e) {
3909 return true; // EoS, because the "poll" method timed out while waiting for new data
3910 } // This is a specialized catch situation -- the ::recv function could still return an ETIMEDOUT error
3911 } // -x- if timeout -x-
3913 return true; // EoS, because new data was not detected
3914 } // -x- bool eos -x-
3916 /*======================================================================*//**
3918 Find out what the specified IP address string's family (@c SO_DOMAIN) is.
3919 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
3920 address is not available on any local interface
3921 @throws randolf::rex::xEAI_NONAME If @c address is an empty string
3922 @returns @c AF_UNSPEC (if family couldn't be determined)
3923 @returns @c AF_INET (IPv4 address)
3924 @returns @c AF_INET6 (IPv6 address)
3925 @returns @c AF_UNIX (UNIX Domain address)
3926 @returns ...or other family as applicable
3927 @see socket_family()
3928 @see socket_protocol()
3931 *///=========================================================================
3933 /// Address, similar to @ref bind() addressing, including non-standard "if="
3934 /// variant that names a network interface
3935 const std::string& address,
3936 /// Preferred family to return first (used only with interface mode where the
3937 /// network interface is specified after the "if=" prefix); the default value
3938 /// of @c AF_UNSPEC will return the first family interface found
3939 const int preferred_family = AF_UNSPEC) {
3941 // --------------------------------------------------------------------------
3942 // Simple checks first.
3943 // --------------------------------------------------------------------------
3944 if (address.size() == 0) randolf::rex::mk_exception("", EAI_NONAME);
3945 if (address.front() == '/') return AF_UNIX;
3947 // --------------------------------------------------------------------------
3948 // if=<interface-name>: Same "interface" option that we support in bind()
3949 // --------------------------------------------------------------------------
3950 if (address.starts_with("if=")) {
3951 //std::cout << "address=" << address.substr(3).data() << std::endl; // Debug
3952 struct ifaddrs* ifaddr; // Chained interface addresses structure
3953 if (::getifaddrs(&ifaddr) == -1) { // Error occurred, so we're done here
3954 freeifaddrs(ifaddr); // Memory management
3955 randolf::rex::mk_exception("getifaddrs() error (prequisite to searching for " + address + ")", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
3956 } // -x- if getifaddrs -x
3958 // --------------------------------------------------------------------------
3959 // Iterate through interface addresses. Each address should have a different
3960 // address family/domain, and is never AF_UNSPEC (this should never happen
3961 // because it wouldn't make sense, but if the network implementation was
3962 // broken and including an AF_UNSPEC family/domain, it would be useless for
3963 // the purposes of calling the ::socket() function so our code will just
3964 // ignore it anyway.
3965 // --------------------------------------------------------------------------
3966 int first_family = AF_UNSPEC; // We're using AF_UNSPEC (0) to indicate that there isn't a family/domain
3967 for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
3968 if (ifa->ifa_addr == nullptr) continue; // No address structure, so this isn't useful to us at all
3969 if (ifa->ifa_addr->sa_family == AF_UNSPEC) continue; // Skip AF_UNSPEC family/domain, as we can't use this
3970 if (address.compare(3, -1, ifa->ifa_name) != 0) continue; // Not the interface name we're looking for
3972 // --------------------------------------------------------------------------
3973 // Return current address's family if preferred_family is not specified (if
3974 // it's set to AF_UNSPEC {0}), or if it matches the current address.
3975 // --------------------------------------------------------------------------
3976 //std::cout << "if=" << ifa->ifa_name << " family=" << ifa->ifa_addr->sa_family << std::endl; // Debug
3977 if (preferred_family == AF_UNSPEC || preferred_family == ifa->ifa_addr->sa_family) {
3978 first_family = ifa->ifa_addr->sa_family; // Arbitrarily re-using first_family variable
3979 //freeifaddrs(ifaddr); // Memory management
3980 //return first_family; // Return current family/domain (re-used first_family variable)
3982 } // -x- if preferred_family -x-
3984 // --------------------------------------------------------------------------
3985 // Keep track of only the first_family/domain that we found; if we can't find
3986 // the preferred_family, then we'll be able to return the first one we found
3987 // once the current loop is exhausted without finding a preferred familiy
3988 // (this is, in effect, a fallback).
3989 // --------------------------------------------------------------------------
3990 if (first_family == AF_UNSPEC && ifa->ifa_addr->sa_family != AF_UNSPEC)
3991 first_family = ifa->ifa_addr->sa_family;
3992 //std::cout << " first_family=" << first_family << std::endl; // Debug
3993 } // -x- for *ifa -x-
3995 // --------------------------------------------------------------------------
3996 // Return first_family/domain (or throw an exception if it wasn't found).
3997 // --------------------------------------------------------------------------
3998 freeifaddrs(ifaddr); // Memory management
3999 if (first_family == AF_UNSPEC) randolf::rex::mk_exception("Interface (" + address.substr(3) + ") doesn't exist" + (preferred_family == AF_UNSPEC ? "" : " or the preferred family/domain (" + std::to_string(preferred_family) + ") is not supported"), EADDRNOTAVAIL);
4000 return first_family;
4002 } // -x- if /^if=/ -x-
4004 // --------------------------------------------------------------------------
4005 // Attempt to detect IPv4 or IPv6 using a simple shortcut with ::inet_pton(),
4006 // which requires some additional memory allocation (that the simple checks
4007 // from earlier don't need).
4008 // --------------------------------------------------------------------------
4009 char buf[sizeof(struct in6_addr)]; // Binary address storage
4010 if (::inet_pton(AF_INET, address.data(), buf) == 1) return AF_INET;
4011 if (::inet_pton(AF_INET6, address.data(), buf) == 1) return AF_INET6;
4013 // --------------------------------------------------------------------------
4014 // Throw xEAI_FAMILY exception because no family/domain was found.
4015 // --------------------------------------------------------------------------
4016 randolf::rex::mk_exception("No family/domain available for: " + address, EAI_FAMILY);
4018 } // -x- int family -x-
4020 /*======================================================================*//**
4022 Find out what this rsocket's default listen backlog is.
4023 @returns The default listen backlog
4027 *///=========================================================================
4028 int get_backlog() noexcept {
4029 return __socket_backlog;
4030 } // -x- int get_backlog -x-
4032 /*======================================================================*//**
4034 Find out what buffer size is used by the various recv() methods.
4035 @returns Buffer size (in bytes)
4036 @see buffer_size(const size_t nbytes)
4037 @see buffer_size_reset
4040 *///=========================================================================
4041 size_t get_buffer_size() noexcept {
4042 return __buffer->get_size();
4043 } // -x- size_t get_buffer_size -x-
4045 /*======================================================================*//**
4047 Find out what the current EoL (End of Line) sequence is set to.
4049 To send an EoL sequence do not use `send(r.eol())` because it may not be
4050 initialized yet and the endpoint you're sending to may seem unresponsive or
4051 other unexpected behaviour may occur.@n
4053 To send an EoL sequence properly, use @ref sendline(); although specifying no
4054 parameters is more efficient than specifying an empty string (@c ""), the
4055 specialized @ref send_eol() method is the most efficient option for sending
4056 an EoL sequence separately.
4058 An empty EoL sequence means that CRLF will be sent by @ref sendline(), and
4059 that @ref recvline(), and @ref recv_rline() will automatically detect from
4060 one of @c CR, @c CRLF, @c LF, and @c LFCR.
4061 @returns Current EoL sequence
4064 @see eol_consumed_seq
4074 *///=========================================================================
4075 std::string get_eol() noexcept {
4077 } // -x- std::string get_eol -x-
4079 /*======================================================================*//**
4081 Find out what this rsocket's name is.
4083 The built-in SNI mechanism will overwrite this data to indicate the hostname
4084 that was specified by the TLS-encrypted endpoint that triggered an internal
4085 SNI callback -- use the @ref name_sni() method to find out which hostname is
4086 actually being used by TLS.
4087 @returns The name of this rsocket (or an empty @c std::string if this rsocket
4088 doesn't have a name)
4092 *///=========================================================================
4093 std::string get_name() noexcept {
4095 } // -x- std::string name -x-
4097 /*======================================================================*//**
4099 Find out what this rsocket's actual TLS SNI hostname is.
4101 This is the exact - or closest-matching (in the case of wildcards) - hostname
4102 associated with an actual TLS certificate that was provided in the configured
4103 @ref rsocket_sni object.
4104 @returns The hostname associated with the TLS certificate selected by SNI
4108 @see tls_sni_has_name
4110 *///=========================================================================
4111 std::string get_name_sni() noexcept {
4113 } // -x- std::string get_name_sni -x-
4115 /*======================================================================*//**
4117 Get underlying socket family/domain constant (SO_DOMAIN).
4118 @returns socket family/domain constant
4122 @see get_socket_fd()
4123 @see get_socket_protocol()
4124 @see get_socket_type()
4126 *///=========================================================================
4127 int get_socket_family() noexcept {
4128 return __socket_addr->ss_family;
4129 } // -x- int get_socket_family -x-
4131 /*======================================================================*//**
4133 Get underlying socket descriptor/handle.
4134 @returns socket descriptor/handle
4135 @returns 0 = socket not yet allocated
4139 @see get_socket_family()
4140 @see get_socket_protocol()
4141 @see get_socket_type()
4143 *///=========================================================================
4144 int get_socket_fd() noexcept {
4146 } // -x- int get_socket_fd -x-
4148 /*======================================================================*//**
4150 Get underlying socket protocol constant (SO_PROTOCOL).
4151 @returns socket protocol constant
4154 @see get_socket_family()
4155 @see get_socket_fd()
4156 @see get_socket_type()
4158 *///=========================================================================
4159 int get_socket_protocol() noexcept {
4160 return __socket_protocol;
4161 } // -x- int get_socket_protocol -x-
4163 /*======================================================================*//**
4165 Get underlying socket type constant (SO_TYPE).
4166 @returns socket type constant
4169 @see get_socket_family()
4170 @see get_socket_fd()
4171 @see get_socket_protocol()
4173 *///=========================================================================
4174 int get_socket_type() noexcept {
4175 return __socket_type;
4176 } // -x- int get_socket_type -x-
4178 /*======================================================================*//**
4180 Obtain the receive (read) or send (write/transmit) timeout is set to on the
4183 Since getting the read timeout is such a common operation, this specialized
4184 method was created to ease software development efforts; internally we're
4185 just calling getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO).
4187 @throws randolf::rex::xEBADF The underlying socket is not open
4188 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4189 part of the user address space
4190 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
4191 valid for this socket's family (a.k.a., communication domain)
4192 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
4194 @throws randolf::rex::xENOTDIR If the @c direction parameter is supplied with
4195 an incorrect value (not @c SO_RCVTIMEO or @c SO_SNDTIMEO); this
4196 exception is normally a file-system related error, so we're using it
4197 here instead of EINVAL to make detecting this problem simpler for
4198 software developers (plus, "DIR" relates well to "direction"); this
4199 exception shouldn't need to be caught in the vast majority of uses,
4200 although one use where it should be caught is if the end-user has
4201 free reign to set the @c direction parameter to any value (e.g., a
4202 customizable software debugging interface)
4203 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4204 doesn't refer to a socket
4206 @returns @c timeval socket option structure wrapped in std::unique_ptr
4208 @see timeout_recvline
4212 *///=========================================================================
4213 std::unique_ptr<timeval> get_timeout(
4215 /// @c SO_RCVTIMEO (default)@n
4217 const int direction = SO_RCVTIMEO) {
4219 // --------------------------------------------------------------------------
4220 // Get timeout for the specified direction.
4221 // --------------------------------------------------------------------------
4222 if (direction == SO_RCVTIMEO || direction == SO_SNDTIMEO) {
4223 return getsockopt_timeval(SOL_SOCKET, direction); // TODO: Does this need to be wrapped up in std::shared_ptr?
4224 } // -x- if direction -x-
4226 // --------------------------------------------------------------------------
4227 // Throw exception because direction is invalid.
4228 // --------------------------------------------------------------------------
4229 throw randolf::rex::xENOTDIR("invalid timeout direction (must be SO_RCVTIMEO or SO_SNDTIMEO)");
4231 } // -x- std::unique_ptr<timeval> get_timeout -x-
4233 /*======================================================================*//**
4235 Find out what the read timeout is set to when using the @ref recvline()
4238 @returns @c long value (0 = no timeout)
4243 @see timeout_recvline(long)
4245 *///=========================================================================
4246 long get_timeout_recvline() {
4247 return __recvline_timeout;
4248 } // -x- long get_timeout_recvline -x-
4250 /*======================================================================*//**
4252 Get peer name returns the address of the socket as a sockaddr_storage
4255 The resulting structure is wrapped in std::unique_ptr (a C++ smart pointer
4256 that aids in the prevention of resource leaks).
4258 @throws randolf::rex::xEBADF The underlying socket is not open
4259 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4260 part of the user address space
4261 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
4262 valid for this socket's family (a.k.a., communication domain)
4263 @throws randolf::rex::xENOBUFS Insufficient memory
4264 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4265 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4266 doesn't refer to a socket
4268 @returns sockaddr_storage structure
4271 *///=========================================================================
4272 std::unique_ptr<sockaddr_storage> getpeername() {
4273 std::unique_ptr sa = std::make_unique_for_overwrite<sockaddr_storage>();
4274 socklen_t len = sizeof(sockaddr_storage);
4275 __rc_check(::getpeername(__socket_fd, (struct sockaddr*)sa.get(), &len));
4277 } // -x- std::unique_ptr<sockaddr_storage> getpeername -x-
4279 /*======================================================================*//**
4281 Get peer name returns the address of the socket as a std::string object.
4283 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
4284 (e.g., because the @c family doesn't utilize or support an address
4285 {or the format isn't known}
4286 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
4287 @throws randolf::rex::xEBADF The underlying socket is not open
4288 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4289 part of the user address space
4290 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
4291 valid for this socket's family (a.k.a., communication domain)
4292 @throws randolf::rex::xENOBUFS Insufficient memory
4293 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
4294 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4295 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4296 doesn't refer to a socket
4298 @returns string representation of peer name
4300 *///=========================================================================
4301 std::string getpeername_ntop() {
4302 sockaddr_storage sa;
4303 socklen_t len = sizeof(sockaddr_storage);
4304 __rc_check(::getpeername(__socket_fd, (sockaddr*)&sa, &len));
4305 return inet_ntop(&sa);
4306 } // -x- std::string getpeername_ntop -x-
4308 /*======================================================================*//**
4310 Get specified "sockaddr_storage" structure's address as a "sockaddr"
4311 structure, for sockets in one of the supported families:
4315 - AF_UNIX (Domain socket path)
4316 - AF_PACKET (Ethernet node/mac. address)
4318 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
4319 (e.g., because the @c family doesn't utilize or support an address
4320 {or the format isn't known}
4322 @returns pointer to sockaddr structure within provided sockaddr_storage
4324 @see mk_sockaddr_storage
4330 *///=========================================================================
4331 static sockaddr* getsockaddr(
4332 /// The @c sockaddr_storage structure wherein @c sockaddr will be referenced from
4333 const sockaddr_storage* sa) {
4334 switch (sa->ss_family) {
4335 case AF_INET: // IPv4 address
4336 return (sockaddr*)&(((struct sockaddr_in*)sa)->sin_addr);//->sin_addr);
4337 case AF_INET6: // IPv6 address
4338 return (sockaddr*)&(((struct sockaddr_in6*)sa)->sin6_addr);//->sin6_addr);
4339 case AF_UNIX: // UNIX (path) domain socket
4340 return (sockaddr*)&(((struct sockaddr_un*)sa)->sun_path);//->sun_path);
4341 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
4342 // case /*AF_LINK* /18: // Link layer interface (arp)
4344 case AF_PACKET: // Packet (ethernet) address (packet capturing)
4345 return (sockaddr*)&(((struct sockaddr_ll*)sa)->sll_addr);//->sll_addr);
4346 } // -x- switch family -x-
4347 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY"); // Everything else
4348 } // -x- sockaddr* getsockaddr -x-
4350 /*======================================================================*//**
4352 Get socket name returns the address of the socket as a "sockaddr_storage"
4355 The resulting structure is wrapped in std::unique_ptr (a C++ smart pointer
4356 that aids in the prevention of resource leaks).
4358 @throws randolf::rex::xEBADF The underlying socket is not open
4359 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4360 part of the user address space
4361 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
4362 valid for this socket's family (a.k.a., communication domain)
4363 @throws randolf::rex::xENOBUFS Insufficient memory
4364 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4365 doesn't refer to a socket
4367 @returns sockaddr_storage structure
4370 *///=========================================================================
4371 std::unique_ptr<sockaddr_storage> getsockname() {
4372 std::unique_ptr sa = std::make_unique_for_overwrite<sockaddr_storage>();
4373 socklen_t len = sizeof(sockaddr_storage);
4374 __rc_check(::getsockname(__socket_fd, (struct sockaddr*)sa.get(), &len));
4376 } // -x- std::unique_ptr<sockaddr_storage> getsockname -x-
4378 /*======================================================================*//**
4380 Get socket name returns the name of the socket as a std::string object.
4382 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
4383 (e.g., because the @c family doesn't utilize or support an address
4384 {or the format isn't known}
4385 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
4386 @throws randolf::rex::xEBADF The underlying socket is not open
4387 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4388 part of the user address space
4389 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
4390 valid for this socket's family (a.k.a., communication domain)
4391 @throws randolf::rex::xENOBUFS Insufficient memory
4392 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
4393 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4394 doesn't refer to a socket
4396 @returns string representation of socket name
4398 *///=========================================================================
4399 std::string getsockname_ntop() {
4400 sockaddr_storage sa;
4401 socklen_t len = sizeof(sockaddr_storage);
4402 __rc_check(::getsockname(__socket_fd, (sockaddr*)&sa, &len));
4403 return inet_ntop(&sa);
4404 } // -x- std::string getsockname_ntop -x-
4406 /*======================================================================*//**
4408 Get socket option details in the form of an integer.
4410 Most options return an integer, with the remaining options returning a
4411 pointer to a structure wrapped in a std::unique_ptr (a C++ smart pointer that
4412 aids in the prevention of resource leaks); the primitive types int, u_int,
4413 and u_char are not wrapped in C++ smart pointers because returning them by
4414 value is more efficient since allocating memory for an entire structure isn't
4418 It is up to the developer to know which return type is needed according to
4419 the socket option, otherwise an exception will likely be thrown -- in some
4420 cases where the wrong type will seem to work, this is due to the wrong type
4421 providing a minimally sufficient amount of memory for the storage of the
4422 resulting structure.
4425 The returned values/structures are not marked as "const" because they may
4426 need to be modified for unforseen purposes. Modifying the returend values or
4427 structures is fine because they are intended to be independent and are
4428 expected to have no direct impact on the rsocket's internal variables and
4431 Templates in C++ aren't used here because they don't work properly for our
4432 needs due to neccesity to handle both fundamental types and structures; it
4433 turns out that mixing these is impossible when using the same function name,
4434 so this just doesn't work as well as we'd like it to. (We may try to work on
4435 this again in the future as time permits to provide an additional method for
4436 obtaining socket options, but with the intention of never removing this
4437 current set of methods so as to ensure backward compatibility in the future.)
4439 @throws randolf::rex::xEBADF The underlying socket is not open
4440 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4441 part of the user address space
4442 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
4443 valid for this socket's family (a.k.a., communication domain)
4444 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
4446 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4447 doesn't refer to a socket
4449 @returns socket option value
4450 @see setsockopt(const int, const int)
4451 @see setsockopt(const int, const int, const int)
4453 *///=========================================================================
4455 /// The level at which the option resides; typically @c SOL_SOCKET
4457 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4460 socklen_t slt{sizeof(value)};
4461 __rc_check(::getsockopt(__socket_fd, level, option, &value, &slt));
4462 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
4464 } // -x- int getsockopt_int -x-
4466 /*======================================================================*//**
4468 Get socket option details in the form of an unsigned integer.
4469 @copydetails getsockopt_int(const int, const int)
4470 @returns socket option value
4471 @see setsockopt(const int, const int, const u_int)
4473 *///=========================================================================
4474 u_int getsockopt_u_int(
4475 /// The level at which the option resides; typically @c SOL_SOCKET
4477 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4480 socklen_t slt{sizeof(value)};
4481 __rc_check(::getsockopt(__socket_fd, level, option, &value, &slt));
4482 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
4484 } // -x- u_int getsockopt_u_int -x-
4486 /*======================================================================*//**
4488 Get socket option details in the form of an unsigned character.
4489 @copydetails getsockopt_int(const int, const int)
4490 @returns socket option value
4491 @see setsockopt(const int, const int, const u_char)
4493 *///=========================================================================
4494 u_char getsockopt_u_char(
4495 /// The level at which the option resides; typically @c SOL_SOCKET
4497 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4500 socklen_t slt{sizeof(value)};
4501 __rc_check(::getsockopt(__socket_fd, level, option, &value, &slt));
4502 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
4504 } // -x- u_char getsockopt_u_char -x-
4506 /*======================================================================*//**
4508 Get socket option details in the form of a structure.
4509 @copydetails getsockopt_int(const int, const int);
4510 @returns socket option structure wrapped in std::unique_ptr
4511 @see setsockopt(const int, const int, const linger&)
4513 *///=========================================================================
4514 std::unique_ptr<linger> getsockopt_linger(
4515 /// The level at which the option resides; typically @c SOL_SOCKET
4517 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4519 std::unique_ptr value = std::make_unique_for_overwrite<linger>();
4520 socklen_t slt{sizeof(value)};
4521 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4522 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4524 } // -x- std::unique_ptr<linger> getsockopt_linger -x-
4526 /*======================================================================*//**
4527 @copydoc getsockopt_linger(const int, const int)
4528 @see setsockopt(const int, const int, const timeval&)
4530 *///=========================================================================
4531 std::unique_ptr<timeval> getsockopt_timeval(
4532 /// The level at which the option resides; typically @c SOL_SOCKET
4534 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4536 std::unique_ptr value = std::make_unique_for_overwrite<timeval>();
4537 socklen_t slt{sizeof(value)};
4538 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4539 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4541 } // -x- std::unique_ptr<timeval> getsockopt_timeval -x-
4543 /*======================================================================*//**
4544 @copydoc getsockopt_linger(const int, const int)
4545 @see setsockopt(const int, const int, const in_addr&)
4547 *///=========================================================================
4548 std::unique_ptr<in_addr> getsockopt_in_addr(
4549 /// The level at which the option resides; typically @c SOL_SOCKET
4551 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4553 std::unique_ptr value = std::make_unique_for_overwrite<in_addr>();
4554 socklen_t slt{sizeof(value)};
4555 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4556 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4558 } // -x- std::unique_ptr<in_addr> getsockopt_in_addr -x-
4560 /*======================================================================*//**
4561 @copydoc getsockopt_linger(const int, const int)
4562 @see setsockopt(const int, const int, const ip_mreq&)
4564 *///=========================================================================
4565 std::unique_ptr<ip_mreq> getsockopt_ip_mreq(
4566 /// The level at which the option resides; typically @c SOL_SOCKET
4568 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4570 std::unique_ptr value = std::make_unique_for_overwrite<ip_mreq>();
4571 socklen_t slt{sizeof(value)};
4572 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4573 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4575 } // -x- std::unique_ptr<ip_mreq> getsockopt_ip_mreq -x-
4577 /*======================================================================*//**
4578 @copydoc getsockopt_linger(const int, const int)
4579 @see setsockopt(const int, const int, const ip_mreq_source&)
4581 *///=========================================================================
4582 std::unique_ptr<ip_mreq_source> getsockopt_ip_mreq_source(
4583 /// The level at which the option resides; typically @c SOL_SOCKET
4585 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4587 std::unique_ptr value = std::make_unique_for_overwrite<ip_mreq_source>();
4588 socklen_t slt{sizeof(value)};
4589 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4590 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4592 } // -x- std::unique_ptr<ip_mreq_source> getsockopt_ip_mreq_source -x-
4594 /*======================================================================*//**
4595 @copydoc getsockopt_linger(const int, const int)
4596 @see setsockopt(const int, const int, const ip_mreqn&)
4598 *///=========================================================================
4599 std::unique_ptr<ip_mreqn> getsockopt_ip_mreqn(
4600 /// The level at which the option resides; typically @c SOL_SOCKET
4602 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4604 std::unique_ptr value = std::make_unique_for_overwrite<ip_mreqn>();
4605 socklen_t slt{sizeof(value)};
4606 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4607 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4609 } // -x- std::unique_ptr<ip_mreqn> getsockopt_ip_mreqn -x-
4611 /*======================================================================*//**
4612 @copydoc getsockopt_linger(const int, const int)
4613 @see setsockopt(const int, const int, const icmp6_filter&)
4615 *///=========================================================================
4616 std::unique_ptr<icmp6_filter> getsockopt_icmp6_filter(
4617 /// The level at which the option resides; typically @c SOL_SOCKET
4619 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4621 std::unique_ptr value = std::make_unique_for_overwrite<icmp6_filter>();
4622 socklen_t slt{sizeof(value)};
4623 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4624 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4626 } // -x- std::unique_ptr<icmp6_filter> getsockopt_icmp6_filter -x-
4628 /*======================================================================*//**
4629 @copydoc getsockopt_linger(const int, const int)
4630 @see setsockopt(const int, const int, const sockaddr_in6&)
4632 *///=========================================================================
4633 std::unique_ptr<sockaddr_in6> getsockopt_sockaddr_in6(
4634 /// The level at which the option resides; typically @c SOL_SOCKET
4636 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4638 std::unique_ptr value = std::make_unique_for_overwrite<sockaddr_in6>();
4639 socklen_t slt{sizeof(value)};
4640 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4641 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4643 } // -x- std::unique_ptr<sockaddr_in6> getsockopt_sockaddr_in6 -x-
4645 /*======================================================================*//**
4646 @copydoc getsockopt_linger(const int, const int)
4647 @see setsockopt(const int, const int, const ip6_mtuinfo&)
4649 *///=========================================================================
4650 std::unique_ptr<ip6_mtuinfo> getsockopt_ip6_mtuinfo(
4651 /// The level at which the option resides; typically @c SOL_SOCKET
4653 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4655 std::unique_ptr value = std::make_unique_for_overwrite<ip6_mtuinfo>();
4656 socklen_t slt{sizeof(value)};
4657 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4658 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4660 } // -x- std::unique_ptr<ip6_mtuinfo> getsockopt_ip6_mtuinfo -x-
4662 /*======================================================================*//**
4663 @copydoc getsockopt_linger(const int, const int)
4664 @see setsockopt(const int, const int, const ipv6_mreq&)
4666 *///=========================================================================
4667 std::unique_ptr<ipv6_mreq> getsockopt_ipv6_mreq(
4668 /// The level at which the option resides; typically @c SOL_SOCKET
4670 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4672 std::unique_ptr value = std::make_unique_for_overwrite<ipv6_mreq>();
4673 socklen_t slt{sizeof(value)};
4674 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4675 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4677 } // -x- std::unique_ptr<ipv6_mreq> getsockopt_ipv6_mreq -x-
4679 /*======================================================================*//**
4680 @copydoc getsockopt_linger(const int, const int)
4681 @see setsockopt(const int, const int, const group_req&)
4683 *///=========================================================================
4684 std::unique_ptr<group_req> getsockopt_group_req(
4685 /// The level at which the option resides; typically @c SOL_SOCKET
4687 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4689 std::unique_ptr value = std::make_unique_for_overwrite<group_req>();
4690 socklen_t slt{sizeof(value)};
4691 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4692 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4694 } // -x- std::unique_ptr<group_req> getsockopt_group_req -x-
4696 /*======================================================================*//**
4697 @copydoc getsockopt_linger(const int, const int)
4698 @see setsockopt(const int, const int, const group_source_req&)
4700 *///=========================================================================
4701 std::unique_ptr<group_source_req> getsockopt_group_source_req(
4702 /// The level at which the option resides; typically @c SOL_SOCKET
4704 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4706 std::unique_ptr value = std::make_unique_for_overwrite<group_source_req>();
4707 socklen_t slt{sizeof(value)};
4708 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4709 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4711 } // -x- std::unique_ptr<group_source_req> getsockopt_group_source_req -x-
4713 /*======================================================================*//**
4714 @copydoc getsockopt_linger(const int, const int)
4715 @see setsockopt(const int, const int)
4716 @see setsockopt(const int, const int, const int)
4718 *///=========================================================================
4719 template<class T> std::unique_ptr<T> getsockopt_other(
4720 /// The level at which the option resides; typically @c SOL_SOCKET
4722 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4724 std::unique_ptr value = std::make_unique_for_overwrite<T>();
4725 socklen_t slt{sizeof(value)};
4726 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4727 if (__debug) __debug_sockopt_other("getsockopt_other(socket{0x", level, option, socklen_t(sizeof(value)));
4729 } // -x- std::unique_ptr<T> getsockopt_other -x-
4731 /*======================================================================*//**
4733 Get underlying socket's address as an @cstd::string object, for sockets in
4734 one of the supported families:
4738 - AF_UNIX (Domain socket path)
4739 - AF_PACKET (Ethernet node/mac. address)
4741 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
4742 (e.g., because the @c family doesn't utilize or support an address
4743 {or the format isn't known}
4744 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
4745 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
4747 @returns string representation of underlying socket's address
4748 @see inet_ntop(sockaddr_storage*) static method
4751 *///=========================================================================
4752 std::string inet_ntop() {
4753 return inet_ntop((sockaddr_storage*)__socket_addr);
4754 } // -x- std::string inet_ntop -x-
4756 /*======================================================================*//**
4758 Get specified "sockaddr_storage" structure's address as a std::string, for
4759 sockets in one of the supported families:
4763 - AF_UNIX (Domain socket path)
4764 - AF_PACKET (Ethernet node/mac. address)
4766 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
4767 (e.g., because the @c family doesn't utilize or support an address
4768 {or the format isn't known}
4769 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
4770 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
4772 @returns string representation of underlying socket's address
4773 @see inet_ntop() non-static method
4776 *///=========================================================================
4777// static const std::string inet_ntop(
4778 static std::string inet_ntop(
4779 /// Source structure that [should] contain address data
4780 sockaddr_storage* sa) {
4782 switch (sa->ss_family) {
4783 case AF_INET: // IPv4 address
4785 char ntop[sizeof(sockaddr_in)]{0};
4786 const char* rc = ::inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), ntop, sizeof(ntop));
4787// TODO: if (rc == nullptr) _rc...
4791 case AF_INET6: // IPv6 address
4793 char ntop[sizeof(sockaddr_in6)]{0};
4794 const char* rc = ::inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), ntop, sizeof(ntop));
4795// TODO: if (rc == nullptr) _rc...
4799 case AF_UNIX: // UNIX (path) domain socket
4800 str.assign(((struct sockaddr_un *)sa)->sun_path);
4802 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
4803 // case /*AF_LINK* /18: // Link layer interface (arp)
4805 case AF_PACKET: // Packet (ethernet) address (packet capturing)
4806 str.assign(to_mac(((struct sockaddr_ll *)sa)->sll_addr).get());
4808 default: // Everything else
4809 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY");
4810 } // -x- switch family -x-
4812 } // -x- std::string inet_ntop -x-
4814 /*======================================================================*//**
4816 Find out whether an internal read buffer was allocated (this is most likely
4817 triggered by an attempt to read a line of text).
4819 The buffer_size() methods report on how much memory was allocated for the
4820 internal read buffer or to set its size (in bytes).
4821 @returns TRUE = an internal read buffer was allocated
4822 @returns FALSE = an internal read buffer was not allocated
4824 @see buffer_size(const size_t nbytes)
4826 *///=========================================================================
4827 bool is_buffered() noexcept {
4828 return __buffer != nullptr;
4829 } // -x- bool is_buffered -x-
4831 /*======================================================================*//**
4833 Find out whether the underlying socket is not open (which may not be the same
4834 as specifically "closed" since a newly instantiated empty socket begins in a
4835 "not open" state despite the underlying socket not explicitly having been
4837 @returns TRUE = not open
4838 @returns FALSE = open
4842 *///=========================================================================
4843 bool is_closed() noexcept {
4844 return !__socket_open;
4845 } // -x- bool is_closed -x-
4847 /*======================================================================*//**
4849 Find out whether the underlying socket is connected with/to an endpoint.
4850 @returns TRUE = open
4851 @returns FALSE = not open
4856 *///=========================================================================
4857 bool is_connected() noexcept {
4858 return __socket_connected;
4859 } // -x- bool is_connected -x-
4861 /*======================================================================*//**
4863 Find out whether the default byte order for this host is LSB (small endian).
4865 If you're trying to choose which endian type to use when designing a new
4866 internet protocol, then big endian is normally the better option. However,
4867 if your new protocol will only be used by hardware that all share the same
4868 endianness, then that endianness is probably the more optimal option since it
4869 will translate to an overall lesser consumption of CPU cycles by reducing or
4870 eliminating endianness conversions.
4871 @returns TRUE = LSB (little endian)
4872 @returns FALSE = MSB (big endian / network byte order)
4875 *///=========================================================================
4876 bool is_endian_lsb() noexcept {
4877 return !__endian_is_msb;
4878 } // -x- bool is_endian_lsb -x-
4880 /*======================================================================*//**
4882 Find out whether the default byte order for this host is MSB (big endian).
4884 Big endian is the standard known as "network byte order" that's also used in
4885 various header fields in internet packets.
4887 If you're trying to choose which endian type to use when designing a new
4888 internet protocol, then big endian is normally the better option. However,
4889 if your new protocol will only be used by hardware that all share the same
4890 endianness, then that endianness is probably the more optimal option since it
4891 will translate to an overall lesser consumption of CPU cycles by reducing or
4892 eliminating endianness conversions.
4893 @returns TRUE = MSB (big endian / network byte order)
4894 @returns FALSE = LSB (little endian)
4897 *///=========================================================================
4898 bool is_endian_msb() noexcept {
4899 return __endian_is_msb;
4900 } // -x- bool is_endian_msb -x-
4902 /*======================================================================*//**
4904 Find out if the EoL adoption policy is enabled for the @ref recvline() method
4905 (see the @ref eol_adoption method to find out how the dynamically-detected
4906 EoL sequence gets adopted, and under what conditions).
4907 @returns TRUE = EoL adoption is enabled
4908 @returns FALSE = EoL adoption is disabled
4916 *///=========================================================================
4917 bool is_eol_adoption() noexcept {
4918 return __eol_adoption;
4919 } // -x- bool is_eol_adoption -x-
4921 /*======================================================================*//**
4923 Find out if the EoL substitution policy is enabled for the @ref printf(),
4924 @ref printfline(), @ref vprintf(), and @ref vprintfline() methods.
4925 @returns TRUE = EoL substitution is enabled
4926 @returns FALSE = EoL substitution is disabled
4936 *///=========================================================================
4937 bool is_eol_fix_printf() noexcept {
4938 return __eol_fix_printf;
4939 } // -x- bool is_eol_fix_printf -x-
4941 /*======================================================================*//**
4943 Find out whether the @c IPPROTO_MPTCP protocol will be replaced automatically
4944 with @c IPPROTO_TCP internally when using certain POSIX functions that are
4945 known to not support MPTCP.
4946 @returns TRUE = substitution will occur (only for @c IPPROTO_MPTCP)
4947 @returns FALSE = no subsitution will occur
4952 *///=========================================================================
4953 bool is_mptcp() noexcept {
4954 return __socket_mptcp_flag;
4955 } // -x- bool is_mptcp -x-
4957 /*======================================================================*//**
4959 Find out whether the underlying socket is open.
4960 @returns TRUE = open
4961 @returns FALSE = not open
4964 *///=========================================================================
4965 bool is_open() noexcept {
4966 return __socket_open;
4967 } // -x- bool is_open -x-
4969 /*======================================================================*//**
4971 Find out whether encrypted communications is enabled or disabled.
4972 @returns TRUE = encrypted communications is enabled
4973 @returns FALSE = encrypted communications is disabled
4974 @see tls(bool, TLS_FLAGS)
4977 *///=========================================================================
4978 bool is_tls() noexcept {
4980 } // -x- bool is_tls -x-
4982 /*======================================================================*//**
4984 Find out whether TLS context is in TLS_CLIENT mode.
4985 @returns TRUE = TLS context is in TLS_CLIENT mode
4986 @returns FALSE = TLS context is in TLS_SERVER mode
4990 *///=========================================================================
4991 bool is_tls_client_mode() noexcept {
4992 return !__tls_server_mode;
4993 } // -x- bool is_tls_client_mode -x-
4995 /*======================================================================*//**
4997 Find out whether egress from encryption (to unencrypted mode) is allowed.
4998 @returns TRUE = egress from encrypted communications is allowed
4999 @returns FALSE = egress from encrypted communications is not allowed
5003 *///=========================================================================
5004 bool is_tls_egress_okay() noexcept {
5005 return __tls_egress;
5006 } // -x- bool is_tls_egress_okay -x-
5008 /*======================================================================*//**
5010 Find out whether encrypted communications is exclusive.
5011 @returns TRUE = encrypted communications is exclusive
5012 @returns FALSE = encrypted communications is not exclusive
5015 *///=========================================================================
5016 bool is_tls_exclusive() noexcept {
5017 return __tls_exclusive;
5018 } // -x- bool is_tls_exclusive -x-
5020 /*======================================================================*//**
5022 Find out whether ingress to encryption (from unencrypted mode) is allowed.
5023 @returns TRUE = ingress to encrypted communications is allowed
5024 @returns FALSE = ingress to encrypted communications is not allowed
5028 *///=========================================================================
5029 bool is_tls_ingress_okay() noexcept {
5030 return __tls_ingress;
5031 } // -x- bool is_tls_ingress_okay -x-
5033 /*======================================================================*//**
5035 Find out whether read-ahead mode will automatically be enabled internally by
5036 the @ref tls and @ref tls_ctx methods.
5037 @returns TRUE = read-ahead mode was/will-be enabled (default)
5038 @returns FALSE = read-ahead mode wasn't/will-not-be enabled
5039 @see TLS_NO_READ_AHEAD
5043 *///=========================================================================
5044 bool is_tls_read_ahead_enabled() noexcept {
5045 return !__tls_no_read_ahead;
5046 } // -x- bool is_tls_read_ahead_enabled -x-
5048 /*======================================================================*//**
5050 Find out whether TLS context is in TLS_SERVER mode.
5051 @returns TRUE = TLS context is in TLS_SERVER mode
5052 @returns FALSE = TLS context is in TLS_CLIENT mode
5056 *///=========================================================================
5057 bool is_tls_server_mode() noexcept {
5058 return __tls_server_mode;
5059 } // -x- bool is_tls_server_mode -x-
5061 /*======================================================================*//**
5063 Find out whether SNI (Server Name Identifier) is enabled (configured, which
5064 implies that an internal callback function was also set up).
5065 @returns TRUE = SNI is enabled
5066 @returns FALSE = SNI is disabled
5070 *///=========================================================================
5071 bool is_tls_sni() noexcept {
5072 return __tls_sni != nullptr;
5073 } // -x- bool is_tls_sni -x-
5075 /*======================================================================*//**
5077 Find out whether SNI (Server Name Identifier) received an empty hostname or
5078 if no hostname was provided.
5079 @returns TRUE = SNI hostname was received and it was not empty
5080 @returns FALSE = SNI hostname was not received or it was empty
5081 @see is_tls_sni_match()
5084 *///=========================================================================
5085 bool is_tls_sni_has_name() noexcept {
5086 return __tls_sni_has_name;
5087 } // -x- bool is_tls_sni_has_name -x-
5089 /*======================================================================*//**
5091 Find out whether SNI (Server Name Identifier) was matched, which means that
5092 we're using one of the supplementary TLS certificates that are included in
5093 the associated @ref rsocket_sni object as separate TLS contexts.
5095 When this method returns @c TRUE, it means the @c OpenSSL callback (which
5096 occurs during the TLS handshake process) completed the TLS handshake with one
5097 of the TLS certificates that's included in this @c rsocket's @ref rsocket_sni
5098 object instead of the primary TLS certificate assigned to this @c rsocket,
5099 and this @c rsocket is using the respective TLS context instead of the
5100 primary (default) one.
5101 @returns TRUE = SNI was matched
5102 @returns FALSE = SNI wasn't matched
5105 @see tls_sni_has_name
5107 *///=========================================================================
5108 bool is_tls_sni_match() noexcept {
5109 return __tls_sni_match;
5110 } // -x- bool is_tls_sni_match -x-
5112 /*======================================================================*//**
5114 Enable listening mode for this rsocket to prepare it to accept() new inbound
5117 The backlog defaults to SOMAXCONN (4096 on Linux, and 128 on older systems),
5118 which is common on most systems. If a higher value is supplied that exceeds
5119 @c kern.somaxconn, the kernel will automatically override the backlog with
5120 the value stored in @c kern.somaxconn without generating any errors or
5123 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
5124 @throws randolf::rex::xEADDRINUSE No ephemeral ports are available for
5125 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5126 but it really isn't)
5127 @throws randolf::rex::xEBADF The underlying socket is not open
5128 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5129 doesn't refer to a socket
5130 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5131 @returns The same rsocket object so as to facilitate stacking
5140 *///=========================================================================
5142 /// Backlog queue size (0 = uses rsocket's default; see @ref backlog for more
5143 /// details about this); specifying a non-zero backlog also updates rocket's
5144 /// internal default (SOMAXCONN is 4096 on Linux, and 128 on older systems)
5146 if (__debug) debug("listen(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5147 + ", " + std::to_string(backlog)
5150 // --------------------------------------------------------------------------
5151 // Use rsocket's default backlog if not specified, otherwise update it
5152 // with the new value specified here.
5153 // --------------------------------------------------------------------------
5154 if (backlog = 0) backlog = __socket_backlog;
5155 else __socket_backlog = backlog;
5157 // --------------------------------------------------------------------------
5158 // Configure underlying socket to queue up to "backlog" inbound connections.
5159 // --------------------------------------------------------------------------
5160 __rc_check(::listen(__socket_fd, backlog));
5163 } // -x- rsocket& listen -x-
5165 /*======================================================================*//**
5167 Convert an IPv4 address, IPv6 address, ethernet packet, or UNIX domain
5168 socket to a sockaddr_storage structure.
5170 If service_name is an absolute path (that begins with a "/" charcter) and the
5171 family is set to AF_UNSPEC (the default), then the resulting family will be
5175 This method utilizes the results of getaddrinfo().
5177 Other families like AF_LINK and AF_PACKET should work, but haven't been
5178 tested thoroughly. The additional support we provide for IPv4 and IPv6
5179 addresses is to copy the port number into the resulting structure (as a
5183 The resulting sockaddr_storage structure is wrapped in std::unique_ptr (a C++
5184 smart pointer that aids in the prevention of resource leaks).
5187 This method is thread-safe.
5189 @throws randolf::rex::xEAI_ADDRFAMILY If specified network host doesn't have
5190 any addresses in the specified address family
5191 @throws randolf::rex::xEAI_AGAIN Temporary failure code from DNS server (try
5193 @throws randolf::rex::xEAI_BADFLAGS Invalid flags in @c hints.ai_flags (or
5194 `hints.ai_flags` included @c AI_CANONNAME with @c nullptr as @c name)
5195 @throws randolf::rex::xEAI_FAIL Permanent failure code from DNS server
5196 @throws randolf::rex::xEAI_FAMILY The specified family is not supported
5197 @throws randolf::rex::xEAI_MEMORY Out of memory
5198 @throws randolf::rex::xEAI_NONAME If node_name is nullptr or an empty string
5199 @throws randolf::rex::xEAI_SERVICE The specified service is not available
5200 for the specified socket type
5201 @throws randolf::rex::xEAI_SOCKTYPE The specified socket type is not
5203 @throws randolf::rex::xEAI_SYSTEM Other system error (use errno to determine
5204 what the error is, then run use @ref randolf::rex::rex::mk_exception
5205 to throw the correct exception)
5207 @returns sockaddr_storage structure
5209 *///=========================================================================
5210// static std::unique_ptr<sockaddr_storage> mk_sockaddr_storage(
5211 static struct sockaddr_storage* mk_sockaddr_storage(
5212 /// IP address or UNIX domain socket address to convert
5213 const char* node_name,
5214 /// Port number (or service name used by some other families)
5215 const char* service_name = nullptr,
5216 /// Optional pointer to a helpful addrinfo structure
5217 const addrinfo* hints = nullptr) {
5219 // --------------------------------------------------------------------------
5220 // Initial sanity checks.
5221 // --------------------------------------------------------------------------
5222 if (node_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME); // if (node_name == nullptr && service_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME);
5224 // --------------------------------------------------------------------------
5225 // Handle any special cases.
5226 // --------------------------------------------------------------------------
5227 switch (hints == nullptr ? AF_UNSPEC : hints->ai_family) { // Default to AF_UNSPEC in the absence of hints
5229 if (node_name[0] != '/') break;
5230 // Next entry MUST be "case AF_UNIX" for this fall-through to work
5233 // For some unknown reason, clang++ can't compile this line that
5234 // g++ has absolutely no trouble with at all:
5235 // std::unique_ptr sa = std::make_unique_for_overwrite<sockaddr_storage>(AF_UNIX);
5236 // So, after wasting more than a year trying to figure out what the
5237 // hundreds of lines of cryptic errors meant, I eventually gave up
5238 // on clang++ for being such a dumbfuck, and used this work-around:
5239 struct sockaddr_storage* sa = (struct sockaddr_storage*)::malloc(sizeof(sockaddr_storage));
5240 sa->ss_family = AF_UNIX;
5241// std::unique_ptr sa = std::make_unique_for_overwrite<sockaddr_storage>();
5242// sa->ss_family = AF_UNIX;
5243 std::strcpy(((struct sockaddr_un *)sa)->sun_path, node_name); // Copy path
5247 } // -x- switch hints->family -x-
5249 // --------------------------------------------------------------------------
5250 // Acquire addrinfo[] linked-list array.
5251 // --------------------------------------------------------------------------
5252 addrinfo* __addr_result = nullptr; // This is temporary
5253//std::cout << " Making socket structure... node_name=" << node_name << " ...hints: ai_family=" << hints->ai_family << " ai_socktype=" << hints->ai_socktype << " ai_flags=" << hints->ai_flags << std::endl;
5254 randolf::rex::mk_exception("", getaddrinfo(node_name,
5259 // --------------------------------------------------------------------------
5260 // Find first valid addrinfo[] array by trying to open a socket with it.
5261 // --------------------------------------------------------------------------
5262 for (addrinfo* ar = __addr_result; ar != nullptr; ar = ar->ai_next) {
5263//std::cout << " ... ai_family=" << ar->ai_family << " ai_socktype=" << ar->ai_socktype << " ai_flags=" << ar->ai_flags << std::endl; // *******************
5264// ***************************** TODO: Make sure this loop is working properly
5266//std::cout << " !!! ai_family=" << __addr_result->ai_family << std::endl; // *******************
5268 // --------------------------------------------------------------------------
5269 // Copy the address to "sa" and return it. The "addr" data for IPv4 and IPv6
5270 // addresses include the TCP/UDP port number (service) in first two bytes as
5271 // as an unsigned integer (u_int16), followed immediately by the IP address.
5273 // We're taking the first record only. We assume that hints{} is defined on
5274 // an as-needed basis.
5276 // Note: AF_LINK and AF_PACKET are not widely supported, but we've made an
5277 // effort to accomodate their usage.
5278 // --------------------------------------------------------------------------
5279 struct sockaddr_storage* sa = (struct sockaddr_storage*)::malloc(sizeof(sockaddr_storage));
5280 //std::unique_ptr sa = std::make_unique_for_overwrite<sockaddr_storage>();
5281 switch (__addr_result->ai_family) {
5282 case AF_INET: // IPv4 address
5283 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_in));
5285 case AF_INET6: // IPv6 address
5286 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_in6));
5288 /* // We're handling this earlier as a special case: TODO: Move special handling to here
5289 case AF_UNIX: // UNIX (path) domain socket
5290 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_un));
5293 case /*AF_LINK*/18: // Link layer interface (arp)
5294 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_dl));
5296 case AF_PACKET: // Packet (ethernet) address (packet capturing)
5297 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_ll));
5299 default: // Everything else
5300 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_storage));
5301 } // -x- switch family -x-
5302 freeaddrinfo(__addr_result); // We don't need this anymore
5304 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
5306 /*======================================================================*//**
5307 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
5308 *///=========================================================================
5309 static struct sockaddr_storage* mk_sockaddr_storage(
5310 /// IP address or UNIX domain socket address to convert
5311 const char* node_name,
5313 const u_int16_t service_name,
5314 /// Optional pointer to a helpful addrinfo structure
5315 const addrinfo* hints = nullptr) {
5316 std::string port = std::to_string(service_name);
5317 return mk_sockaddr_storage(node_name, port.data(), hints);
5318 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
5320 /*======================================================================*//**
5321 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
5322 *///=========================================================================
5323 static struct sockaddr_storage* mk_sockaddr_storage(
5324 /// IP address or UNIX domain socket address to convert
5325 const std::string node_name,
5327 const u_int16_t service_name,
5328 /// Optional pointer to a helpful addrinfo structure
5329 const addrinfo* hints = nullptr) {
5330 std::string port = std::to_string(service_name);
5331 return mk_sockaddr_storage(node_name.data(), port.data(), hints);
5332 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
5334 /*======================================================================*//**
5335 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
5336 *///=========================================================================
5337 static struct sockaddr_storage* mk_sockaddr_storage(
5338 /// IP address or UNIX domain socket address to convert
5339 const std::string node_name,
5340 /// Port number (or server name used by some other families)
5341 const std::string service_name,
5342 /// Optional pointer to a helpful addrinfo structure
5343 const addrinfo* hints = nullptr) {
5344 return mk_sockaddr_storage(node_name.data(), service_name.data(), hints);
5345 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
5347 /*======================================================================*//**
5349 Set the special internal MPTCP protocol substitution policy, which determines
5350 whether to automatically substitute the @c IPPROTO_MPTCP protocol internally
5351 with the @c IPPROTO_TCP when using certain POSIX functions that are known to
5354 This method can be used before or after configuring a new socket.
5355 @returns The same rsocket object so as to facilitate stacking
5360 *///=========================================================================
5362 /// Set special internal MPTCP protocol substitution policy@n
5363 /// TRUE = substitution will occur (only for @c IPPROTO_MPTCP); this is the
5364 /// default for a new rsocket@n
5365 /// FALSE = no subsitution will occur
5366 const bool flag) noexcept {
5367 __socket_mptcp_flag = flag;
5369 } // -x- rsocket& mptcp -x-
5371 /*======================================================================*//**
5373 Specify a name for this rsocket.
5375 This is an arbitrary name that is entirely optional, and should be regarded
5376 as similar to the naming of threads.
5377 @returns The same rsocket object so as to facilitate stacking
5380 *///=========================================================================
5382 /// Name to assign to this @c rsocket
5383 const std::string& name) noexcept {
5384 // const std::lock_guard<std::mutex> lock(__name);
5387 } // -x- rsocket& name -x-
5389 /*======================================================================*//**
5391 Get socket I/O statistics from internally-tracked socket I/O counters.
5393 The number of bytes transmitted and received is tracked internally, so that
5394 the information can be used later in logging. These statistics are available
5395 at all times, but for logging purposes it makes the most sense to copy this
5396 information after the rsocket is closed, at which time the final statistics
5397 will continue to be available until the rsocket's destructor takes over.
5400 This method is threadsafe.
5401 @returns rsocket_io wrapped in a std::unique_ptr object to help ease memory
5407 *///=========================================================================
5408 std::unique_ptr<rsocket_io> net_io() noexcept {
5409 std::unique_ptr stats = std::make_unique_for_overwrite<rsocket_io>();
5410 stats->bytes_rx = __bytes_rx;
5411 stats->bytes_tx = __bytes_tx;
5412 stats->bytes_sx = __tls ? 0 : (__buffer != nullptr ? __buffer->get_utilized() : 0); // <bytes in buffer; are these lost bytes that should be subtracted from __bytes_rx?>
5413 stats->crypt_rx = __crypt_rx;
5414 stats->crypt_tx = __crypt_tx;
5415 stats->crypt_sx = __tls ? (__buffer != nullptr ? __buffer->get_utilized() : 0) : 0; // <bytes in buffer; are these lost bytes that should be subtracted from __crypt_rx?>
5416 stats->is_final = false;
5418 } // -x- std::unique_ptr<rsocket_io> net_io -x-
5420 /*======================================================================*//**
5422 Get socket I/O statistics from internally-tracked socket I/O counters as a
5423 pre-formatted std::string object.
5425 The number of bytes transmitted and received is tracked internally, so that
5426 the information can be used later in logging. These statistics are available
5427 at all times, but for logging purposes it makes the most sense to copy this
5428 information after the rsocket is closed, at which time the final statistics
5429 will continue to be available until the rsocket's destructor takes over.
5432 The format string may contain any characters, with only instances of the
5433 following case-sensitive command sequences to be interpolated accordingly
5434 (invalid commands will be ignored and will remain in place, unmodified):
5436 <table cellpadding=8 cellspacing=0 border=1>
5437 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
5438 <tr><td>$$</td><td>One dollar sign ("$")</td><td>@e N/A</td></tr>
5439 <tr><td>$aR</td><td>Total (all) bytes received</td><td>bytes_rx @c + crypt_rx</td></tr>
5440 <tr><td>$aS</td><td>Total (all) spare bytes recevied in ring buffer</td><td>bytes_sx @c + crypt_sx</td></tr>
5441 <tr><td>$aT</td><td>Total (all) bytes transmitted</td><td>bytes_tx @c + crypt_tx</td></tr>
5442 <tr><td>$bR</td><td>Unencrypted bytes received</td><td>bytes_rx</td></tr>
5443 <tr><td>$bS</td><td>Unencrypted spare bytes received in ring buffer</td><td>bytes_sx</td></tr>
5444 <tr><td>$bT</td><td>Unencrypted bytes transmitted</td><td>bytes_tx</td></tr>
5445 <tr><td>$cR</td><td>Encrypted bytes recevied</td><td>crypt_rx</td></tr>
5446 <tr><td>$cS</td><td>Encrypted spare bytes received in ring buffer</td><td>crypt_sx</td></tr>
5447 <tr><td>$cT</td><td>Encrypted bytes transmitted</td><td>crypt_tx</td></tr>
5448 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
5451 Why do we use dollar signs instead of percent symbols, like other formatting
5452 functions like printf() does? So that the format string won't be in conflict
5453 with any percent-prefixed commands in printf() and similar funcions. This
5454 means that a printf() format string can be put through a first pass here to
5455 get the needed statistics interpolated into the needed positions.
5458 This method is threadsafe.
5459 @returns An interpolated format string as an std::string object.
5464 *///=========================================================================
5468 /// Length of format string (in bytes), or 0 to auto-detect length if format string is an ASCIIZ string
5470 /// Pointer to an @ref rsocket_io structure to read the statistics from (nullptr = use internal copy of current statistics)
5471 // TODO: Convert this to a static method and create a new method without the "len" parameter that just calls the static method with the pointer to the internal "addr" buffer (this will make it easier to call this method in a static fashion later without incurring any of the overhead of instantiation)
5472 rsocket_io* addr = nullptr) noexcept {
5474 // --------------------------------------------------------------------------
5475 // Measure size of format string if an ASCIIZ string was indicated.
5476 // --------------------------------------------------------------------------
5477 if (len == 0) len = std::strlen(format);
5479 // --------------------------------------------------------------------------
5480 // Internal variables.
5481 // --------------------------------------------------------------------------
5482 rsocket_io* data = addr == nullptr ? net_io().get() : addr; // Copy current statistics so we have a snapshot instead of a set of possibly-changing statistics to work with
5483 std::string stats; // Formatted result
5484 ulong val; // Value (to be inserted)
5485 char c; // Current character
5486 std::string cmd; // Accumulated command (may be subsituted)
5487 bool flag_commas = false; // Thousands separators flag
5488 bool flag_right = false; // Flush-right flag
5490 // --------------------------------------------------------------------------
5491 // Process format string and build resulting formatted stats string.
5493 // This is designed to be fast, and I'm taking huge shortcuts here since the
5494 // commands are all the same size (except for the first one, which is only
5495 // two dollar sign characters). Processing in this loop should be quite fast
5496 // compared to using library functions to search for dollar sign characters
5497 // since data needs to be copied anyway -- why run a loop over the text twice
5498 // when can get away with doing it faster by doing it only once? This is an
5499 // optimized approach, although it probably could be even faster by not using
5500 // std::string for temporary command storage (there are other ways to do this
5501 // but I think this should suffice for now since it isn't expected to be used
5503 // --------------------------------------------------------------------------
5504 for (int i = 0; i < len; i++) {
5506 // --------------------------------------------------------------------------
5507 // First character (potentially).
5508 // --------------------------------------------------------------------------
5509 if ((c = format[i]) != '$') { // Not a dollar sign -- save it and move on
5513 cmd = c; // Start building up the command string
5515 // --------------------------------------------------------------------------
5516 // Second character: Part 1 of 2
5518 // TODO: Add support for "," commas, and "r" right-alignment.
5519 // --------------------------------------------------------------------------
5520 if (++i == len) continue; // End of format string, so we're done
5521 cmd.push_back(c = format[i]);
5522 if (c == '$') { // Consume the previous dollar sign so that $$ turns into $
5523 stats.push_back(c); // Add $ to stats string (we're only keeping one dollar sign)
5526 flag_commas = flag_right = false; // Reset these flags for a clean start
5528 // --------------------------------------------------------------------------
5530 // , = Include thousands separators (commas)
5531 // r = Flush right (leading spaces will be added)
5532 // --------------------------------------------------------------------------
5535 if (++i == len) continue; // End of format string, so we're done
5536 cmd.push_back(c = format[i]);
5538 goto net_io_flags_loop;
5539 } else if (c == 'r') {
5540 if (++i == len) continue; // End of format string, so we're done
5541 cmd.push_back(c = format[i]);
5543 goto net_io_flags_loop;
5544 } // -x- if [,r] -x-
5547 // --------------------------------------------------------------------------
5548 // Second character: Part 1 of 2
5549 // --------------------------------------------------------------------------
5550 if (c < 'a' || c > 'c') { // Not a, b, or c, so treat it as a regular character
5551 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
5553 } // -x- if !~[abc] -x-
5555 // --------------------------------------------------------------------------
5557 // --------------------------------------------------------------------------
5558 if (++i == len) continue; // End of format string, so we're done
5559 cmd.push_back(c = format[i]);
5560 if (c != 'R' && c != 'T') { // Not R or T, so treat it as a regular character
5561 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
5563 } // -x- if !~[RT] -x-
5565 // --------------------------------------------------------------------------
5566 // Command processing. If the command is valid, then it will be interpolated
5567 // with its expected result by replacing it with the resulting value.
5568 // --------------------------------------------------------------------------
5569 //std::cout << "[" << cmd << "]"; // Debug
5570 if (cmd.ends_with("aR")) val = data->bytes_rx + data->crypt_rx;
5571 else if (cmd.ends_with("aS")) val = data->bytes_sx + data->crypt_sx;
5572 else if (cmd.ends_with("aT")) val = data->bytes_tx + data->crypt_tx;
5573 else if (cmd.ends_with("bR")) val = data->bytes_rx ;
5574 else if (cmd.ends_with("bS")) val = data->bytes_sx ;
5575 else if (cmd.ends_with("bT")) val = data->bytes_tx ;
5576 else if (cmd.ends_with("cR")) val = data->crypt_rx;
5577 else if (cmd.ends_with("cS")) val = data->crypt_sx;
5578 else if (cmd.ends_with("cT")) val = data->crypt_tx;
5579 else { stats.append(cmd); continue; } // This is wrong, so ignore it
5581 // --------------------------------------------------------------------------
5582 // Re-use cmd to generate formatted value.
5583 // --------------------------------------------------------------------------
5585 cmd.resize(21); // Maximum length without commas (plus character zero): 18446744073709551615
5586 cmd.resize(sprintf(cmd.data(), flag_right ? "%20lu" : "%lu", val)); // The ::sprintf function can't use cmd.data() here
5587 if (flag_commas) cmd = randolf::rtools::insert_commas(cmd.data()); // Insert commas (this makes the string longer)
5589 // --------------------------------------------------------------------------
5590 // Convert to std::string and add to the stats string.
5591 // --------------------------------------------------------------------------
5597 } // -x- std::string net_io -x-
5599 /*======================================================================*//**
5601 Where the destructor should save final I/O statistics before this rsocket's
5602 resources are completely freed/deallocated.
5604 Developers should take care to check that the @ref rsocket_io::is_final flag
5605 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
5606 since there's no guarantee that the destructor will necessarily be executed
5607 in a timely manner (this flag is set last, after all other statistics are
5608 copied into the structure while in a mutex-locked state).
5609 @returns The same rsocket object so as to facilitate stacking
5615 *///=========================================================================
5616 rsocket& net_io_final(
5617 /// Pointer to @ref rsocket_io structure (nullptr = disabled)
5618 rsocket_io* addr) noexcept {
5619 __io_final_addr = addr;
5621 } // -x- rsocket& net_io_final -x-
5623 /*======================================================================*//**
5625 Where the destructor should save current I/O statistics.
5627 Statistics are copied into the structure while in a mutex-locked state, but
5628 the copy itself is not an overall atomic snapshot of the I/O statistics even
5629 though each statistic is copied atomically. This means that the actual
5630 statistics could be slightly different if updates occur independently (e.g.,
5631 due to activities in a different thread), but this likely won't matter since
5632 the anticipated use for these statistics is to display or otherwise present
5633 general statistical information to a user at regular intervals.
5634 @returns The same rsocket object so as to facilitate stacking
5640 *///=========================================================================
5641 rsocket& net_io_update(
5642 /// Pointer to @ref rsocket_io structure (nullptr = do nothing)
5643 rsocket_io* addr) noexcept {
5645 // --------------------------------------------------------------------------
5646 // Copy statistics to final location. (We do this last in case there's an
5647 // issue with locking; there shouldn't be, but if there is then at least we
5648 // already we're not inadvertently hanging on to resources at this point that
5649 // need to be freed/closed anyway.)
5650 // --------------------------------------------------------------------------
5651 if (addr != nullptr) {
5653 addr->bytes_rx = __bytes_rx;
5654 addr->bytes_tx = __bytes_tx;
5655 addr->bytes_sx = __tls ? 0 : (__buffer != nullptr ? __buffer->get_utilized() : 0); // <bytes in buffer; are these lost bytes that should be subtracted from __bytes_rx?>
5656 addr->crypt_rx = __crypt_rx;
5657 addr->crypt_tx = __crypt_tx;
5658 addr->crypt_sx = __tls ? (__buffer != nullptr ? __buffer->get_utilized() : 0) : 0; // <bytes in buffer; are these lost bytes that should be subtracted from __crypt_rx?>
5659 addr->is_final = false;
5661 } // -x- if addr -x-
5664 } // -x- rsocket& net_io_update -x-
5666 /*======================================================================*//**
5668 Return the number of bytes pending to be received, without actually receiving
5672 When using TLS, OpenSSL may not report all the pending data, but this is
5673 resolved here by also adding the total amount of pending raw data that is not
5674 currently being processed in the current record by OpenSSL, even when "read
5677 @throws randolf::rex::xEBADF The underlying socket is not open
5678 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5679 part of the user address space
5680 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
5681 @throws randolf::rex::xENOTTY The underlying socket is not associated with a
5682 character special device, or the specified operation does not apply
5683 to the kind of object that the file descriptor fd references (this
5684 exception will probably never occur unless the underlying socket
5685 handle was arbitrarily replaced with the type of handle that can
5686 cause this error to occur)
5688 @returns Total number of bytes pending (0 = none)
5692 *///=========================================================================
5694 if (__debug) debug("pending(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5697 // --------------------------------------------------------------------------
5698 // Get pending bytes from raw socket.
5699 // --------------------------------------------------------------------------
5700 ulong pending_bytes = 0; // BSD, glibc, etc., use ulong; older UNIX systems use int // TODO: Figure out how to detect what ioctl needs
5701 __rc_check(ioctl(__socket_fd, FIONREAD, &pending_bytes));
5703 // --------------------------------------------------------------------------
5704 // Add pending bytes from OpenSSL if TLS is active, because this total isn't
5705 // included in the raw socket count since OpenSSL received these bytes.
5706 // --------------------------------------------------------------------------
5707 return __tls ? pending_bytes + SSL_pending(__tls_fd) : pending_bytes;
5709 } // -x- ulong pending -x-
5711 /*======================================================================*//**
5713 Poll the underlying socket using the poll() method for data that's ready for
5714 receiving (default), etc.
5717 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
5718 select(), and accept()/accept4() when using multiple sockets.
5720 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5721 part of the user address space
5722 @throws randolf::rex::xEINTR Interrupted by a signal
5723 @throws randolf::rex::xENOMEM Insufficient memory
5724 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5725 doesn't refer to a socket
5726 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
5727 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
5728 is a highly improbable chance that a timeout could still occur if the
5729 data is read by another thread before the `recv(..., MSG_PEEK)` call)
5730 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5733 @returns The resulting (short)pollfd.revents bitfield
5734 @returns 0 if @c timeout_behaviour is set to TIMEOUT_ZERO
5739 *///=========================================================================
5740 // TODO: Integrate SSL_poll (requires OpenSSL 3.3 or newer) with SSL_POLL_EVENT_R, SSL_POLL_EVENT_W, and SSL_POLL_EVENT_E conversion
5742 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
5743 const short events = POLLIN,
5744 /// Number of milliseconds to wait
5745 const int timeout = 0,
5746 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
5747 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
5748 if (__debug) debug("poll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5749 + ", pollfd.events=" + std::to_string(events)
5750 + ", timeout=" + std::to_string(timeout)
5752 struct pollfd fds{__socket_fd, events, 0};
5753 if (__rc_check(::poll(&fds, 1, timeout)) == 0) { // Timeout occurred when ::poll returns 0
5754 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
5758 } // -x- short poll -x-
5760 /*======================================================================*//**
5762 Get port number associated with underlying socket descriptor/handle.
5765 - an emphemeral port number assignment (dynamic) is intended
5766 - the underlying socket details are not defined (e.g., in the case of an
5767 empty rsocket instantiation)
5768 - port numbers are not supported/utilized by the current family (e.g., not
5769 AF_INET {IPv4} or AF_INET6 {IPv6})
5771 The port number can be set in most constructors, or via one of the socket()
5773 @returns Port number (typically TCP and UDP, although some other families
5774 such as SCTP and DCCP also utilize port numbers)
5775 @see socket_family()
5776 @see socket_protocol()
5779 *///=========================================================================
5780 uint16_t port() noexcept {
5781 switch (__socket_addr->ss_family) {
5782 case AF_INET: // IPv4
5783// return ntohs(((sockaddr_in*)&__socket_addr)->sin_port);
5784 return ntohs(((sockaddr_in*)__socket_addr)->sin_port);
5785 case AF_INET6: // IPv6
5786// return ntohs(((sockaddr_in6*)&__socket_addr)->sin6_port);
5787 return ntohs(((sockaddr_in6*)__socket_addr)->sin6_port);
5788 default: // Everything else
5790 } // -x- switch __socket_addr->ss_family -x-
5791 } // -x- uint16_t port -x-
5793 /*======================================================================*//**
5795 Poll the underlying socket using the ppoll() method for data that's ready for
5796 receiving (default), etc.
5799 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
5800 select(), and accept()/accept4() when using multiple sockets.
5802 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5803 part of the user address space
5804 @throws randolf::rex::xEINTR Interrupted by a signal
5805 @throws randolf::rex::xENOMEM Insufficient memory
5806 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5807 doesn't refer to a socket
5808 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
5809 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
5810 is a highly improbable chance that a timeout could still occur if the
5811 data is read by another thread before the `recv(..., MSG_PEEK)` call)
5812 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5815 @returns The resulting (short)pollfd.revents bitfield
5820 *///=========================================================================
5821 // TODO: Integrate SSL_poll (requires OpenSSL 3.3 or newer) with SSL_POLL_EVENT_R, SSL_POLL_EVENT_W, and SSL_POLL_EVENT_E conversion
5823 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
5824 const short events = POLLIN,
5826 const struct timespec* tmo_p = nullptr,
5828 const sigset_t* sigmask = nullptr,
5829 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
5830 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
5831 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5832 + ", pollfd.events=" + std::to_string(events)
5833 + ", timespec{tv_sec=" + std::to_string(tmo_p->tv_sec)
5834 + ", tv_nsec=" + std::to_string(tmo_p->tv_nsec) + "}"
5837 struct pollfd fds{__socket_fd, events, 0};
5838 if (__rc_check(::ppoll(&fds, 1, tmo_p, sigmask)) == 0) { // Timeout occurred when ::ppoll returns 0
5839 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
5843 } // -x- short ppoll -x-
5845 /*======================================================================*//**
5847 Poll the underlying socket using the ppoll() method for data that's ready for
5848 receiving (default), etc.
5851 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
5852 select(), and accept()/accept4() when using multiple sockets.
5854 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5855 part of the user address space
5856 @throws randolf::rex::xEINTR Interrupted by a signal
5857 @throws randolf::rex::xENOMEM Insufficient memory
5858 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5859 doesn't refer to a socket
5860 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
5861 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
5862 is a highly improbable chance that a timeout could still occur if the
5863 data is read by another thread before the `recv(..., MSG_PEEK)` call)
5864 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5867 @returns The resulting (short)pollfd.revents bitfield
5871 *///=========================================================================
5872 // TODO: Integrate SSL_poll (requires OpenSSL 3.3 or newer) with SSL_POLL_EVENT_R, SSL_POLL_EVENT_W, and SSL_POLL_EVENT_E conversion
5874 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
5875 const short events = POLLIN,
5876 /// Timeout in seconds
5877 const long tv_sec = 0,
5878 /// Timeout in nanoseconds
5879 const long tv_nsec = 0,
5881 const sigset_t* sigmask = nullptr,
5882 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
5883 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
5884 struct timespec tmo_p{tv_sec, tv_nsec};
5885 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5886 + ", pollfd.events=" + std::to_string(events)
5887 + ", timespec{tv_sec=" + std::to_string(tmo_p.tv_sec)
5888 + ", tv_nsec=" + std::to_string(tmo_p.tv_nsec) + "}"
5891 struct pollfd fds{__socket_fd, events, 0};
5892 if (__rc_check(::ppoll(&fds, 1, &tmo_p, sigmask)) == 0) { // Timeout occurred when ::ppoll returns 0
5893 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
5897 } // -x- short ppoll -x-
5899 /*======================================================================*//**
5901 Send a formatted string to the @ref rsocket endpoint.
5903 The @c format is described in the documentation for the POSIX or Standard C
5904 Library @c printf() function.
5905 @throws randolf::rex::xEBADF The underlying socket is not open
5906 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
5907 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
5908 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
5909 @throws randolf::rex::xENOMEM Insufficient memory
5910 @returns The same rsocket object so as to facilitate stacking
5912 @see is_eol_fix_printf
5919 *///=========================================================================
5921 /// Format string to use
5923 /// Variadic arguments
5925 char* buf = nullptr;
5927 ::va_start(args, format);
5928 int rc = ::vasprintf(&buf, format, args);
5929 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
5930 if (rc < 0 && buf != nullptr) ::free(buf);
5931 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
5932 if (__eol_fix_printf && !__eol.empty()) {
5933 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
5935 __send(str.data(), str.length());
5940 } catch (std::exception& e) { // Free buf then re-throw the exception
5941 ::free(buf); // Prevent memory leak when an exception is thrown
5944 } // -x- if __eol_fix_printf -x-
5946 } // -x- rsocket& printf -x-
5948 /*======================================================================*//**
5950 Send a formatted string to the @ref rsocket endpoint, and append an EoL
5953 The @c format is described in the documentation for the POSIX or Standard C
5954 Library @c printf() function.
5955 @throws randolf::rex::xEBADF The underlying socket is not open
5956 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
5957 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
5958 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
5959 @throws randolf::rex::xENOMEM Insufficient memory
5960 @returns The same rsocket object so as to facilitate stacking
5963 @see is_eol_fix_printf
5970 *///=========================================================================
5971 rsocket& printfline(
5972 /// Format string to use
5974 /// Variadic arguments
5976 char* buf = nullptr;
5978 ::va_start(args, format);
5979 int rc = ::vasprintf(&buf, format, args);
5980 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
5981 if (rc < 0 && buf != nullptr) ::free(buf);
5982 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
5983 if (__eol_fix_printf && !__eol.empty()) {
5984 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
5987 __send(str.data(), str.length());
5990 __sendline(buf, rc);
5992 } catch (std::exception& e) { // Free buf then re-throw the exception
5993 ::free(buf); // Prevent memory leak when an exception is thrown
5996 } // -x- if __eol_fix_printf -x-
5998 } // -x- rsocket& printfline -x-
6000 /*======================================================================*//**
6002 Receive data from the endpoint into a @c std::vector<char> that is allocated
6005 If nbytes is 0, then the internal @ref buffer_size() will be used as the
6006 default, which can also be changed from its compiled-in default of 1024 by
6007 using one of the buffer_size() methods.
6010 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6011 reception, but it's important to note that it does implement temporary
6012 blocking while waiting for data.
6014 @throws randolf::rex::xEBADF The underlying socket is not open
6015 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6017 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6018 part of the user address space
6019 @throws randolf::rex::xEINTR Interrupted by a signal
6020 @throws randolf::rex::xEINVAL Invalid argument passed
6021 @throws randolf::rex::xENOMEM Insufficient memory
6022 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6023 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6024 doesn't refer to a socket
6025 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6028 @returns appropriately-sized vector of characters
6029 @see recv(std::vector<char>, const int)
6030 @see recvz(const size_t, const int)
6032 @see send(const std::vector<char>, const int)
6034 *///=========================================================================
6035 std::vector<char> recv(
6036 /// Maximum number of bytes to receive
6037 const size_t nbytes = 0,
6042 /// MSG_CMSG_CLOEXEC
6043 const int posix_flags = 0) {
6044 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6045 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6047 + ", " + std::to_string(buf_size)
6048 + ", " + std::to_string(posix_flags)
6050 std::vector<char> buf(buf_size);
6051 buf.resize(__recv(buf.data(), buf.size(), posix_flags));
6053 } // -x- std::vector<char> recv -x-
6055 /*======================================================================*//**
6057 Receive data from the endpoint into the @c std::vector object supplied in the
6058 @c buf parameter, overwriting any pre-existing data.
6060 The maximum number of bytes that can be received won't exceed the number of
6061 bytes that the supplied @c std::vector<char> was initialized or resized to.
6064 For @c std::vector it's important that the @c resize() method is used to
6065 pre-allocate the underlying char[] array, instead of the unfortunately-named
6066 @c reserve() method that doesn't pre-allocate, to avoid causing segmentation
6067 faults or other undefined behaviours.
6070 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6071 reception, but it's important to note that it does implement temporary
6072 blocking while waiting for data.
6074 @throws randolf::rex::xEBADF The underlying socket is not open
6075 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6077 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6078 part of the user address space
6079 @throws randolf::rex::xEINTR Interrupted by a signal
6080 @throws randolf::rex::xEINVAL Invalid argument passed
6081 @throws randolf::rex::xENOMEM Insufficient memory
6082 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6083 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6084 doesn't refer to a socket
6085 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6088 @returns The same array that was specified in the @c buf parameter
6089 @see recv(const size_t, const int)
6090 @see recvz(const size_t, const int)
6091 @see recv_append_to(std::vector&, const int)
6092 @see send(const std::vector<char>, const int)
6095 *///=========================================================================
6096 std::vector<char>& recv(
6097 /// Target std::vector<char> to receive data into (any existing data residing
6098 /// in this @c vector will be overwritten)
6099 std::vector<char>& buf,
6104 /// MSG_CMSG_CLOEXEC
6105 const int posix_flags = 0) {
6106 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6108 + ", " + std::to_string(buf.size())
6109 + ", " + std::to_string(posix_flags)
6111 buf.resize(__recv(buf.data(), buf.size(), posix_flags));
6113 } // -x- std::vector<char>& recv -x-
6115 /*======================================================================*//**
6117 Receive data from the endpoint, appending the received data directly into a
6118 @c std::string object.
6120 If nbytes is 0, then the internal @ref buffer_size() will be used as the
6121 default, which can also be changed from its compiled-in default of 1024 by
6122 using one of the buffer_size() methods.
6125 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6126 reception, but it's important to note that it does implement temporary
6127 blocking while waiting for data.
6129 @throws randolf::rex::xEBADF The underlying socket is not open
6130 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6132 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6133 part of the user address space
6134 @throws randolf::rex::xEINTR Interrupted by a signal
6135 @throws randolf::rex::xEINVAL Invalid argument passed
6136 @throws randolf::rex::xENOMEM Insufficient memory
6137 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6138 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6139 doesn't refer to a socket
6140 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6143 @returns reference to the same std::string that data was appended to,
6146 @see recv(std::vector<char>, const int)
6147 @see recv_append_to(std::vector<char>&, const int)
6149 @see recvz(const size_t, const int)
6150 @see send(const std::string, const int)
6152 *///=========================================================================
6153 std::string& recv_append_to(
6154 /// Where to append the data
6156 /// Maximum number of bytes to receive
6157 const size_t nbytes = 0,
6162 /// MSG_CMSG_CLOEXEC
6163 const int posix_flags = 0) {
6164 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6165 if (__debug) debug("recv_append_to(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6166 + ", <(std::string&)buf>"
6167 + ", " + std::to_string(buf_size)
6168 + ", " + std::to_string(posix_flags)
6170 int target_size = buf.size();
6171 buf.resize(target_size + buf_size); // Pre-fill additional anticipated string size
6172 buf.resize(target_size + __recv((void*)(buf.data() + target_size), buf_size, posix_flags)); // Shorten string
6174 } // -x- std::string& recv_append_to -x-
6176 /*======================================================================*//**
6178 Receive data from the endpoint, appending the received data directly into a
6179 `std::vector<char>` object.
6181 If nbytes is 0, then the internal @ref buffer_size() will be used as the
6182 default, which can also be changed from its compiled-in default of 1024 by
6183 using one of the buffer_size() methods.
6186 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6187 reception, but it's important to note that it does implement temporary
6188 blocking while waiting for data.
6190 @throws randolf::rex::xEBADF The underlying socket is not open
6191 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6193 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6194 part of the user address space
6195 @throws randolf::rex::xEINTR Interrupted by a signal
6196 @throws randolf::rex::xEINVAL Invalid argument passed
6197 @throws randolf::rex::xENOMEM Insufficient memory
6198 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6199 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6200 doesn't refer to a socket
6201 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6204 @returns reference to the same std::string that data was appended to,
6207 @see recv(std::vector<char>, const int)
6208 @see recv_append_to(std::string&, const int)
6209 @see recvz(const size_t, const int)
6210 @see send(const std::string, const int)
6212 *///=========================================================================
6213 std::vector<char>& recv_append_to(
6214 /// Where to append the data
6215 std::vector<char>& buf,
6216 /// Maximum number of bytes to receive
6217 const size_t nbytes = 0,
6222 /// MSG_CMSG_CLOEXEC
6223 const int posix_flags = 0) {
6224 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6225 if (__debug) debug("recv_append_to(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6226 + ", <(std::vector<char>&)buf>"
6227 + ", " + std::to_string(buf_size)
6228 + ", " + std::to_string(posix_flags)
6230 int target_size = buf.size();
6231 buf.resize(target_size + buf_size); // Pre-fill additional anticipated string size
6232 buf.resize(target_size + __recv((void*)(buf.data() + target_size), buf_size, posix_flags)); // Shorten string
6234 } // -x- std::vector<char>& recv_append_to -x-
6236 /*======================================================================*//**
6238 Receive data from the endpoint into a @c std::string object that is allocated
6241 If nbytes is 0, then the internal @ref buffer_size() will be used as the
6242 default, which can also be changed from its compiled-in default of 1024 by
6243 using one of the buffer_size() methods.
6246 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6247 reception, but it's important to note that it does implement temporary
6248 blocking while waiting for data.
6250 @throws randolf::rex::xEBADF The underlying socket is not open
6251 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6253 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6254 part of the user address space
6255 @throws randolf::rex::xEINTR Interrupted by a signal
6256 @throws randolf::rex::xEINVAL Invalid argument passed
6257 @throws randolf::rex::xENOMEM Insufficient memory
6258 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6259 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6260 doesn't refer to a socket
6261 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6264 @returns appropriately-sized std::string of characters
6266 @see recv(std::vector<char>, const int)
6267 @see recv_append_to(std::string&, const int)
6268 @see recvz(const size_t, const int)
6269 @see send(const std::string, const int)
6271 *///=========================================================================
6272 std::string recv_as_string(
6273 /// Maximum number of bytes to receive
6274 const size_t nbytes = 0,
6279 /// MSG_CMSG_CLOEXEC
6280 const int posix_flags = 0) {
6281 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6282 if (__debug) debug("recv_as_string(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6283 + ", " + std::to_string(buf_size)
6284 + ", " + std::to_string(posix_flags)
6287 buf.resize(buf_size); // Pre-fill anticipated string size
6288 buf.resize(__recv(buf.data(), buf_size, posix_flags)); // Shorten string
6290 } // -x- std::string recv_as_string -x-
6292 /*======================================================================*//**
6294 Receive an ASCIIZ string from the endpoint, including the NULL terminator.
6296 @throws randolf::rex::xEBADF The underlying socket is not open
6297 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6299 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6300 part of the user address space
6301 @throws randolf::rex::xEINTR Interrupted by a signal
6302 @throws randolf::rex::xEINVAL Invalid argument passed
6303 @throws randolf::rex::xENOMEM Insufficient memory
6304 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6305 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6306 doesn't refer to a socket
6307 @throws randolf::rex::xERANGE if no NULL terminator is detected (this may
6308 occur before the underlying ASCIIZ string char* array is allocated)
6309 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6312 @returns Pointer to ASCIIZ string (will need to be deleted by caller)
6313 @see send_asciiz(const char*, const int)
6315 *///=========================================================================
6317 /// Maximum number of bytes to receive
6318 const size_t nbytes = 0,
6323 /// MSG_CMSG_CLOEXEC
6324 const int posix_flags = 0) {
6325 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6326 if (__debug) debug("recv_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6327 + ", " + std::to_string(buf_size)
6328 + ", " + std::to_string(posix_flags)
6331 // --------------------------------------------------------------------------
6332 // Calculate size of buffer (includes NULL terminator since we'll also be
6333 // receiving this from the endpoint).
6334 // --------------------------------------------------------------------------
6337 // --------------------------------------------------------------------------
6338 // Reduce buffer size to what is actually read (remember: we don't actually
6339 // know where the EoL sequence is yet, or if there even is one).
6340 // --------------------------------------------------------------------------
6341 int max = __recv(&buf, buf_size, posix_flags | MSG_PEEK); // Look-ahead at socket stream (buffer was inflated to include EoL sequence)
6343 for (int i = 0; i < max; i++) {
6344 if (buf[i] == 0) { // EoD
6347 } // -x- if v[i] -x-
6349 if (len < 0) throw randolf::rex::xERANGE("ERANGE: NULL terminator not found");
6351 // --------------------------------------------------------------------------
6352 // I'd love to use std::unique_ptr<char*> for this next part, but it leads
6353 // to intermittent segmentation faults and/or "malloc(): corrupted top size
6354 // occurs" errors outside of this method. So, my conclusion is that char*
6355 // (and char[], as I've tried with this too) are not properly supported by:
6356 // std::unique_ptr<char*> v = std::make_shared<char*>(new char[len + 1]);
6358 // Using std::string() is really not what we want because there is confusion
6359 // concerning the NULL terminator -- with char* strings, it's crystal clear
6360 // that there must be a NULL terminator (since a length isn't tracked). Now,
6361 // if you want an std::string, just initialize as follows:
6363 // char* temp_string = r.recv_asciiz();
6364 // std::string mystr = std::string(temp_string);
6365 // delete temp_string;
6366 // --------------------------------------------------------------------------
6367 char* v = new char[len + 1];
6368 int rc = __recv(v, len + 1, posix_flags); // Consume with NULL terminator from socket stream
6369// TODO: Free "v" in a catch-and-rethrow block (check other calls to __recv and __send too)
6372 } // -x- char* recv_asciiz -x-
6374 /*======================================================================*//**
6376 Receive one byte (unsigned 8-bit byte) of data from the endpoint.
6378 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6379 reception, but it's important to note that it does implement temporary
6380 blocking while waiting for data.
6382 @throws randolf::rex::xEBADF The underlying socket is not open
6383 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6385 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6386 part of the user address space
6387 @throws randolf::rex::xEINTR Interrupted by a signal
6388 @throws randolf::rex::xEINVAL Invalid argument passed
6389 @throws randolf::rex::xENOMEM Insufficient memory
6390 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6391 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6392 doesn't refer to a socket
6393 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6396 @returns one unsigned character
6397 @see recv(std::vector<char>, const int)
6398 @see recvz(const size_t, const int)
6403 *///=========================================================================
6409 /// MSG_CMSG_CLOEXEC
6410 const int posix_flags = 0) {
6412 if (__debug) debug("recv_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6414 + ", " + std::to_string(sizeof(buf))
6415 + ", " + std::to_string(posix_flags)
6417 int rc = __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6419 } // -x- byte recv_byte -x-
6421 /*======================================================================*//**
6423 Receive one character (signed 8-bit byte) of data from the endpoint.
6425 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6426 reception, but it's important to note that it does implement temporary
6427 blocking while waiting for data.
6429 @throws randolf::rex::xEBADF The underlying socket is not open
6430 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6432 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6433 part of the user address space
6434 @throws randolf::rex::xEINTR Interrupted by a signal
6435 @throws randolf::rex::xEINVAL Invalid argument passed
6436 @throws randolf::rex::xENOMEM Insufficient memory
6437 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6438 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6439 doesn't refer to a socket
6440 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6443 @returns one signed character
6444 @see recv(std::vector<char>, const int)
6445 @see recvz(const size_t, const int)
6450 *///=========================================================================
6456 /// MSG_CMSG_CLOEXEC
6457 const int posix_flags = 0) {
6459 if (__debug) debug("recv_char(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6461 + ", " + std::to_string(sizeof(buf))
6462 + ", " + std::to_string(posix_flags)
6464 int rc = __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6465 // How to detect disconnected stream when telnet user presses CTRL-C?
6466 //std::cout << "rc=" << std::to_string(rc) << std::endl; // Debug (to be removed)
6467// TODO: Investigate checking platform's char size and moving signing bit accordingly
6469 } // -x- char recv_char -x-
6471 /*======================================================================*//**
6473 Receive a line of data from the endpoint, into a new @ref randolf::rline
6474 object, with the EoL character(s) isolated. This is meant for multiline
6475 ASCII and UTF-8 text, and while it will also work with binary data that
6476 doesn't include any EoL sequence characters (ASCII `10` and `13`),
6477 @ref recv(), @ref recvz(), and other methods are much better-suited to
6478 receive binary data.
6480 This is essentially a wrapper around what recvline() does, but returns both
6481 the line of text and the EoL sequence together in a new @ref randolf::rline
6484 For additional details on the other parameters, please see the @ref recvline
6485 method's documentation for the remaining details.
6487 If you're using a customzied EoL sequence, then it's important to note that
6488 it probably won't be recognized by the @ref randolf::rline class, but do also
6489 check that documentation to confirm this.
6491 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
6492 @throws randolf::rex::xEBADF The underlying socket is not open
6493 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6495 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6496 part of the user address space
6497 @throws randolf::rex::xEINTR Interrupted by a signal
6498 @throws randolf::rex::xEINVAL Invalid argument passed
6499 @throws randolf::rex::xENOMEM Insufficient memory
6500 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6501 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6502 doesn't refer to a socket
6503 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
6504 no EoL character sequence is detected after recvline's buffer became
6505 full (whatever data is waiting may still be received using any of the
6506 recv_ methods {with or without the @c MSG_PEEK flag set}, including
6507 @c recvline() with a larger buffer {see the @c nbytes parameter})
6508 @throws randolf::rex::xERANGE if the timeout parameter is below 0
6509 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6512 @returns One line of text as a randolf::rline object.
6515 @see eol_consumed_seq()
6518 @see send_rline(const randolf::rline, const int)
6519 @see sendline(const std::string, const int)
6521 @see timeout_recvline
6522 @see timeout_recvline(long)
6524 *///=========================================================================
6525 randolf::rline recv_rline(
6526 /// Maximum number of bytes to receive (including EoL character sequence)
6527 const size_t nbytes = 0,
6532 /// MSG_CMSG_CLOEXEC
6533 const int posix_flags = 0,
6534 /// Line timeout (in seconds)@n
6535 /// 0 = no timeout (default), unless it was configured by way of the
6536 /// @ref timeout_recvline(long) method
6538 /// Configuration parameters
6539 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
6540 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
6541 // + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF; TODO: Add an option for this
6542 // Internally, the length of the @ref eol() sequence is added to the buffer size
6543 // so that the maximum line length without EoL characters matches the maximum
6544 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
6545 if (__debug) debug("recv_rline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6546 + ", " + std::to_string(buf_size)
6547 + ", " + std::to_string(posix_flags)
6550 __recvline(str, buf_size, posix_flags, timeout, recvline_flags);
6551 return randolf::rline(str, __eol_consumed_seq);
6552 } // -x- randolf::rline recv_rline -x-
6554 /*======================================================================*//**
6556 Receive a line of data from the endpoint assigned into the specifieid
6557 @ref randolf::rline object, with the EoL character(s) isolated. This is
6558 meant for multiline ASCII and UTF-8 text, and while it will also work with
6559 binary data that doesn't include any EoL sequence characters (ASCII `10` and
6560 `13`), @ref recv(), @ref recvz(), and other methods are much better-suited to
6561 receive binary data.
6564 Be sure to read the docuemntation for the @c eol and @c eol_adoption methods,
6565 especially if you're using @c recvline or @c recv_rline to implement an
6566 internet daemon, because pre-configuring the EoL sequence is an important
6567 consideration for some protocols.
6569 This is essentially a wrapper around what recvline() does, but returns both
6570 the line of text and the EoL sequence together, assigned into the existing
6571 @ref randolf::rline object.
6573 For additional details on the other parameters, please see the @ref recvline
6574 method's documentation for the remaining details.
6576 If you're using a customzied EoL sequence, then it's important to note that
6577 it probably won't be recognized by the @ref randolf::rline class, but do also
6578 check that documentation to confirm this.
6580 When providing @c nullptr for the line parameter, a new @ref randolf::rline
6581 object will be instantiated that will then need to be deleted after it's no
6584 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
6585 @throws randolf::rex::xEBADF The underlying socket is not open
6586 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6588 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6589 part of the user address space
6590 @throws randolf::rex::xEINTR Interrupted by a signal
6591 @throws randolf::rex::xEINVAL Invalid argument passed
6592 @throws randolf::rex::xENOMEM Insufficient memory
6593 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6594 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6595 doesn't refer to a socket
6596 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
6597 no EoL character sequence is detected after recvline's buffer became
6598 full (whatever data is waiting may still be received using any of the
6599 recv_ methods {with or without the @c MSG_PEEK flag set}, including
6600 @c recvline() with a larger buffer {see the @c nbytes parameter})
6601 @throws randolf::rex::xERANGE if the timeout parameter is below 0
6602 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6605 @returns One line of text as a randolf::rline object.
6608 @see eol_consumed_seq()
6612 @see send_rline(const randolf::rline, const int)
6613 @see sendline(const std::string, const int)
6615 @see timeout_recvline
6616 @see timeout_recvline(long)
6618 *///=========================================================================
6619 randolf::rline* recv_rline(
6620 /// Pointer to the pre-instantiated randolf::rline object to assign into@n
6621 /// nullptr = instantiate a new randolf::rline object internally, with this
6622 /// rsocket's current EoL sequence set to override the default
6623 randolf::rline* line,
6624 /// Maximum number of bytes to receive (including EoL character sequence)
6625 const size_t nbytes = 0,
6630 /// MSG_CMSG_CLOEXEC
6631 const int posix_flags = 0,
6632 /// Line timeout (in seconds)@n
6633 /// 0 = no timeout (default), unless it was configured by way of the
6634 /// @ref timeout_recvline(long) method
6636 /// Configuration parameters
6637 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
6638 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
6639 // + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF; TODO: Add an option for this
6640 // Internally, the length of the @ref eol() sequence is added to the buffer size
6641 // so that the maximum line length without EoL characters matches the maximum
6642 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
6643 if (__debug) debug("recv_rline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6644 + ", <randolf::rline*>"
6645 + ", " + std::to_string(buf_size)
6646 + ", " + std::to_string(posix_flags)
6647 + ", " + std::to_string(timeout)
6648 + ", " + std::to_string(recvline_flags)
6651 // --------------------------------------------------------------------------
6652 // Instantiate new object if nullptr was provided.
6653 // --------------------------------------------------------------------------
6654 if (line == nullptr) line = new randolf::rline("", __eol);
6656 // --------------------------------------------------------------------------
6657 // Append received line of data to line, and return it.
6658 // --------------------------------------------------------------------------
6660 __recvline(str, buf_size, posix_flags, timeout, recvline_flags);
6661 line->assign(str + __eol_consumed_seq);
6664 } // -x- randolf::rline* recv_rline -x-
6666 /*======================================================================*//**
6668 This method is the same as the @ref recv_rline() method, except that it also
6669 appends data to an existing @ref randolf::rline line. If the line provided
6670 is longer than @c nbytes bytes, then the line will be returned as is, without
6671 any attempts made to receive more data nor to search for an EoL sequence.
6674 It's important to make sure that the EoL sequence is the same for both this
6675 rsocket object and for the rline object provided, otherwise the results could
6678 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
6679 @throws randolf::rex::xEBADF The underlying socket is not open
6680 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6682 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6683 part of the user address space
6684 @throws randolf::rex::xEINTR Interrupted by a signal
6685 @throws randolf::rex::xEINVAL Invalid argument passed
6686 @throws randolf::rex::xENOMEM Insufficient memory
6687 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6688 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6689 doesn't refer to a socket
6690 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
6691 no EoL character sequence is detected after recvline's buffer became
6692 full (whatever data is waiting may still be received using any of the
6693 recv_ methods {with or without the @c MSG_PEEK flag set}, including
6694 @c recvline() with a larger buffer {see the @c nbytes parameter})
6695 @throws randolf::rex::xERANGE if the timeout parameter is below 0
6696 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6699 @returns One line of text as a randolf::rline object.
6702 @see eol_consumed_seq()
6706 @see send_rline(const randolf::rline, const int)
6707 @see sendline(const std::string, const int)
6709 @see timeout_recvline
6710 @see timeout_recvline(long)
6712 *///=========================================================================
6713 randolf::rline& recv_rline_append_to(
6714 /// Where to append the data
6715 randolf::rline& line,
6716 /// Maximum number of bytes to receive (including EoL character sequence)
6717 const size_t nbytes = 0,
6722 /// MSG_CMSG_CLOEXEC
6723 const int posix_flags = 0,
6724 /// Line timeout (in seconds)@n
6725 /// 0 = no timeout (default), unless it was configured by way of the
6726 /// @ref timeout_recvline(long) method
6728 /// Configuration parameters
6729 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
6730 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
6731 // + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF; TODO: Add an option for this
6732 // Internally, the length of the @ref eol() sequence is added to the buffer size
6733 // so that the maximum line length without EoL characters matches the maximum
6734 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
6735 if (__debug) debug("recv_rline_append_to(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6736 + ", <randolf::rline>"
6737 + ", " + std::to_string(buf_size)
6738 + ", " + std::to_string(posix_flags)
6739 + ", " + std::to_string(timeout)
6740 + ", " + std::to_string(recvline_flags)
6743 // --------------------------------------------------------------------------
6744 // Append received line of data to line, and return it.
6745 // --------------------------------------------------------------------------
6746 if (line.size(true) < nbytes) {
6747 std::string str(line.get(true));
6748//std::cout << "str[0]=" << str << std::endl; // Debug (for advanced edge cases, which has been resolved)
6749 __recvline(str, buf_size, posix_flags, timeout, recvline_flags);
6750//std::cout << "str[1]=" << str << std::endl; // Debug (for advanced edge cases, which has been resolved)
6751 line.assign(str + __eol_consumed_seq);
6752 } // -x- if line.sizeI(true) -x-
6755 } // -x- randolf::rline* recv_rline_append_to -x-
6757 /*======================================================================*//**
6759 Receive a data structure from the endpoint.
6761 MSB/LSB considerations are important for any integers within your structure,
6762 so be sure to sanitize them with htons(), ntohs(), and related methods prior
6763 to sending, and then after receiving. This way, your data will be protected
6764 against corruption resulting from byte order differences when communicating
6765 between hardware architectures that differ in MSB and LSB byte ordering.
6767 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6768 reception, but it's important to note that it does implement temporary
6769 blocking while waiting for data.
6771 @throws randolf::rex::xEBADF The underlying socket is not open
6772 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6774 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6775 part of the user address space
6776 @throws randolf::rex::xEINTR Interrupted by a signal
6777 @throws randolf::rex::xEINVAL Invalid argument passed
6778 @throws randolf::rex::xENOMEM Insufficient memory
6779 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6780 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6781 doesn't refer to a socket
6782 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6785 @returns Data structure
6788 *///=========================================================================
6789 template <typename T> T recv_struct(
6794 /// MSG_CMSG_CLOEXEC
6795 const int posix_flags = 0) {
6797 if (__debug) debug("recv_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6799 + ", " + std::to_string(sizeof(buf))
6800 + ", " + std::to_string(posix_flags)
6802 __recv(&buf, sizeof(buf), posix_flags);
6804 } // -x- T recv_struct -x-
6806 /*======================================================================*//**
6808 Receive one 16-bit unsigned integer of data in LSB (little endian) order from
6811 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
6812 reception, which implements temporary blocking while waiting for data.
6814 @throws randolf::rex::xEBADF The underlying socket is not open
6815 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6817 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6818 part of the user address space
6819 @throws randolf::rex::xEINTR Interrupted by a signal
6820 @throws randolf::rex::xEINVAL Invalid argument passed
6821 @throws randolf::rex::xENOMEM Insufficient memory
6822 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6823 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6824 doesn't refer to a socket
6825 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6828 @returns uint16_t (converted to local endianness)
6829 @see recv_uint16_msb
6830 @see send_uint16_lsb
6832 *///=========================================================================
6833 uint16_t recv_uint16_lsb(
6838 /// MSG_CMSG_CLOEXEC
6839 const int posix_flags = 0) {
6841 if (__debug) debug("recv_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6843 + ", " + std::to_string(sizeof(buf))
6844 + ", " + std::to_string(posix_flags)
6846 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6847 return !__endian_is_msb ? buf : ntohs(buf);
6848 } // -x- uint16_t recv_uint16_lsb -x-
6850 /*======================================================================*//**
6852 Receive one 16-bit unsigned integer of data in MSB (big endian) order from
6855 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
6856 reception, which implements temporary blocking while waiting for data.
6858 @throws randolf::rex::xEBADF The underlying socket is not open
6859 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6861 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6862 part of the user address space
6863 @throws randolf::rex::xEINTR Interrupted by a signal
6864 @throws randolf::rex::xEINVAL Invalid argument passed
6865 @throws randolf::rex::xENOMEM Insufficient memory
6866 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6867 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6868 doesn't refer to a socket
6869 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6872 @returns uint16_t (converted to local endianness)
6873 @see recv_uint16_lsb
6874 @see send_uint16_msb
6876 *///=========================================================================
6877 uint16_t recv_uint16_msb(
6882 /// MSG_CMSG_CLOEXEC
6883 const int posix_flags = 0) {
6885 if (__debug) debug("recv_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6887 + ", " + std::to_string(sizeof(buf))
6888 + ", " + std::to_string(posix_flags)
6890 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6891 return __endian_is_msb ? buf : htons(buf);
6892 } // -x- uint16_t recv_uint16_msb -x-
6894 /*======================================================================*//**
6896 Receive one 32-bit unsigned integer of data in LSB (little endian) order from
6899 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
6900 reception, which implements temporary blocking while waiting for data.
6902 @throws randolf::rex::xEBADF The underlying socket is not open
6903 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6905 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6906 part of the user address space
6907 @throws randolf::rex::xEINTR Interrupted by a signal
6908 @throws randolf::rex::xEINVAL Invalid argument passed
6909 @throws randolf::rex::xENOMEM Insufficient memory
6910 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6911 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6912 doesn't refer to a socket
6913 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6916 @returns uint32_t (converted to local endianness)
6917 @see recv_uint32_msb
6918 @see send_uint32_lsb
6920 *///=========================================================================
6921 uint32_t recv_uint32_lsb(
6926 /// MSG_CMSG_CLOEXEC
6927 const int posix_flags = 0) {
6929 if (__debug) debug("recv_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6931 + ", " + std::to_string(sizeof(buf))
6932 + ", " + std::to_string(posix_flags)
6934 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6935 return !__endian_is_msb ? buf : htonl(buf);
6936 } // -x- uint32_t recv_uint32_lsb -x-
6938 /*======================================================================*//**
6940 Receive one 32-bit unsigned integer of data in MSB (big endian) order from
6943 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
6944 reception, which implements temporary blocking while waiting for data.
6946 @throws randolf::rex::xEBADF The underlying socket is not open
6947 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6949 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6950 part of the user address space
6951 @throws randolf::rex::xEINTR Interrupted by a signal
6952 @throws randolf::rex::xEINVAL Invalid argument passed
6953 @throws randolf::rex::xENOMEM Insufficient memory
6954 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6955 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6956 doesn't refer to a socket
6957 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6960 @returns uint32_t (converted to local endianness)
6961 @see recv_uint32_lsb
6962 @see send_uint32_msb
6964 *///=========================================================================
6965 uint32_t recv_uint32_msb(
6970 /// MSG_CMSG_CLOEXEC
6971 const int posix_flags = 0) {
6973 if (__debug) debug("recv_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6975 + ", " + std::to_string(sizeof(buf))
6976 + ", " + std::to_string(posix_flags)
6978 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6979 return __endian_is_msb ? buf : ntohl(buf);
6980 } // -x- uint32_t recv_uint32_msb -x-
6982 /*======================================================================*//**
6984 Receive one 64-bit unsigned integer of data in LSB (little endian) order from
6987 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
6988 reception, which implements temporary blocking while waiting for data.
6990 @throws randolf::rex::xEBADF The underlying socket is not open
6991 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6993 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6994 part of the user address space
6995 @throws randolf::rex::xEINTR Interrupted by a signal
6996 @throws randolf::rex::xEINVAL Invalid argument passed
6997 @throws randolf::rex::xENOMEM Insufficient memory
6998 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6999 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7000 doesn't refer to a socket
7001 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7004 @returns uint64_t (converted to local endianness)
7005 @see recv_uint64_msb
7006 @see send_uint64_lsb
7008 *///=========================================================================
7009 uint64_t recv_uint64_lsb(
7014 /// MSG_CMSG_CLOEXEC
7015 const int posix_flags = 0) {
7017 if (__debug) debug("recv_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7019 + ", " + std::to_string(sizeof(buf))
7020 + ", " + std::to_string(posix_flags)
7022 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
7023 return !__endian_is_msb ? buf : ((((uint64_t)ntohl((buf) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((buf) >> 32)));
7024 } // -x- uint64_t recv_uint64_lsb -x-
7026 /*======================================================================*//**
7028 Receive one 64-bit unsigned integer of data in MSB (big endian) order from
7031 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
7032 reception, which implements temporary blocking while waiting for data.
7034 @throws randolf::rex::xEBADF The underlying socket is not open
7035 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7037 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7038 part of the user address space
7039 @throws randolf::rex::xEINTR Interrupted by a signal
7040 @throws randolf::rex::xEINVAL Invalid argument passed
7041 @throws randolf::rex::xENOMEM Insufficient memory
7042 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7043 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7044 doesn't refer to a socket
7045 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7048 @returns uint64_t (converted to local endianness)
7049 @see recv_uint64_lsb
7050 @see send_uint64_msb
7052 *///=========================================================================
7053 uint64_t recv_uint64_msb(
7058 /// MSG_CMSG_CLOEXEC
7059 const int posix_flags = 0) {
7061 if (__debug) debug("recv_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7063 + ", " + std::to_string(sizeof(buf))
7064 + ", " + std::to_string(posix_flags)
7066 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
7067 return __endian_is_msb ? buf : ((((uint64_t)htonl((buf) & 0xffffffffUL)) << 32) | htonl((uint32_t)((buf) >> 32)));
7068 } // -x- uint64_t recv_uint64_msb -x-
7070 /*======================================================================*//**
7072 Receive data from a specific endpoint.
7075 The @c MSG_WAITALL flag is particularly useful for preventing premature data
7076 reception, but it's important to note that it does implement temporary
7077 blocking while waiting for data.
7080 This method is not compatible with TLS.
7082 @throws randolf::rex::xEBADF The underlying socket is not open
7083 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7085 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7086 part of the user address space
7087 @throws randolf::rex::xEINTR Interrupted by a signal
7088 @throws randolf::rex::xEINVAL Invalid argument passed
7089 @throws randolf::rex::xENOMEM Insufficient memory
7090 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7091 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7092 doesn't refer to a socket
7093 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7096 @returns Appropriately-sized vector of characters
7099 *///=========================================================================
7100 std::vector<char>* recvfrom(
7101 /// Maximum number of bytes to receive
7102 const size_t nbytes,
7107 /// MSG_CMSG_CLOEXEC
7108 const int posix_flags,
7109 /// Target endpoint address structure
7110 struct sockaddr *from,
7111 /// Size of target endpoint structure
7112 socklen_t fromlen = sizeof(sockaddr)) {
7113 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
7114 if (__debug) debug("recvfrom(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7116 + ", " + std::to_string(buf_size)
7117 + ", " + std::to_string(posix_flags)
7119 + ", " + std::to_string(fromlen)
7121 std::vector<char>* v = new std::vector<char>(buf_size);
7122 v->resize(__track_bytes_rx(__rc_check(::recvfrom(__socket_fd, v->data(), v->size(), posix_flags, from, &fromlen))));
7124 } // -x- std::vector<char>* recvfrom -x-
7127 /*======================================================================*//**
7129 This is an internal function that:
7130 1. receives data from the endpoint:
7131 - via underlying socket when TLS is not enabled
7132 - via the OpenSSL socket API when TLS is enabled
7133 2. checks for a socket I/O error (and throws an exception)
7134 3. tracks number of bytes received (if there were no errors)
7137 The ring buffer will only be used if there is one. The @ref __recvline
7138 method is one method that instantiates the internal ring buffer, and there
7139 could be others in the future (although this is not strongly anticipated
7140 aside from a method that allows the software developer to explicitly choose
7141 to have a buffer active for all socket-read operations).
7143 @throws randolf::rex::xEAGAIN The underlying socket timed out
7144 @throws randolf::rex::xEBADF The underlying socket is not open
7145 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7147 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7148 part of the user address space
7149 @throws randolf::rex::xEINTR Interrupted by a signal
7150 @throws randolf::rex::xEINVAL Invalid argument passed
7151 @throws randolf::rex::xENOMEM Insufficient memory
7152 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7153 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7154 doesn't refer to a socket
7155 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7158 @returns Number of bytes that were successfully received
7161 *///=========================================================================
7165 /// Maximum number of bytes to receive into @c data array
7167 /// Flags (ignored with encrypted streams, except for the MSG_PEEK flag)
7168 const int posix_flags = 0) {
7170 // --------------------------------------------------------------------------
7171 // When internal buffering is not set up (which is the default operation), we
7172 // simply pass data directly.
7173 // --------------------------------------------------------------------------
7174 if (__buffer == nullptr) {
7175 return __tls ? (posix_flags & MSG_PEEK ? __rc_check_tls(SSL_peek(__tls_fd, data, len))
7176 : __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, data, len))))
7177 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, data, len, posix_flags)));
7178 } // -x- if !__buffer -x-
7180 // --------------------------------------------------------------------------
7181 // If the amount of data contained in the internal buffer is sufficient to
7182 // satisfy the amount of data that "len" represents, then all that's needed
7183 // is to return "len" bytes from the ring buffer without having to receive
7184 // more data from the socket.
7185 // --------------------------------------------------------------------------
7186 if (len <= __buffer->get_utilized()) {
7187 if (posix_flags & MSG_PEEK) __buffer->peek_to_array( (size_t)len, (char*)data); // Copy character(s) from ring buffer because MSG_PEEK flag is set
7188 else __buffer->remove_to_array((size_t)len, (char*)data); // Consume character(s) from ring buffer
7192 // --------------------------------------------------------------------------
7193 // Consume data from socket to make up for OpenSSL's inability to read beyond
7194 // one packet of data in its SSL_peek() and SSL_read() methods, and also some
7195 // inconsistencies with certain raw (unencrypted) socket implementations that
7196 // occasionally refuse to read beyond one packet of data (at least on a first
7197 // attempt to read with or without the MSG_PEEK flag).
7199 // Fill internal buffer. It may be more than len is set to, but this is okay
7200 // because we're committed to using internal buffering now anyway (it's far
7201 // more likely that a protocol that requires the use of recvline() even once
7202 // is going to be using it repeatedly, but as long as the ring buffer is
7203 // large enough to cover the maximum length of a line of text, including its
7204 // EoL sequence, then this will always succeed).
7205 // --------------------------------------------------------------------------
7206 __buffer->defragment(true);
7207 __buffer_bam = __buffer->data_bam(__buffer_bam); // Update ring buffer setup (will be allocated automatically if "nullptr" default)
7208 size_t available = __buffer->get_available();
7209 int n = 0; // Number of bytes
7210 if (__tls) n = __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, __buffer_bam->avail1, std::min(__buffer_bam->avail1_size, (size_t)len)))); // Not using SSL_peek() at all here because we need to consume this data as we stuff it into the internal buffer
7211 else n = __track_bytes_rx(__rc_check( ::recv(__socket_fd, __buffer_bam->avail1, std::min(__buffer_bam->avail1_size, (size_t)len), posix_flags & (~MSG_PEEK)))); // Mask the MSG_PEEK flag because we need to consume this data as we stuff it into the internal buffer
7212 __buffer->set_head(n, false); // Add number of bytes received to the ring buffer after receiving directly into the available portion of the ring buffer
7214 // --------------------------------------------------------------------------
7215 // Copy or move data depending on whether POSIX flag MSG_PEEK was set.
7216 // --------------------------------------------------------------------------
7217 n = std::min(len, (int)__buffer->get_utilized()); // Work with the lesser number of bytes
7218 if (n > 0) { // At least 1 byte is available
7219 if (posix_flags & MSG_PEEK) __buffer->peek_to_array( (size_t)n, (char*)data); // Copy character(s) from ring buffer because MSG_PEEK flag is set
7220 else __buffer->remove_to_array((size_t)n, (char*)data); // Consume character(s) from ring buffer
7222 return n; // Return the number of bytes that were copied/moved
7224 } // -x- int __recv -x-
7226 /*======================================================================*//**
7228 This is an internal function that:
7229 1. receives a line of data from the endpoint:
7230 - via underlying socket when TLS is not enabled
7231 - via the OpenSSL socket API when TLS is enabled
7232 2. checks for a socket I/O error (and throws an exception)
7233 3. tracks number of bytes received (if there were no errors)
7234 4. isolates the EoL sequence from the line of data, and records it for
7235 later reference (for those cases where it's needed)
7238 The two exceptions that need to be handled are @ref randolf::rex::xEOVERFLOW
7239 (when the endpoint sends oversized lines) and @ref randolf::rex::xETIMEDOUT
7240 (when the endpoint takes too long to respond) for a robust usage.
7242 @throws randolf::rex::xEBADF The underlying socket is not open
7243 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7245 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7246 part of the user address space
7247 @throws randolf::rex::xEINTR Interrupted by a signal
7248 @throws randolf::rex::xEINVAL Invalid argument passed
7249 @throws randolf::rex::xENOMEM Insufficient memory
7250 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7251 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7252 doesn't refer to a socket
7253 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
7254 no EoL character sequence is detected after recvline's buffer became
7255 full (whatever data is waiting may still be received using any of the
7256 recv_ methods {with or without the @c MSG_PEEK flag set}, including
7257 @c recvline() with a larger buffer {see the @c nbytes parameter})
7258 @throws randolf::rex::xERANGE if the timeout parameter is below 0
7259 @throws randolf::rex::xETIMEDOUT If a @c timeout occurs
7262 *///=========================================================================
7263 std::string& __recvline(
7264 /// Where to append the data
7266 /// Maximum number of bytes to receive (including EoL character sequence) @n
7267 /// @c 0 (or below) = "buf" will be returned as is@n
7268 /// if this quantity is larger than @c buf.size() then an exception will be
7269 /// thrown before any socket/internal-buffer read attempts occur
7272 /// MSG_PEEK (with some limitations)@n
7274 /// MSG_DONTWAIT (ineffective when @c timeout is not 0)@n
7275 /// MSG_CMSG_CLOEXEC
7276 const int posix_flags,
7277 /// Line timeout (in seconds)@n
7278 /// 0 = no timeout (default), unless it was configured by way of the
7279 /// @ref timeout_recvline(long) method
7280 const ulong timeout,
7281 /// Configuration parameters
7282 const int recvline_flags) {
7284 // --------------------------------------------------------------------------
7285 // Prevent undefined behaviour (I don't like wasting CPU cycles, but line
7286 // reading does require more processing).
7287 // --------------------------------------------------------------------------
7288 if (buf_size <= 0) { // Buffer is empty or has an unrealistic size that can be silently ignored
7289 __eol_consumed_seq.clear(); // Indicate that there's no EoL sequence
7290 return buf; // Return "buf" as is
7291 } // -x- if buf_size -x-
7293 // --------------------------------------------------------------------------
7294 // Internal variables.
7295 // --------------------------------------------------------------------------
7296 const int old_size = buf.size();
7297 if (old_size >= buf_size) throw new randolf::rex::xEOVERFLOW("Pre-defined line is too large"); //return buf;
7298 const int recv_size = buf_size - old_size; // This will not be below 1
7300 // --------------------------------------------------------------------------
7301 // Pre-allocate data so we can write to it directly.
7302 // --------------------------------------------------------------------------
7303 buf.resize(buf_size + old_size);
7305 // --------------------------------------------------------------------------
7306 // Perform a highly specialized check for whether the supplied buffer already
7307 // completes an EoL sequence. If it does, then stop now and return it as is
7308 // with one byte discarded from the stream (if needed; it almost always will
7310 // --------------------------------------------------------------------------
7313 // --------------------------------------------------------------------------
7314 // Use exceptions to handle timeouts and generate randolf::rex::xETIMEDOUT,
7315 // which is helpful because timeout errors vary on different Operating
7316 // Systems, systems configurations, and in different socket implementations.
7317 // --------------------------------------------------------------------------
7320 // --------------------------------------------------------------------------
7321 // Peek ahead at the first character, just in case it's the second part of an
7322 // EoL sequence (this ensures that automatic determination will also work).
7323 // --------------------------------------------------------------------------
7324 __recv(buf.data() + old_size, 1, posix_flags | MSG_PEEK | MSG_WAITALL); // Read-ahead one character (one byte), or else timeout
7326 } catch (const randolf::rex::xEAGAIN& e) { // Socket timeout derived from EAGAIN error
7327 throw randolf::rex::xETIMEDOUT("randolf::rex::xEAGAIN");
7328 } catch (const randolf::rex::xEWOULDBLOCK& e) { // Socket timeout derived from EWOULDBLOCK error
7329 throw randolf::rex::xETIMEDOUT("randolf::rex::xEWOULDBLOCK");
7330 } catch (const randolf::rex::xTLS_SYSCALL& e) { // Socket timeout derived from TLS_SYSCALL error
7331 throw randolf::rex::xETIMEDOUT("randolf::rex::xTLS_SYSCALL");
7332 //} catch (const std::exception& e) { // Debug
7333 // std::cout << "Unknown error " << e.what() << std::endl;
7337 // --------------------------------------------------------------------------
7338 // If EoL is found, then update internal variables and return the buffer
7339 // after truncating it to exclude the EoL sequence characters (and tracking
7340 // of the EoL sequence as is the expected behaviour).
7341 // --------------------------------------------------------------------------
7343 int len_with_eol = 0;
7344 if ((len = eol_index(buf, &len_with_eol)) >= 0) { // EoL found, so we're done now (-1 == no EoL found)
7345 int old_size = buf.size(); // Update buffer size so that final discard can be last before return in this code block
7346 __eol_consumed_seq = buf.substr(len, len_with_eol - len); // Save EoL sequence
7347 buf.resize(len); // Truncate string without the EoL sequence
7348 if (old_size == len_with_eol) discard(1); // Discard the character we peeked ahead at if it completed the EoL sequence detection
7350 } // -x- if eol_index -x-
7352 // --------------------------------------------------------------------------
7353 // Remove extra character we appended to "buf" earlier because it's not as
7354 // simple if we have to adjust and track the number of bytes to read next.
7355 // --------------------------------------------------------------------------
7356 //buf.resize(buf.size() - 1); // We don't do this in this case because of pre-resizing
7358 } // -x if old_size -x-
7360 // --------------------------------------------------------------------------
7361 // If an internal buffer, which is used by __recv, hasn't been instantiated,
7362 // then instantiate it and save the pointer to it for future use by __recv().
7364 // Buffering becomes necessary with TLS connection because SSL_peek fails to
7365 // return data that spans multiple packets. What triggers this behaviour is
7366 // the command "stty -icanon && openssl s_client host_name tcp_port" followed
7367 // by manual interactive typing some text then pressing the "[Enter]" key.
7369 // The ::malloc(__buffer_size) won't be leaked because it will be freed in
7370 // in this rsocket's destructor (if it isn't freed elsewhere before this).
7371 // --------------------------------------------------------------------------
7372 if (__buffer == nullptr) __buffer = new rring(__buffer_size);
7374 // --------------------------------------------------------------------------
7375 // Calculate timeout_target for easy comparisons with time(0) in the loop,
7376 // and other internal variables.
7377 // --------------------------------------------------------------------------
7378 ulong timeout_target = timeout == 0
7379 ? (__recvline_timeout == 0 ? ULONG_MAX : __recvline_timeout + time(0))
7380 : timeout + time(0);
7381 int br = 0; // Quantity of Bytes Received
7382 int len = -1; // Length of string before EoL sequence (-1 = no EoL sequence)
7383 int len_with_eol = 0; // Length of string including the EoL sequence (which may be 0 or 1 or 2)
7385 // --------------------------------------------------------------------------
7386 // Take advantage of RAII in C++ to ensure that the non-recvline timeout
7387 // settings will be restored regardless of a normal return or throwing any
7389 // --------------------------------------------------------------------------
7390 RAII_timeout raii(*this, timeout);
7392 // --------------------------------------------------------------------------
7393 // Line-reading loop. Unlike a regular recv() call, the recvline() method
7394 // attempts to read an entire line of text, and it's possible that this line
7395 // of text is sent across multiple packets, including half-way between a CRLF
7396 // EoL sequence, so we either stop upon encountering the desired EoL sequence
7397 // (be it one or two characters long), or we read all the way to the end of
7398 // the maximum line length and then throw rex::xEOVERFLOW exception if no
7399 // EoL sequence was encountered, or rex::xETIMEDOUT exception if the allotted
7400 // recvline_timeout() time expired (and this particular timeout can actually
7401 // be longer than the non-line-receiving per-character timeout).
7402 // --------------------------------------------------------------------------
7405 // --------------------------------------------------------------------------
7406 // Use exceptions to handle timeouts and generate randolf::rex::xETIMEDOUT,
7407 // which is helpful because timeout errors vary on different Operating
7408 // Systems, systems configurations, and in different socket implementations.
7409 // --------------------------------------------------------------------------
7412 // --------------------------------------------------------------------------
7413 // Attempt to read data, but without consuming it. The reason for this is
7414 // that we need to scan the data for an EoL sequence, but if there isn't one
7415 // then we need to try again while waiting for more data. (The current data
7416 // will be buffered by __recv so we will actually be waiting for more data to
7417 // to arrive from the endpoint.)
7418 // --------------------------------------------------------------------------
7419//std::cout << "br0=" << br << " old_size=" << old_size << " buf_size=" << buf_size << std::endl; // Debug
7420 br = __recv(buf.data() + old_size, recv_size, posix_flags | MSG_PEEK); // Look-ahead at socket stream (with buffer size that was inflated earlier to include EoL sequence)
7421//std::cout << "br1=" << br << " old_size=" << old_size << " buf_size=" << buf_size << std::endl; // Debug
7423 } catch (const randolf::rex::xEAGAIN& e) { // Socket timeout derived from EAGAIN error
7424 throw randolf::rex::xETIMEDOUT("randolf::rex::xEAGAIN");
7425 } catch (const randolf::rex::xEWOULDBLOCK& e) { // Socket timeout derived from EWOULDBLOCK error
7426 throw randolf::rex::xETIMEDOUT("randolf::rex::xEWOULDBLOCK");
7427 } catch (const randolf::rex::xTLS_SYSCALL& e) { // Socket timeout derived from TLS_SYSCALL error
7428 throw randolf::rex::xETIMEDOUT("randolf::rex::xTLS_SYSCALL");
7429 //} catch (const std::exception& e) { // Debug
7430 // std::cout << "Unknown error " << e.what() << std::endl;
7434 // --------------------------------------------------------------------------
7435 // If EoL sequence found, save the consumed EoL sequence and return the line.
7436 // --------------------------------------------------------------------------
7437 if ((len = eol_index(buf, &len_with_eol)) >= 0) { // EoL found, so we're done now (-1 == no EoL found)
7438//std::cout << "Found // EoL: br=" << br << " len=" << len << " len_with_eol=" << len_with_eol << " eol.size=" << __eol.size() << " buf_size=" << buf_size << " old_size=" << old_size << " buf=[" << buf.data() << "]" << std::endl;
7439// __eol_consumed_seq = buf.substr(len, len_with_eol - len); // Save EoL sequence (copy it from the string) // TODO: Optimize this
7440 __eol_consumed_seq.assign(buf, len, len_with_eol - len); // Save EoL sequence (copy it from the string); "len" serves as the position here
7441 buf.resize(len); // Truncate string without the EoL sequence
7442 if (!(posix_flags & MSG_PEEK)) discard(len_with_eol - old_size); // Discard all characters that were added to the buffer
7445//std::cout << "Not found // EoL: br=" << br << " len=" << len << " len_with_eol=" << len_with_eol << " eol.size=" << __eol.size() << " buf_size=" << buf_size << " old_size=" << old_size << " buf=[" << buf.data() << "]" << std::endl;
7447 // --------------------------------------------------------------------------
7448 // If EoS wasn't found and the maximum line length has been reached, then we
7449 // need to end this loop so that we can handle any recvline_flags and/or
7450 // throw an xEOVERFLOW exception.
7451 // --------------------------------------------------------------------------
7452 } while (br != recv_size); // Loop again if buffer isn't full yet
7454 // --------------------------------------------------------------------------
7455 // If a timeout occurred, then throw the randolf::rex::xETIMEDOUT exception.
7456 // --------------------------------------------------------------------------
7458 if (time(0) > timeout_target)
7459 throw randolf::rex::xETIMEDOUT("recvline timed out"); // We reached the line timeout target
7461 // ==========================================================================
7462 // We've exited the loop, and now we're deciding how to handle a full buffer
7463 // that doesn't have an EoL, and obviously indicates line-overflow situation.
7464 // ==========================================================================
7466 // --------------------------------------------------------------------------
7467 // Caller wants partial data instead of throwing an exception, even if it has
7469 // --------------------------------------------------------------------------
7470 if (recvline_flags & RECVLINE_PARTIAL) {
7471 __eol_consumed_seq.clear(); // Indicate that there's no EoL sequence
7472 if (!(posix_flags & MSG_PEEK)) discard(br);
7473 buf.resize(br + old_size); // Truncate string to "br" (bytes received) in addition to the original buffer's bytes
7474//std::cout << "No EoL // br=" << br << " buf=[" << buf.data() << "]" << std::endl; // Debug
7476 } // -x- if RECVLINE_PARTIAL -x-
7478 // --------------------------------------------------------------------------
7479 // Generate xEOVERFLOW exception.
7481 // Discarded data is limited to the maximum length of the line that could be
7482 // received if the RECVLINE_LIMIT_DISCARD flag is set, otherwise the amount
7483 // of data that can be discarded will run beyond the maximum length.
7485 // TODO: Throw a different exception for no EoL when the buffer isn't full.
7486 // --------------------------------------------------------------------------
7487 if (!(recvline_flags & RECVLINE_NO_DISCARD_ON_OVERFLOW) && !(posix_flags & MSG_PEEK)) {
7488 if (recvline_flags & RECVLINE_LIMIT_DISCARD) {
7489 discard(br); // Consume only data that was received (which should always just be the maximum line length)
7491 discard_line(); // Consume remaining data beyond maximum line length (this is the default behaviour)
7493 } // -x- if !RECVLINE_NO_DISCARD_ON_OVERFLOW -x-
7495 throw randolf::rex::xEOVERFLOW("recvline overflow (line too long or missing EoL sequence)");
7496 } // -x- std::string& __recvline -x-
7499 /*======================================================================*//**
7501 Receive a line of data from the endpoint, with the EoL character(s) removed.
7502 While this is meant for ASCII and UTF-8 text, it will also work with binary
7503 data that doesn't include EoL character sequences as non-line-ending data
7504 (when receiving binary data, the @ref recv() and @ref recvz() methods tend to
7508 Be sure to read the docuemntation for the @c eol and @c eol_adoption methods,
7509 especially if you're using @c recvline or @c recv_rline to implement an
7510 internet daemon, because pre-configuring the EoL sequence is an important
7511 consideration for some protocols.
7513 In the following example, you can use the command `telnet 127.0.0.1 1024` to
7514 connect to the complied code once it's running, and then issue the commands
7515 "exit" or "quit" to end the session, or "down" to terminate the daemon:
7518 #include <iostream> // std::cout, std::cerr, and friends
7519 #include <string> // std::string
7520 #include <randolf/rex>
7521 #include <randolf/rsocket>
7525 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
7526 r.bind("127.0.0.1", 1024);
7529 bool running = true;
7531 std::cout << "Waiting for connections on 127.0.0.1:1024" << std::endl;
7532 std::unique_ptr<randolf::rsocket> c = r.accept_up();
7534 c->sendline("+-----------------+", MSG_MORE)
7535 .sendline("| === Welcome === |", MSG_MORE)
7536 .sendline("+-----------------+");
7540 std::string line = c->recvline(); // <-- You are here
7542 if (line == "exit" || line == "quit") break;
7543 else if (line == "down") running = false;
7544 else if (line.empty()) continue;
7545 else c->send("You wrote \"")
7549 } // -x- while true -x-
7551 } // -x- while running -x-
7554 } catch (const randolf::rex::xEADDRINUSE& e) {
7555 std::cerr << "Could not bind IP address: " << e.what() << std::endl;
7556 return EXIT_FAILURE;
7557 } catch (const randolf::rex::xALL& e) {
7558 std::cerr << "Socket exception: " << e.what() << std::endl;
7559 return EXIT_FAILURE;
7560 } catch (const std::exception& e) {
7561 std::cerr << "Other exception: " << e.what() << std::endl;
7562 return EXIT_FAILURE;
7565 return EXIT_SUCCESS;
7566 } // -x- int main -x-
7569 If @c nbytes is 0, then the internal buffer_size will be used as the default,
7570 which can also be changed from its compiled-in default of 1024 by using one
7571 of the buffer_size() methods. The total number of bytes received, including
7572 the EoL sequence, will not exceed the total number of byte specified with the
7573 @c nbytes parameter.
7575 For @c nbytes the EoL character sequence size should be included, especially
7576 for shorter lines -- if the EoL character sequence is detected automatically
7577 on-the-fly (which is the default), then provisioning 2 characters is needed
7578 since an EoL sequence could be 1 or 2 characters long (line lengths shorter
7579 than 3 characters could be particularly problematic when detecting on-the-fly
7580 EoL character sequences). In such a case, a better approach is to allow for
7581 2 additional characters and then test the length of the returned string to
7582 ensure it doesn't exceed whatever the required maximum is (and throw the same
7583 @ref randolf::rex::xEOVERFLOW exception if the length is exceeded).
7585 For more information about which characters an EoL sequence is comprised of,
7586 and how our specialized support for multiple EoL sequences works, see the
7587 documentation for the various @ref eol methods.
7590 When setting the recvline timeout (either with the @c timeout parameter with
7591 this method, or by using the @ref timeout_recvline(long) method), you may
7592 also need to set the socket timeout beforehand using the @ref timeout()
7593 method, because @c recvline doesn't do this...
7596 @ref timeout_recvline sets the overall total timeout for an entire line to
7599 @ref timeout sets the timeout between individual characters received (such
7600 as keystrokes from a live end-user)
7603 If your socket timeout is longer than then recvline timeout, this will have
7604 the effect of rendering the recvling timeout as ineffective.
7606 The @c timeout parameter can be used to override the total overall timeout
7607 for receiving a line, which is useful for specific situations where a
7608 specific timeout is desired for particular prompts, during certain data entry
7612 When a line is not found (@c randolf::rex::xEAGAIN), or a line is too long
7613 because there's too much data (longer than a line) without an EoL sequence
7614 (@c randolf::rex::xOVERFLOW), then that data will not be discarded or
7615 consumed, and will remain ready for receiving (@ref recv) or for discarding
7619 If you're searching for a readline() method for socket I/O, then this is most
7620 likely what you're wanting/needing.
7622 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
7623 @throws randolf::rex::xEBADF The underlying socket is not open
7624 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7626 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7627 part of the user address space
7628 @throws randolf::rex::xEINTR Interrupted by a signal
7629 @throws randolf::rex::xEINVAL Invalid argument passed
7630 @throws randolf::rex::xENOMEM Insufficient memory
7631 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7632 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7633 doesn't refer to a socket
7634 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
7635 no EoL character sequence is detected after recvline's buffer became
7636 full (whatever data is waiting may still be received using any of the
7637 recv_ methods {with or without the @c MSG_PEEK flag set}, including
7638 @c recvline() with a larger buffer {see the @c nbytes parameter})
7639 @throws randolf::rex::xERANGE if the timeout parameter is below 0
7640 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7643 @returns One line of text as a std::string object.
7647 @see eol_consumed_seq()
7649 @see sendline(const std::string, const int)
7651 @see timeout_recvline
7652 @see timeout_recvline(long)
7654 *///=========================================================================
7655 std::string recvline(
7656 /// Maximum number of bytes to receive (including EoL character sequence)@n
7657 /// 0 = use internal @ref buffer_size()
7658 const size_t nbytes = 0,
7663 /// MSG_CMSG_CLOEXEC
7664 const int posix_flags = 0,
7665 /// Line timeout (in seconds)@n
7666 /// 0 = no timeout (default) or whatever @ref timeout was set to, unless it
7667 /// was configured by way of the @ref timeout_recvline(long) method
7669 /// Configuration parameters
7670 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
7671 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
7672 // + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF; TODO: Add an option for this
7673 // Internally, the length of the @ref eol() sequence is added to the buffer size
7674 // so that the maximum line length without EoL characters matches the maximum
7675 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
7676 if (__debug) debug("recvline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7677 + ", " + std::to_string(buf_size)
7678 + ", " + std::to_string(posix_flags)
7679 + ", " + std::to_string(timeout)
7680 + ", " + std::to_string(recvline_flags)
7683 __recvline(str, buf_size, posix_flags, timeout, recvline_flags);
7685 } // -x- std::string recvline -x-
7687 /*======================================================================*//**
7689 This method is the same as the @ref recvline() method, except that it also
7690 appends data to an existing std::string line. If the line provided is longer
7691 than @c nbytes bytes, then the line will be returned as is without any
7692 attempts made to receive more data nor to search for an EoL sequence
7694 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
7695 @throws randolf::rex::xEBADF The underlying socket is not open
7696 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7698 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7699 part of the user address space
7700 @throws randolf::rex::xEINTR Interrupted by a signal
7701 @throws randolf::rex::xEINVAL Invalid argument passed
7702 @throws randolf::rex::xENOMEM Insufficient memory
7703 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7704 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7705 doesn't refer to a socket
7706 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
7707 no EoL character sequence is detected after recvline's buffer became
7708 full (whatever data is waiting may still be received using any of the
7709 recv_ methods {with or without the @c MSG_PEEK flag set}, including
7710 @c recvline() with a larger buffer {see the @c nbytes parameter})
7711 @throws randolf::rex::xERANGE if the timeout parameter is below 0
7712 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7715 @returns One line of text as a std::string object.
7719 @see eol_consumed_seq()
7721 @see sendline(const std::string, const int)
7723 @see timeout_recvline
7724 @see timeout_recvline(long)
7726 *///=========================================================================
7727 std::string& recvline_append_to(
7728 /// Where to append the data
7730 /// Maximum number of bytes to receive (including EoL character sequence)@n
7731 /// 0 = use internal @ref buffer_size()
7732 const size_t nbytes = 0,
7737 /// MSG_CMSG_CLOEXEC
7738 const int posix_flags = 0,
7739 /// Line timeout (in seconds)@n
7740 /// 0 = no timeout (default) or whatever @ref timeout was set to, unless it
7741 /// was configured by way of the @ref timeout_recvline(long) method
7743 /// Configuration parameters
7744 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
7745 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
7746 // + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF; TODO: Add an option for this
7747 // Internally, the length of the @ref eol() sequence is added to the buffer size
7748 // so that the maximum line length without EoL characters matches the maximum
7749 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
7750 if (__debug) debug("recvline_append_to(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7752 + ", " + std::to_string(buf_size)
7753 + ", " + std::to_string(posix_flags)
7754 + ", " + std::to_string(timeout)
7755 + ", " + std::to_string(recvline_flags)
7757 if (line.size() < nbytes)
7758 __recvline(line, buf_size, posix_flags, timeout, recvline_flags);
7760 } // -x- std::string& recvline_append_to -x-
7762 /*======================================================================*//**
7764 Receive data in the form of a "msghdr" structure.
7766 This method is not compatible with TLS.
7768 @throws randolf::rex::xEBADF The underlying socket is not open
7769 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7771 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7772 part of the user address space
7773 @throws randolf::rex::xEINTR Interrupted by a signal
7774 @throws randolf::rex::xEINVAL Invalid argument passed
7775 @throws randolf::rex::xENOMEM Insufficient memory
7776 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7777 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7778 doesn't refer to a socket
7779 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7782 @returns pointer to "msghdr" structure
7785 *///=========================================================================
7787 /// Pointer to "msghdr" structure (or nullptr for automatic allocation)
7793 /// MSG_CMSG_CLOEXEC
7794 const int posix_flags = 0) {
7795 if (__debug) debug("recvmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7797 + ", " + std::to_string(posix_flags)
7799 if (msg == nullptr) msg = new msghdr{0};
7800 __track_bytes_rx(__rc_check(::recvmsg(__socket_fd, msg, posix_flags)));
7802 } // -x- msghdr* recvmsg -x-
7804 /*======================================================================*//**
7806 Receive data in the form of an "mmsghdr" structure.
7808 This method is not compatible with TLS.
7810 @throws randolf::rex::xEBADF The underlying socket is not open
7811 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7813 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7814 part of the user address space
7815 @throws randolf::rex::xEINTR Interrupted by a signal
7816 @throws randolf::rex::xEINVAL Invalid argument passed
7817 @throws randolf::rex::xENOMEM Insufficient memory
7818 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7819 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7820 doesn't refer to a socket
7821 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7824 @returns pointer to "mmsghdr" structure
7827 *///=========================================================================
7829 /// Pointer to "mmsghdr" structure (or nullptr for automatic allocation)
7830 struct mmsghdr* mmsg,
7831 /// Size of target endpoint structure
7832 const unsigned int vlen = sizeof(mmsghdr),
7838 const int posix_flags = 0,
7840 struct timespec* timeout = {0}) {
7841 if (__debug) debug("recvmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7843 + ", " + std::to_string(vlen)
7844 + ", " + std::to_string(posix_flags)
7845 + ", timeout{tv_sec=" + std::to_string(timeout->tv_sec)
7846 + ", tv_nsec=" + std::to_string(timeout->tv_nsec) + "}"
7848 __track_bytes_rx(__rc_check(::recvmmsg(__socket_fd, mmsg, vlen, posix_flags, timeout)));
7850 } // -x- mmsghdr* recvmmsg -x-
7852 /*======================================================================*//**
7854 Receive data from the endpoint, and add a 0 (null) onto the end. This is
7855 useful when using the resulting std::vector<char> as an ASCIIZ string.
7857 If nbytes is 0, then the internal buffer_size will be used as the default,
7858 which can also be changed from its compiled-in default of 1024 by using one
7859 of the buffer_size() methods.
7862 The resulting std::vector size is always inflated by 1. This means that
7863 relying on a comparison against 0 will result in an infinite loop:
7865 for (std::vector<char> v = r.recvz(); v.size > 0; v = recvz()) { ... }
7867 So, you'll need to compare against 1 instead of 0 to compensate fot the
7868 inflated size due to the addition of null character 0:
7870 for (std::vector<char> v = r.recvz(); v.size > 1; v = recvz()) { ... }
7874 The resulting size of std::vector<char> will be <tt>nbytes + 1</tt> which
7875 ensures that any string processing functions or presentation libraries - such
7876 as @c printf() - expecting an ASCIIZ string buffer will stop at null (0), and
7877 will reliably output no more than the maximum size specified.
7879 The way to think of this is that @c nbytes specifies the maximum number of
7880 bytes (a.k.a., characters) to recieve over the underlying socket, and the
7881 final 0 (null) being added is not included in the maximum specified by the
7882 @c nbytes parameter.
7885 The @c MSG_WAITALL flag is particularly useful for preventing premature data
7886 reception, but it's important to note that it does implement temporary
7887 blocking while waiting for data.
7889 @throws randolf::rex::xEBADF The underlying socket is not open
7890 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7892 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7893 part of the user address space
7894 @throws randolf::rex::xEINTR Interrupted by a signal
7895 @throws randolf::rex::xEINVAL Invalid argument passed
7896 @throws randolf::rex::xENOMEM Insufficient memory
7897 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7898 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7899 doesn't refer to a socket
7900 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7903 @returns appropriately-sized vector of characters + 1 (includes additional
7904 null terminator character 0)
7905 @see recv(const size_t, const int)
7906 @see recv(std::vector<char>, const int)
7909 *///=========================================================================
7910 std::vector<char>* recvz(
7911 /// Maximum number of bytes to receive
7912 const size_t nbytes = 0,
7917 /// MSG_CMSG_CLOEXEC
7918 const int posix_flags = 0) {
7919 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
7920 if (__debug) debug("recvz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7922 + ", " + std::to_string(buf_size)
7923 + ", " + std::to_string(posix_flags)
7925 std::vector<char>* v = new std::vector<char>(buf_size);
7926 v->resize(__recv(v->data(), v->size(), posix_flags) + 1); // Add 1 to include room for final null character 0
7927 v->at(v->size() - 1) = (char)0; // Set final element to 0 (null)
7929 } // -x- std::vector<char>* recvz -x-
7932 /*======================================================================*//**
7934 This is an internal function that:
7935 1. sends data to the endpoint:
7936 - via underlying socket when TLS is not enabled
7937 - via the OpenSSL socket API when TLS is enabled
7938 2. checks for a socket I/O error (and throws an exception)
7939 3. tracks number of bytes transmitted (if there were no errors)
7942 This method is threadsafe because the underlying POSIX send() and OpenSSL's
7943 SSL_write() functions are threadsafe.
7945 @throws randolf::rex::xEBADF The underlying socket is not open
7946 @throws randolf::rex::xECONNRESET Connect reset by peer
7947 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7948 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7949 part of the user address space
7950 @throws randolf::rex::xEINTR Interrupted by a signal
7951 @throws randolf::rex::xEINVAL Invalid argument passed
7952 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7953 occur, but the POSIX sockets documentation lists it as one of the
7954 errors that can be returned, perhaps because some incorrectly
7955 implemented TCP/IP stacks return this error?)
7956 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7957 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7958 and 65,527 bytes for IPv6)
7959 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7960 network congestion (or, less commonly, insufficient memory)
7961 @throws randolf::rex::xENOMEM Insufficient memory
7962 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7963 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7964 doesn't refer to a socket
7965 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7966 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7967 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7969 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7971 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7972 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7973 but it really isn't)
7975 @returns Number of bytes that were successfully transmitted
7978 *///=========================================================================
7982 /// Length of message data
7984 /// Flags (ignored with encrypted streams)
7985 const int posix_flags = 0) {
7986 if (__tls) return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data, len)));
7987 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data, len, posix_flags)));
7988 } // -x- int __send -x-
7990 /*======================================================================*//**
7992 This is an internal function that:
7993 1. sends data to the endpoint:
7994 - via underlying socket when TLS is not enabled
7995 - via the OpenSSL socket API when TLS is enabled
7996 2. checks for a socket I/O error (and throws an exception)
7997 3. tracks number of bytes transmitted (if there were no errors)
8000 This method is threadsafe because the underlying POSIX send() and OpenSSL's
8001 SSL_write() functions are threadsafe.
8003 @throws randolf::rex::xEBADF The underlying socket is not open
8004 @throws randolf::rex::xECONNRESET Connect reset by peer
8005 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8006 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8007 part of the user address space
8008 @throws randolf::rex::xEINTR Interrupted by a signal
8009 @throws randolf::rex::xEINVAL Invalid argument passed
8010 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8011 occur, but the POSIX sockets documentation lists it as one of the
8012 errors that can be returned, perhaps because some incorrectly
8013 implemented TCP/IP stacks return this error?)
8014 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8015 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8016 and 65,527 bytes for IPv6)
8017 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8018 network congestion (or, less commonly, insufficient memory)
8019 @throws randolf::rex::xENOMEM Insufficient memory
8020 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8021 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8022 doesn't refer to a socket
8023 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8024 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8025 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8027 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8029 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8030 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8031 but it really isn't)
8033 @returns Number of bytes that were successfully transmitted
8036 *///=========================================================================
8040 /// Number of bytes to send
8042 /// Flags (ignored with encrypted streams)
8043 const int posix_flags = 0) {
8044 if (__tls) { // Encrypted
8045 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data , len )))
8046 + __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.data(), __eol_out.length())));
8047 } else { // Not encrypted
8048 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data , len, posix_flags | MSG_MORE))) // MSG_MORE prevents extra packets from being sent
8049 + __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.data(), __eol_out.length(), posix_flags )));
8050 } // -x- if __tls -x-
8051 } // -x- int __sendline -x-
8053 /*======================================================================*//**
8055 This is an internal function that:
8056 1. sends data to the endpoint:
8057 - via underlying socket when TLS is not enabled
8058 - via the OpenSSL socket API when TLS is enabled
8059 2. checks for a socket I/O error (and throws an exception)
8060 3. tracks number of bytes transmitted (if there were no errors)
8063 This method is threadsafe because the underlying POSIX send() and OpenSSL's
8064 SSL_write() functions are threadsafe.
8066 @throws randolf::rex::xEBADF The underlying socket is not open
8067 @throws randolf::rex::xECONNRESET Connect reset by peer
8068 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8069 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8070 part of the user address space
8071 @throws randolf::rex::xEINTR Interrupted by a signal
8072 @throws randolf::rex::xEINVAL Invalid argument passed
8073 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8074 occur, but the POSIX sockets documentation lists it as one of the
8075 errors that can be returned, perhaps because some incorrectly
8076 implemented TCP/IP stacks return this error?)
8077 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8078 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8079 and 65,527 bytes for IPv6)
8080 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8081 network congestion (or, less commonly, insufficient memory)
8082 @throws randolf::rex::xENOMEM Insufficient memory
8083 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8084 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8085 doesn't refer to a socket
8086 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8087 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8088 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8090 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8092 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8093 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8094 but it really isn't)
8096 @returns Number of bytes that were successfully transmitted
8098 *///=========================================================================
8100 /// Flags (ignored with encrypted streams)
8101 const int posix_flags = 0) {
8102 if (__tls) { // Encrypted
8103 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.data(), __eol_out.length())));
8104 } else { // Not encrypted
8105 return __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.data(), __eol_out.length(), posix_flags)));
8106 } // -x- if __tls -x-
8107 } // -x- int __send_eol -x-
8108 // TODO: Create a recv_eol() method that some people will probably consider to be evil (ha ha!)
8109 // recv_eol() will need to be able to auto-detect the CRLF sequence on-the-fly (if necessary) just like recvline() does
8112 /*======================================================================*//**
8114 Send data in the form of a std::string to the endpoint.
8116 This method is threadsafe.
8118 @throws randolf::rex::xEBADF The underlying socket is not open
8119 @throws randolf::rex::xECONNRESET Connect reset by peer
8120 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8121 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8122 part of the user address space
8123 @throws randolf::rex::xEINTR Interrupted by a signal
8124 @throws randolf::rex::xEINVAL Invalid argument passed
8125 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8126 occur, but the POSIX sockets documentation lists it as one of the
8127 errors that can be returned, perhaps because some incorrectly
8128 implemented TCP/IP stacks return this error?)
8129 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8130 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8131 and 65,527 bytes for IPv6)
8132 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8133 network congestion (or, less commonly, insufficient memory)
8134 @throws randolf::rex::xENOMEM Insufficient memory
8135 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8136 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8137 doesn't refer to a socket
8138 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8139 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8140 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8142 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8144 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8145 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8146 but it really isn't)
8148 @returns The same rsocket object so as to facilitate stacking
8151 *///=========================================================================
8154 const std::string& msg,
8161 const int posix_flags = 0) {
8162 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8164 + ", " + std::to_string(msg.length())
8165 + ", " + std::to_string(posix_flags)
8167 __send(msg.data(), msg.length(), posix_flags);
8169 } // -x- rsocket& send -x-
8171 /*======================================================================*//**
8173 Send data in the form of a std::vector<char> to the endpoint.
8175 This method is threadsafe.
8177 @throws randolf::rex::xEBADF The underlying socket is not open
8178 @throws randolf::rex::xECONNRESET Connect reset by peer
8179 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8180 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8181 part of the user address space
8182 @throws randolf::rex::xEINTR Interrupted by a signal
8183 @throws randolf::rex::xEINVAL Invalid argument passed
8184 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8185 occur, but the POSIX sockets documentation lists it as one of the
8186 errors that can be returned, perhaps because some incorrectly
8187 implemented TCP/IP stacks return this error?)
8188 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8189 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8190 and 65,527 bytes for IPv6)
8191 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8192 network congestion (or, less commonly, insufficient memory)
8193 @throws randolf::rex::xENOMEM Insufficient memory
8194 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8195 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8196 doesn't refer to a socket
8197 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8198 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8199 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8201 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8203 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8204 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8205 but it really isn't)
8207 @returns The same rsocket object so as to facilitate stacking
8208 @see recv(std::vector<char>, const int)
8210 *///=========================================================================
8213 const std::vector<char>& msg,
8220 const int posix_flags = 0) {
8221 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8223 + ", " + std::to_string(msg.size())
8224 + ", " + std::to_string(posix_flags)
8226 __send(msg.data(), msg.size(), posix_flags);
8228 } // -x- rsocket& send -x-
8230 /*======================================================================*//**
8232 Send data in the form of a C-string to the endpoint.
8234 This method is threadsafe.
8236 @throws randolf::rex::xEBADF The underlying socket is not open
8237 @throws randolf::rex::xECONNRESET Connect reset by peer
8238 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8239 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8240 part of the user address space
8241 @throws randolf::rex::xEINTR Interrupted by a signal
8242 @throws randolf::rex::xEINVAL Invalid argument passed
8243 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8244 occur, but the POSIX sockets documentation lists it as one of the
8245 errors that can be returned, perhaps because some incorrectly
8246 implemented TCP/IP stacks return this error?)
8247 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8248 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8249 and 65,527 bytes for IPv6)
8250 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8251 network congestion (or, less commonly, insufficient memory)
8252 @throws randolf::rex::xENOMEM Insufficient memory
8253 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8254 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8255 doesn't refer to a socket
8256 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8257 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8258 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8260 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8262 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8263 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8264 but it really isn't)
8266 @returns The same rsocket object so as to facilitate stacking
8267 @see recv(const size_t, const int)
8270 *///=========================================================================
8272 /// Pointer to data to send
8274 /// Number of bytes to send, or 0 to auto-detect length if message is an ASCIIZ string
8282 const int posix_flags = 0) {
8283 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8285 + ", " + std::to_string(len)
8286 + ", " + std::to_string(posix_flags)
8289 // --------------------------------------------------------------------------
8290 // Measure size of format string if an ASCIIZ string was indicated.
8291 // --------------------------------------------------------------------------
8292 if (len == 0) len = std::strlen(msg);
8294 // --------------------------------------------------------------------------
8296 // --------------------------------------------------------------------------
8297 __send(msg, len, posix_flags);
8299 } // -x- rsocket& send -x-
8301 /*======================================================================*//**
8303 Send data in the form of an ASCIIZ string to the endpoint, including the
8304 terminating NULL character.
8306 This method is threadsafe.
8308 @throws randolf::rex::xEBADF The underlying socket is not open
8309 @throws randolf::rex::xECONNRESET Connect reset by peer
8310 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8311 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8312 part of the user address space
8313 @throws randolf::rex::xEINTR Interrupted by a signal
8314 @throws randolf::rex::xEINVAL Invalid argument passed
8315 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8316 occur, but the POSIX sockets documentation lists it as one of the
8317 errors that can be returned, perhaps because some incorrectly
8318 implemented TCP/IP stacks return this error?)
8319 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8320 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8321 and 65,527 bytes for IPv6)
8322 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8323 network congestion (or, less commonly, insufficient memory)
8324 @throws randolf::rex::xENOMEM Insufficient memory
8325 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8326 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8327 doesn't refer to a socket
8328 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8329 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8330 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8332 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8334 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8335 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8336 but it really isn't)
8338 @returns The same rsocket object so as to facilitate stacking
8339 @see recv_asciiz(const size_t, const int)
8340 @see sendz(const char*, const int) which doesn't transmit the terminating
8343 *///=========================================================================
8344 rsocket& send_asciiz(
8345 /// Pointer to data to send
8353 const int posix_flags = 0) {
8354 if (__debug) debug("send_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8356 + ", " + std::to_string(std::strlen(msg) + 1)
8357 + ", " + std::to_string(posix_flags)
8359 __send(msg, std::strlen(msg) + 1, posix_flags);
8361 } // -x- rsocket& send_asciiz -x-
8363 /*======================================================================*//**
8365 Send one 8-bit byte (one unsigned character) of data to the endpoint.
8367 This method is threadsafe.
8369 @throws randolf::rex::xEBADF The underlying socket is not open
8370 @throws randolf::rex::xECONNRESET Connect reset by peer
8371 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8372 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8373 part of the user address space
8374 @throws randolf::rex::xEINTR Interrupted by a signal
8375 @throws randolf::rex::xEINVAL Invalid argument passed
8376 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8377 occur, but the POSIX sockets documentation lists it as one of the
8378 errors that can be returned, perhaps because some incorrectly
8379 implemented TCP/IP stacks return this error?)
8380 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8381 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8382 and 65,527 bytes for IPv6)
8383 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8384 network congestion (or, less commonly, insufficient memory)
8385 @throws randolf::rex::xENOMEM Insufficient memory
8386 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8387 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8388 doesn't refer to a socket
8389 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8390 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8391 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8393 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8395 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8396 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8397 but it really isn't)
8399 @returns The same rsocket object so as to facilitate stacking
8404 *///=========================================================================
8414 const int posix_flags = 0) {
8415 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8417 + ", " + std::to_string(sizeof(value))
8418 + ", " + std::to_string(posix_flags)
8420 __send(&value, sizeof(value), posix_flags);
8422 } // -x- rsocket& send_byte -x-
8424 /*======================================================================*//**
8426 Send one signed character (one 8-bit byte) of data to the endpoint.
8428 @returns The same rsocket object so as to facilitate stacking
8433 *///=========================================================================
8443 const int posix_flags = 0) {
8444 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8446 + ", " + std::to_string(sizeof(value))
8447 + ", " + std::to_string(posix_flags)
8449 __send(&value, sizeof(value), posix_flags);
8451 } // -x- rsocket& send_char -x-
8453 /*======================================================================*//**
8455 Send the EoL sequence to the endpoint.
8457 This method is threadsafe.
8459 @throws randolf::rex::xEBADF The underlying socket is not open
8460 @throws randolf::rex::xECONNRESET Connect reset by peer
8461 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8462 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8463 part of the user address space
8464 @throws randolf::rex::xEINTR Interrupted by a signal
8465 @throws randolf::rex::xEINVAL Invalid argument passed
8466 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8467 occur, but the POSIX sockets documentation lists it as one of the
8468 errors that can be returned, perhaps because some incorrectly
8469 implemented TCP/IP stacks return this error?)
8470 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8471 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8472 and 65,527 bytes for IPv6)
8473 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8474 network congestion (or, less commonly, insufficient memory)
8475 @throws randolf::rex::xENOMEM Insufficient memory
8476 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8477 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8478 doesn't refer to a socket
8479 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8480 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8481 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8483 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8485 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8486 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8487 but it really isn't)
8489 @returns The same rsocket object so as to facilitate stacking
8495 *///=========================================================================
8503 const int posix_flags = 0) {
8504 if (__debug) debug("send_eol(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8505 + ", " + std::to_string(posix_flags)
8507 __send_eol(posix_flags);
8509 } // -x- rsocket& send_eol -x-
8511 /*======================================================================*//**
8513 Send data in the form of a @ref randolf::rline to the endpoint, with an EoL
8516 This method is threadsafe.
8518 @throws randolf::rex::xEBADF The underlying socket is not open
8519 @throws randolf::rex::xECONNRESET Connect reset by peer
8520 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8521 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8522 part of the user address space
8523 @throws randolf::rex::xEINTR Interrupted by a signal
8524 @throws randolf::rex::xEINVAL Invalid argument passed
8525 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8526 occur, but the POSIX sockets documentation lists it as one of the
8527 errors that can be returned, perhaps because some incorrectly
8528 implemented TCP/IP stacks return this error?)
8529 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8530 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8531 and 65,527 bytes for IPv6)
8532 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8533 network congestion (or, less commonly, insufficient memory)
8534 @throws randolf::rex::xENOMEM Insufficient memory
8535 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8536 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8537 doesn't refer to a socket
8538 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8539 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8540 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8542 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8544 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8545 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8546 but it really isn't)
8548 @returns The same rsocket object so as to facilitate stacking
8556 *///=========================================================================
8557 rsocket& send_rline(
8559 randolf::rline& line,
8566 const int posix_flags = 0) {
8567 if (__debug) debug("sendline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8568 + ", <randolf::rline>"
8569 + ", " + std::to_string(line.length())
8570 + ", " + std::to_string(posix_flags)
8572 __sendline(line.data(), line.length(), posix_flags);
8574 } // -x- rsocket& send_rline -x-
8576 /*======================================================================*//**
8578 Send a data structure to the endpoint.
8580 MSB/LSB considerations are important for any integers within your structure,
8581 so be sure to sanitize them with htons(), ntohs(), and related methods prior
8582 to sending, and then after receiving. This way, your data will be protected
8583 against corruption resulting from byte order differences when communicating
8584 between hardware architectures that differ in MSB and LSB byte ordering.
8586 This method is threadsafe.
8588 @throws randolf::rex::xEBADF The underlying socket is not open
8589 @throws randolf::rex::xECONNRESET Connect reset by peer
8590 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8591 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8592 part of the user address space
8593 @throws randolf::rex::xEINTR Interrupted by a signal
8594 @throws randolf::rex::xEINVAL Invalid argument passed
8595 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8596 occur, but the POSIX sockets documentation lists it as one of the
8597 errors that can be returned, perhaps because some incorrectly
8598 implemented TCP/IP stacks return this error?)
8599 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8600 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8601 and 65,527 bytes for IPv6)
8602 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8603 network congestion (or, less commonly, insufficient memory)
8604 @throws randolf::rex::xENOMEM Insufficient memory
8605 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8606 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8607 doesn't refer to a socket
8608 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8609 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8610 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8612 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8614 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8615 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8616 but it really isn't)
8618 @returns The same rsocket object so as to facilitate stacking
8621 *///=========================================================================
8622 template <typename T> rsocket& send_struct(
8624 const T& value, // TODO: Test this to make sure __send(value ...) works; or does it need __send(&value ...)?
8631 const int posix_flags = 0) {
8632 if (__debug) debug("send_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8634 + ", " + std::to_string(sizeof(value))
8635 + ", " + std::to_string(posix_flags)
8637 __send(value, sizeof(value), posix_flags);
8639 } // -x- rsocket& send_struct -x-
8641 /*======================================================================*//**
8643 Send one 16-bit unsigned integer of data in LSB (little endian) order to the
8646 This method is threadsafe.
8648 @throws randolf::rex::xEBADF The underlying socket is not open
8649 @throws randolf::rex::xECONNRESET Connect reset by peer
8650 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8651 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8652 part of the user address space
8653 @throws randolf::rex::xEINTR Interrupted by a signal
8654 @throws randolf::rex::xEINVAL Invalid argument passed
8655 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8656 occur, but the POSIX sockets documentation lists it as one of the
8657 errors that can be returned, perhaps because some incorrectly
8658 implemented TCP/IP stacks return this error?)
8659 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8660 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8661 and 65,527 bytes for IPv6)
8662 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8663 network congestion (or, less commonly, insufficient memory)
8664 @throws randolf::rex::xENOMEM Insufficient memory
8665 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8666 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8667 doesn't refer to a socket
8668 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8669 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8670 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8672 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8674 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8675 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8676 but it really isn't)
8678 @returns The same rsocket object so as to facilitate stacking
8679 @see recv_uint16_lsb
8680 @see send_uint16_msb
8682 *///=========================================================================
8683 rsocket& send_uint16_lsb(
8685 const uint16_t value,
8692 const int posix_flags = 0) {
8693 uint16_t buf = !__endian_is_msb ? value : ntohs(value);
8694 if (__debug) debug("send_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8695 + ", " + std::to_string(value)
8696 + ", " + std::to_string(sizeof(buf))
8697 + ", " + std::to_string(posix_flags)
8699 __send(&buf, sizeof(buf), posix_flags);
8701 } // -x- rsocket& send_uint16_lsb -x-
8703 /*======================================================================*//**
8705 Send one 16-bit integer of data in MSB (big endian) order to the endpoint.
8707 This method is threadsafe.
8709 @throws randolf::rex::xEBADF The underlying socket is not open
8710 @throws randolf::rex::xECONNRESET Connect reset by peer
8711 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8712 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8713 part of the user address space
8714 @throws randolf::rex::xEINTR Interrupted by a signal
8715 @throws randolf::rex::xEINVAL Invalid argument passed
8716 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8717 occur, but the POSIX sockets documentation lists it as one of the
8718 errors that can be returned, perhaps because some incorrectly
8719 implemented TCP/IP stacks return this error?)
8720 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8721 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8722 and 65,527 bytes for IPv6)
8723 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8724 network congestion (or, less commonly, insufficient memory)
8725 @throws randolf::rex::xENOMEM Insufficient memory
8726 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8727 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8728 doesn't refer to a socket
8729 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8730 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8731 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8733 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8735 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8736 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8737 but it really isn't)
8739 @returns The same rsocket object so as to facilitate stacking
8740 @see recv_uint16_msb
8741 @see send_uint16_lsb
8743 *///=========================================================================
8744 rsocket& send_uint16_msb(
8746 const uint16_t value,
8753 const int posix_flags = 0) {
8754 int16_t buf = __endian_is_msb ? value : htons(value);
8755 if (__debug) debug("send_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8756 + ", " + std::to_string(value)
8757 + ", " + std::to_string(sizeof(buf))
8758 + ", " + std::to_string(posix_flags)
8760 __send(&buf, sizeof(buf), posix_flags);
8762 } // -x- rsocket& send_int16_msb -x-
8764 /*======================================================================*//**
8766 Send one 32-bit unsigned integer of data in LSB (little endian) order to the
8769 This method is threadsafe.
8771 @throws randolf::rex::xEBADF The underlying socket is not open
8772 @throws randolf::rex::xECONNRESET Connect reset by peer
8773 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8774 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8775 part of the user address space
8776 @throws randolf::rex::xEINTR Interrupted by a signal
8777 @throws randolf::rex::xEINVAL Invalid argument passed
8778 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8779 occur, but the POSIX sockets documentation lists it as one of the
8780 errors that can be returned, perhaps because some incorrectly
8781 implemented TCP/IP stacks return this error?)
8782 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8783 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8784 and 65,527 bytes for IPv6)
8785 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8786 network congestion (or, less commonly, insufficient memory)
8787 @throws randolf::rex::xENOMEM Insufficient memory
8788 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8789 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8790 doesn't refer to a socket
8791 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8792 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8793 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8795 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8797 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8798 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8799 but it really isn't)
8801 @returns The same rsocket object so as to facilitate stacking
8802 @see recv_uint32_lsb
8803 @see send_uint32_msb
8805 *///=========================================================================
8806 rsocket& send_uint32_lsb(
8808 const uint32_t value,
8815 const int posix_flags = 0) {
8816 uint32_t buf = !__endian_is_msb ? value : ntohl(value);
8817 if (__debug) debug("send_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8818 + ", " + std::to_string(value)
8819 + ", " + std::to_string(sizeof(buf))
8820 + ", " + std::to_string(posix_flags)
8822 __send(&buf, sizeof(buf), posix_flags);
8824 } // -x- rsocket& send_uint32_lsb -x-
8826 /*======================================================================*//**
8828 Send one 32-bit unsigned integer of data in MSB (big endian) order to the
8831 This method is threadsafe.
8833 @throws randolf::rex::xEBADF The underlying socket is not open
8834 @throws randolf::rex::xECONNRESET Connect reset by peer
8835 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8836 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8837 part of the user address space
8838 @throws randolf::rex::xEINTR Interrupted by a signal
8839 @throws randolf::rex::xEINVAL Invalid argument passed
8840 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8841 occur, but the POSIX sockets documentation lists it as one of the
8842 errors that can be returned, perhaps because some incorrectly
8843 implemented TCP/IP stacks return this error?)
8844 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8845 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8846 and 65,527 bytes for IPv6)
8847 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8848 network congestion (or, less commonly, insufficient memory)
8849 @throws randolf::rex::xENOMEM Insufficient memory
8850 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8851 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8852 doesn't refer to a socket
8853 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8854 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8855 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8857 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8859 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8860 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8861 but it really isn't)
8863 @returns The same rsocket object so as to facilitate stacking
8864 @see recv_uint32_msb
8865 @see send_uint32_lsb
8867 *///=========================================================================
8868 rsocket& send_uint32_msb(
8870 const uint32_t value,
8877 const int posix_flags = 0) {
8878 uint32_t buf = __endian_is_msb ? value : htonl(value);
8879 if (__debug) debug("send_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8880 + ", " + std::to_string(value)
8881 + ", " + std::to_string(sizeof(buf))
8882 + ", " + std::to_string(posix_flags)
8884 __send(&buf, sizeof(buf), posix_flags);
8886 } // -x- rsocket& send_uint32_msb -x-
8888 /*======================================================================*//**
8890 Send one 64-bit unsigned integer of data in LSB (little endian) order to the
8893 This method is threadsafe.
8895 @throws randolf::rex::xEBADF The underlying socket is not open
8896 @throws randolf::rex::xECONNRESET Connect reset by peer
8897 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8898 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8899 part of the user address space
8900 @throws randolf::rex::xEINTR Interrupted by a signal
8901 @throws randolf::rex::xEINVAL Invalid argument passed
8902 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8903 occur, but the POSIX sockets documentation lists it as one of the
8904 errors that can be returned, perhaps because some incorrectly
8905 implemented TCP/IP stacks return this error?)
8906 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8907 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8908 and 65,527 bytes for IPv6)
8909 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8910 network congestion (or, less commonly, insufficient memory)
8911 @throws randolf::rex::xENOMEM Insufficient memory
8912 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8913 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8914 doesn't refer to a socket
8915 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8916 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8917 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8919 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8921 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8922 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8923 but it really isn't)
8925 @returns The same rsocket object so as to facilitate stacking
8926 @see recv_uint64_lsb
8927 @see send_uint64_msb
8929 *///=========================================================================
8930 rsocket& send_uint64_lsb(
8932 const uint64_t value,
8939 const int posix_flags = 0) {
8940 uint64_t buf = !__endian_is_msb ? value : ((((uint64_t)ntohl((value) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((value) >> 32)));
8941 if (__debug) debug("send_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8942 + ", " + std::to_string(value)
8943 + ", " + std::to_string(sizeof(buf))
8944 + ", " + std::to_string(posix_flags)
8946 __send(&buf, sizeof(buf), posix_flags);
8948 } // -x- rsocket& send_uint64_lsb -x-
8950 /*======================================================================*//**
8952 Send one 64-bit unsigned integer of data in MSB (big endian) order to the
8955 This method is threadsafe.
8957 @throws randolf::rex::xEBADF The underlying socket is not open
8958 @throws randolf::rex::xECONNRESET Connect reset by peer
8959 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8960 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8961 part of the user address space
8962 @throws randolf::rex::xEINTR Interrupted by a signal
8963 @throws randolf::rex::xEINVAL Invalid argument passed
8964 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8965 occur, but the POSIX sockets documentation lists it as one of the
8966 errors that can be returned, perhaps because some incorrectly
8967 implemented TCP/IP stacks return this error?)
8968 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8969 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8970 and 65,527 bytes for IPv6)
8971 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8972 network congestion (or, less commonly, insufficient memory)
8973 @throws randolf::rex::xENOMEM Insufficient memory
8974 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8975 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8976 doesn't refer to a socket
8977 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8978 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8979 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8981 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8983 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8984 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8985 but it really isn't)
8987 @returns The same rsocket object so as to facilitate stacking
8988 @see recv_uint64_msb
8989 @see send_uint64_lsb
8991 *///=========================================================================
8992 rsocket& send_uint64_msb(
8994 const uint64_t value,
9001 const int posix_flags = 0) {
9002 uint64_t buf = __endian_is_msb ? value : ((((uint64_t)htonl((value) & 0xffffffffUL)) << 32) | htonl((uint32_t)((value) >> 32)));
9003 if (__debug) debug("send_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9004 + ", " + std::to_string(value)
9005 + ", " + std::to_string(sizeof(buf))
9006 + ", " + std::to_string(posix_flags)
9008 __send(&buf, sizeof(buf), posix_flags);
9010 } // -x- rsocket& send_uint64_msb -x-
9012 /*======================================================================*//**
9014 Send data in the form of a std::string to the endpoint, with an EoL sequence
9017 This method is threadsafe.
9019 @throws randolf::rex::xEBADF The underlying socket is not open
9020 @throws randolf::rex::xECONNRESET Connect reset by peer
9021 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
9022 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9023 part of the user address space
9024 @throws randolf::rex::xEINTR Interrupted by a signal
9025 @throws randolf::rex::xEINVAL Invalid argument passed
9026 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
9027 occur, but the POSIX sockets documentation lists it as one of the
9028 errors that can be returned, perhaps because some incorrectly
9029 implemented TCP/IP stacks return this error?)
9030 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
9031 sent atomically (the maximum size is typically 65,507 bytes for IPv4
9032 and 65,527 bytes for IPv6)
9033 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
9034 network congestion (or, less commonly, insufficient memory)
9035 @throws randolf::rex::xENOMEM Insufficient memory
9036 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9037 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9038 doesn't refer to a socket
9039 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
9040 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
9041 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
9043 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
9045 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
9046 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
9047 but it really isn't)
9049 @returns The same rsocket object so as to facilitate stacking
9056 *///=========================================================================
9059 const std::string& msg,
9066 const int posix_flags = 0) {
9067 if (__debug) debug("sendline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9069 + ", " + std::to_string(msg.length())
9070 + "+" + std::to_string(__eol_out.length())
9071 + ", " + std::to_string(posix_flags)
9073 __sendline(msg.data(), msg.length(), posix_flags);
9075 } // -x- rsocket& sendline -x-
9077 /*======================================================================*//**
9079 Send data in the form of a "msghdr" structure to a specific endpoint.
9081 This method is not compatible with TLS.
9083 This method is threadsafe.
9085 @throws randolf::rex::xEBADF The underlying socket is not open
9086 @throws randolf::rex::xECONNRESET Connect reset by peer
9087 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
9088 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9089 part of the user address space
9090 @throws randolf::rex::xEINTR Interrupted by a signal
9091 @throws randolf::rex::xEINVAL Invalid argument passed
9092 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
9093 occur, but the POSIX sockets documentation lists it as one of the
9094 errors that can be returned, perhaps because some incorrectly
9095 implemented TCP/IP stacks return this error?)
9096 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
9097 sent atomically (the maximum size is typically 65,507 bytes for IPv4
9098 and 65,527 bytes for IPv6)
9099 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
9100 network congestion (or, less commonly, insufficient memory)
9101 @throws randolf::rex::xENOMEM Insufficient memory
9102 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9103 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9104 doesn't refer to a socket
9105 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
9106 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
9107 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
9109 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
9111 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
9112 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
9113 but it really isn't)
9115 @returns The same rsocket object so as to facilitate stacking
9118 *///=========================================================================
9120 /// Pointer to data to send
9121 const struct msghdr* msg,
9128 const int posix_flags = 0) {
9129 if (__debug) debug("sendmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9131 + ", " + std::to_string(posix_flags)
9133 __track_bytes_tx(__rc_check(::sendmsg(__socket_fd, msg, posix_flags)));
9135 } // -x- rsocket& sendmsg -x-
9137 /*======================================================================*//**
9139 Send data in the form of a "mmsghdr" structure to a specific endpoint.
9141 This method is not compatible with TLS.
9143 This method is threadsafe.
9145 @throws randolf::rex::xEBADF The underlying socket is not open
9146 @throws randolf::rex::xECONNRESET Connect reset by peer
9147 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
9148 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9149 part of the user address space
9150 @throws randolf::rex::xEINTR Interrupted by a signal
9151 @throws randolf::rex::xEINVAL Invalid argument passed
9152 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
9153 occur, but the POSIX sockets documentation lists it as one of the
9154 errors that can be returned, perhaps because some incorrectly
9155 implemented TCP/IP stacks return this error?)
9156 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
9157 sent atomically (the maximum size is typically 65,507 bytes for IPv4
9158 and 65,527 bytes for IPv6)
9159 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
9160 network congestion (or, less commonly, insufficient memory)
9161 @throws randolf::rex::xENOMEM Insufficient memory
9162 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9163 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9164 doesn't refer to a socket
9165 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
9166 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
9167 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
9169 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
9171 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
9172 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
9173 but it really isn't)
9175 @returns The same rsocket object so as to facilitate stacking
9178 *///=========================================================================
9180 /// Pointer to data to send
9181 struct mmsghdr* mmsg,
9182 /// Size of target endpoint structure
9183 const unsigned int vlen = sizeof(mmsghdr),
9190 const int posix_flags = 0) {
9191 if (__debug) debug("sendmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9193 + ", " + std::to_string(vlen)
9194 + ", " + std::to_string(posix_flags)
9196 __track_bytes_tx(__rc_check(::sendmmsg(__socket_fd, mmsg, vlen, posix_flags)));
9198 } // -x- rsocket& sendmsg -x-
9200 /*======================================================================*//**
9202 Send data in the form of a std::string to a specific endpoint.
9204 This method is not compatible with TLS.
9206 This method is threadsafe.
9208 @throws randolf::rex::xEBADF The underlying socket is not open
9209 @throws randolf::rex::xECONNRESET Connect reset by peer
9210 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
9211 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9212 part of the user address space
9213 @throws randolf::rex::xEINTR Interrupted by a signal
9214 @throws randolf::rex::xEINVAL Invalid argument passed
9215 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
9216 occur, but the POSIX sockets documentation lists it as one of the
9217 errors that can be returned, perhaps because some incorrectly
9218 implemented TCP/IP stacks return this error?)
9219 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
9220 sent atomically (the maximum size is typically 65,507 bytes for IPv4
9221 and 65,527 bytes for IPv6)
9222 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
9223 network congestion (or, less commonly, insufficient memory)
9224 @throws randolf::rex::xENOMEM Insufficient memory
9225 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9226 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9227 doesn't refer to a socket
9228 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
9229 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
9230 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
9232 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
9234 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
9235 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
9236 but it really isn't)
9238 @returns The same rsocket object so as to facilitate stacking
9241 *///=========================================================================
9244 const std::string& msg,
9251 const int posix_flags,
9252 /// Target endpoint address structure
9253 const struct sockaddr *to,
9254 /// Size of target endpoint structure
9255 socklen_t tolen = sizeof(sockaddr)) {
9256 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9258 + ", " + std::to_string(posix_flags)
9260 + ", " + std::to_string(tolen)
9262 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg.data(), msg.length(), posix_flags, to, tolen)));
9264 } // -x- rsocket& sendto -x- // TODO: Create easier-to-use variants
9266 /*======================================================================*//**
9268 Send data in the form of a C-string to a specific endpoint.
9270 This method is not compatible with TLS.
9272 This method is threadsafe.
9274 @throws randolf::rex::xEBADF The underlying socket is not open
9275 @throws randolf::rex::xECONNRESET Connect reset by peer
9276 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
9277 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9278 part of the user address space
9279 @throws randolf::rex::xEINTR Interrupted by a signal
9280 @throws randolf::rex::xEINVAL Invalid argument passed
9281 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
9282 occur, but the POSIX sockets documentation lists it as one of the
9283 errors that can be returned, perhaps because some incorrectly
9284 implemented TCP/IP stacks return this error?)
9285 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
9286 sent atomically (the maximum size is typically 65,507 bytes for IPv4
9287 and 65,527 bytes for IPv6)
9288 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
9289 network congestion (or, less commonly, insufficient memory)
9290 @throws randolf::rex::xENOMEM Insufficient memory
9291 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9292 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9293 doesn't refer to a socket
9294 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
9295 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
9296 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
9298 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
9300 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
9301 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
9302 but it really isn't)
9304 @returns The same rsocket object so as to facilitate stacking
9307 *///=========================================================================
9309 /// Pointer to data to send
9311 /// Number of bytes to send
9319 const int posix_flags,
9320 /// Target endpoint address structure
9321 const struct sockaddr *to,
9322 /// Size of target endpoint structure
9323 socklen_t tolen = sizeof(sockaddr)) {
9324 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9326 + ", " + std::to_string(std::strlen(msg))
9327 + ", " + std::to_string(posix_flags)
9329 + ", " + std::to_string(tolen)
9331 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, len, posix_flags, to, tolen)));
9333 } // -x- rsocket& sendto -x-
9335 /*======================================================================*//**
9337 Send data in the form of an ASCIIZ string to the endpoint. The terminating
9338 NULL character won't be transmitted.
9340 This method is threadsafe.
9342 @throws randolf::rex::xEBADF The underlying socket is not open
9343 @throws randolf::rex::xECONNRESET Connect reset by peer
9344 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
9345 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9346 part of the user address space
9347 @throws randolf::rex::xEINTR Interrupted by a signal
9348 @throws randolf::rex::xEINVAL Invalid argument passed
9349 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
9350 occur, but the POSIX sockets documentation lists it as one of the
9351 errors that can be returned, perhaps because some incorrectly
9352 implemented TCP/IP stacks return this error?)
9353 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
9354 sent atomically (the maximum size is typically 65,507 bytes for IPv4
9355 and 65,527 bytes for IPv6)
9356 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
9357 network congestion (or, less commonly, insufficient memory)
9358 @throws randolf::rex::xENOMEM Insufficient memory
9359 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9360 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9361 doesn't refer to a socket
9362 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
9363 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
9364 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
9366 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
9368 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
9369 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
9370 but it really isn't)
9372 @returns The same rsocket object so as to facilitate stacking
9373 @see recvz(const size_t, const int)
9374 @see send_asciiz(const char*, const int) which also transmits the terminating
9377 *///=========================================================================
9379 /// Pointer to data to send
9387 const int posix_flags = 0) {
9388 if (__debug) debug("sendz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9390 + ", " + std::to_string(std::strlen(msg))
9391 + ", " + std::to_string(posix_flags)
9393 __send(msg, std::strlen(msg), posix_flags);
9395 } // -x- rsocket& sendz -x-
9397 /*======================================================================*//**
9399 Send data in the form of an ASCIIZ string to a specific endpoint. The
9400 terminating NULL character won't be transmitted.
9402 This method is not compatible with TLS.
9404 This method is threadsafe.
9406 @throws randolf::rex::xEBADF The underlying socket is not open
9407 @throws randolf::rex::xECONNRESET Connect reset by peer
9408 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
9409 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9410 part of the user address space
9411 @throws randolf::rex::xEINTR Interrupted by a signal
9412 @throws randolf::rex::xEINVAL Invalid argument passed
9413 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
9414 occur, but the POSIX sockets documentation lists it as one of the
9415 errors that can be returned, perhaps because some incorrectly
9416 implemented TCP/IP stacks return this error?)
9417 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
9418 sent atomically (the maximum size is typically 65,507 bytes for IPv4
9419 and 65,527 bytes for IPv6)
9420 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
9421 network congestion (or, less commonly, insufficient memory)
9422 @throws randolf::rex::xENOMEM Insufficient memory
9423 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9424 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9425 doesn't refer to a socket
9426 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
9427 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
9428 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
9430 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
9432 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
9433 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
9434 but it really isn't)
9439 @returns The same rsocket object so as to facilitate stacking
9440 *///=========================================================================
9442 /// Pointer to data to send
9450 const int posix_flags,
9451 /// Target endpoint address structure
9452 const struct sockaddr* to,
9453 /// Size of target endpoint structure
9454 socklen_t tolen = sizeof(sockaddr)) {
9455 if (__debug) debug("sendzto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9457 + ", " + std::to_string(std::strlen(msg))
9458 + ", " + std::to_string(posix_flags)
9460 + ", " + std::to_string(tolen)
9462 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, std::strlen(msg), posix_flags, to, tolen)));
9464 } // -x- rsocket& sendzto -x-
9466 /*======================================================================*//**
9468 Set socket option with a zero-length value, which is a special case usage
9469 that will probably never be needed, but is included here for future use where
9470 the call to @c setsockopt() would include specifying @c option_value's length
9471 in the final parameter @c option_len as zero.
9473 (Internally, we still populate the pointer for @c option_value to that of an
9474 internal `static const int` {which does have the value of 0} so as to avoid
9475 triggering an unexpected error where this parameter can't be @c nullptr).
9478 These setsockopt() methods take an integer or character value directly, or a
9479 pointer to a structure, and then rsocket handles the remaining tedious
9480 technical details behind-the-scenes for you when calling the underlying
9481 socket's setsockopt() function.
9483 @throws randolf::rex::xEBADF The underlying socket is not open
9484 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9485 part of the user address space
9486 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
9487 valid for this socket's family (a.k.a., communication domain)
9488 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
9490 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9491 doesn't refer to a socket
9493 @returns The same rsocket object so as to facilitate stacking
9497 *///=========================================================================
9498 rsocket& setsockopt(
9499 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9500 /// also be @c IPPROTO_TCP, etc.
9502 /// The name of the option, such as @c TCP_CORK, etc.
9504 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option);
9505 static const int value = 0;
9506 __rc_check(::setsockopt(__socket_fd, level, option, &value, 0));
9508 } // -x- rsocket& setsockopt -x-
9510 /*======================================================================*//**
9512 Set socket option to the specific `integer`.
9515 These setsockopt() methods take an integer or character value directly, or a
9516 pointer to a structure, and then rsocket handles the remaining tedious
9517 technical details behind-the-scenes for you when calling the underlying
9518 socket's setsockopt() function.
9520 @throws randolf::rex::xEBADF The underlying socket is not open
9521 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9522 part of the user address space
9523 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
9524 valid for this socket's family (a.k.a., communication domain)
9525 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
9527 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9528 doesn't refer to a socket
9530 @returns The same rsocket object so as to facilitate stacking
9531 @see getsockopt_int(const int, const int)
9534 *///=========================================================================
9535 rsocket& setsockopt(
9536 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9537 /// also be @c IPPROTO_TCP, etc.
9539 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9541 /// The value that this socket option will be set to
9543 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
9544 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9546 } // -x- rsocket& setsockopt -x-
9548 /*======================================================================*//**
9550 Set socket option to the specific `unsigned integer`.
9551 @copydetails setsockopt(const int, const int, const int)
9554 For any values that require a u_int, you'll need to explicitly cast this type
9555 when specifying the value directly; for example: (u_int)32768
9557 @throws randolf::rex::xEBADF The underlying socket is not open
9558 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9559 part of the user address space
9560 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
9561 valid for this socket's family (a.k.a., communication domain)
9562 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
9564 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9565 doesn't refer to a socket
9567 @returns The same rsocket object so as to facilitate stacking
9568 @see getsockopt_u_int(const int, const int)
9570 *///=========================================================================
9571 rsocket& setsockopt(
9572 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9573 /// also be @c IPPROTO_TCP, etc.
9575 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9577 /// The value that this socket option will be set to
9578 const u_int value) {
9579 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
9580 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9582 } // -x- rsocket& setsockopt -x-
9584 /*======================================================================*//**
9586 Set socket option to the specific `unsigned character`.
9587 @copydetails setsockopt(const int, const int, const int)
9588 @see getsockopt_u_char(const int, const int)
9590 *///=========================================================================
9591 rsocket& setsockopt(
9592 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9593 /// also be @c IPPROTO_TCP, etc.
9595 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9597 /// The value that this socket option will be set to
9598 const u_char value) {
9599 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
9600 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9602 } // -x- rsocket& setsockopt -x-
9604 /*======================================================================*//**
9606 Set socket option to the specific `linger` structure.
9607 @copydetails setsockopt(const int, const int, const int)
9608 @see getsockopt_linger(const int, const int)
9610 *///=========================================================================
9611 rsocket& setsockopt(
9612 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9613 /// also be @c IPPROTO_TCP, etc.
9615 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9617 /// The structure that this socket option will be set to
9618 const linger& value) {
9619 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9620 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9622 } // -x- rsocket& setsockopt -x-
9624 /*======================================================================*//**
9626 Set socket option to the specific `timeval` structure.
9627 @copydetails setsockopt(const int, const int, const linger&)
9628 @see getsockopt_timeval(const int, const int)
9630 *///=========================================================================
9631 rsocket& setsockopt(
9632 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9633 /// also be @c IPPROTO_TCP, etc.
9635 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9637 /// The structure that this socket option will be set to
9638 const timeval& value) {
9639 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9640 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9642 } // -x- rsocket& setsockopt -x-
9644 /*======================================================================*//**
9646 Set socket option to the specific `in_addr` structure.
9647 @copydetails setsockopt(const int, const int, const linger&)
9648 @see getsockopt_in_addr(const int, const int)
9650 *///=========================================================================
9651 rsocket& setsockopt(
9652 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9653 /// also be @c IPPROTO_TCP, etc.
9655 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9657 /// The structure that this socket option will be set to
9658 const in_addr& value) {
9659 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9660 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9662 } // -x- rsocket& setsockopt -x-
9664 /*======================================================================*//**
9666 Set socket option to the specific `ip_mreq` structure.
9667 @copydetails setsockopt(const int, const int, const linger&)
9668 @see getsockopt_ip_mreq(const int, const int)
9670 *///=========================================================================
9671 rsocket& setsockopt(
9672 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9673 /// also be @c IPPROTO_TCP, etc.
9675 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9677 /// The structure that this socket option will be set to
9678 const ip_mreq& value) {
9679 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9680 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9682 } // -x- rsocket& setsockopt -x-
9684 /*======================================================================*//**
9686 Set socket option to the specific `ip_mreq_source` structure.
9687 @copydetails setsockopt(const int, const int, const linger&)
9688 @see getsockopt_ip_mreq_source(const int, const int)
9690 *///=========================================================================
9691 rsocket& setsockopt(
9692 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9693 /// also be @c IPPROTO_TCP, etc.
9695 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9697 /// The structure that this socket option will be set to
9698 const ip_mreq_source& value) {
9699 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9700 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9702 } // -x- rsocket& setsockopt -x-
9704 /*======================================================================*//**
9706 Set socket option to the specific `ip_mreqn` structure.
9707 @copydetails setsockopt(const int, const int, const linger&)
9708 @see getsockopt_ip_mreqn(const int, const int)
9710 *///=========================================================================
9711 rsocket& setsockopt(
9712 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9713 /// also be @c IPPROTO_TCP, etc.
9715 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9717 /// The structure that this socket option will be set to
9718 const ip_mreqn& value) {
9719 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9720 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9722 } // -x- rsocket& setsockopt -x-
9724 /*======================================================================*//**
9726 Set socket option to the specific `icmp6_filter` structure.
9727 @copydetails setsockopt(const int, const int, const linger&)
9728 @see getsockopt_icmp6_filter(const int, const int)
9730 *///=========================================================================
9731 rsocket& setsockopt(
9732 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9733 /// also be @c IPPROTO_TCP, etc.
9735 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9737 /// The structure that this socket option will be set to
9738 const icmp6_filter& value) {
9739 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9740 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9742 } // -x- rsocket& setsockopt -x-
9744 /*======================================================================*//**
9746 Set socket option to the specific `sockaddr_in6` structure.
9747 @copydetails setsockopt(const int, const int, const linger&)
9748 @see getsockopt_sockaddr_in6(const int, const int)
9750 *///=========================================================================
9751 rsocket& setsockopt(
9752 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9753 /// also be @c IPPROTO_TCP, etc.
9755 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9757 /// The structure that this socket option will be set to
9758 const sockaddr_in6& value) {
9759 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9760 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9762 } // -x- rsocket& setsockopt -x-
9764 /*======================================================================*//**
9766 Set socket option to the specific `ip6_mtuinfo` structure.
9767 @copydetails setsockopt(const int, const int, const linger&)
9768 @see getsockopt_ip6_mtuinfo(const int, const int)
9770 *///=========================================================================
9771 rsocket& setsockopt(
9772 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9773 /// also be @c IPPROTO_TCP, etc.
9775 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9777 /// The structure that this socket option will be set to
9778 const ip6_mtuinfo& value) {
9779 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9780 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9782 } // -x- rsocket& setsockopt -x-
9784 /*======================================================================*//**
9786 Set socket option to the specific `ipv6_mreq` structure.
9787 @copydetails setsockopt(const int, const int, const linger&)
9788 @see getsockopt_ipv6_mreq(const int, const int)
9790 *///=========================================================================
9791 rsocket& setsockopt(
9792 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9793 /// also be @c IPPROTO_TCP, etc.
9795 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9797 /// The structure that this socket option will be set to
9798 const ipv6_mreq& value) {
9799 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9800 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9802 } // -x- rsocket& setsockopt -x-
9804 /*======================================================================*//**
9806 Set socket option to the specific `group_req` structure.
9807 @copydetails setsockopt(const int, const int, const linger&)
9808 @see getsockopt_group_req(const int, const int)
9810 *///=========================================================================
9811 rsocket& setsockopt(
9812 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9813 /// also be @c IPPROTO_TCP, etc.
9815 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9817 /// The structure that this socket option will be set to
9818 const group_req& value) {
9819 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9820 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9822 } // -x- rsocket& setsockopt -x-
9824 /*======================================================================*//**
9826 Set socket option to the specific `group_source_req` structure.
9827 @copydetails setsockopt(const int, const int, const linger&)
9828 @see getsockopt_group_source_req(const int, const int)
9830 *///=========================================================================
9831 rsocket& setsockopt(
9832 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9833 /// also be @c IPPROTO_TCP, etc.
9835 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9837 /// The structure that this socket option will be set to
9838 const group_source_req& value) {
9839 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9840 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9842 } // -x- rsocket& setsockopt -x-
9844 /*======================================================================*//**
9846 Shut down the underlying socket, partially or fully.
9848 <div style=padding-left:32px;>
9851 <td valign=top>SHUT_RD:</td>
9852 <td>Further receives will be disallowed.</td>
9855 <td valign=top>SHUT_WR:</td>
9856 <td>Further sends will be disallowed (this may cause actions specific
9857 to the protocol family of the socket to occur).</td>
9860 <td valign=top>SHUT_RDWR:</td>
9861 <td>Further sends and receives will be disallowed (default).</td>
9866 @throws randolf::rex::xEBADF The underlying socket is not open
9867 @throws randolf::rex::xEINVAL Invalid argument passed
9868 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9869 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9870 doesn't refer to a socket
9872 @returns The same rsocket object so as to facilitate stacking
9878 *///=========================================================================
9882 /// SHUT_RDWR (default)
9883 const int how = SHUT_RDWR,
9884 /// Shutdown the TLS connection too (but only if it's alrady enabled), which
9885 /// is the default (disabling this will usually result in errors for the
9886 /// endpoint upon raw socket shutdown)
9887 const bool tls_shutdown = true,
9888 /// Whether to track for the destructor whether this method was called; by
9889 /// default, the destructor will automatically call `shutdown(SHUT_RDRW)` on
9890 /// an rsocket that wasn't closed before closing it unless this @c shutdown()
9891 /// was called at least once.
9892 const bool track_shutdown_for_destructor = true) {
9893 if (__debug) debug("shutdown(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9894 + ", " + std::to_string(how)
9897 // --------------------------------------------------------------------------
9898 // Track whether this method was called. If it was, then the destructor will
9899 // avoid calling it again because the developer may need to control the way
9900 // the socket is shutdown before closing.
9901 // --------------------------------------------------------------------------
9902 if (track_shutdown_for_destructor) __socket_got_shutdown = true;
9904 // --------------------------------------------------------------------------
9905 // Shut down TLS connections first, if TLS is enabled.
9907 // If SSL_shutdown returns a 0, then it means a "bidirectional shutdown" is
9908 // needed which entails calling SSL_shutdown a second time. The error codes
9909 // returned by SSL_shutdown are as follows:
9911 // 1 = Shutdown completed normally
9913 // 0 = Shutdown is not yet finished, and a second SSL_shutdown() is needed
9915 // -1 = Critical error, probably due to a connection problem, or because
9916 // the endpoint already initiated a shutdown, etc.
9918 // We don't really care what the second SSL_shutdown's return code is because
9919 // there's nothing we can do about it whatever it is. This situation is rare
9920 // though, and what follows next closes the raw socket, which is OpenSSL's
9921 // lifeline to communicating with the endpoint.
9922 // --------------------------------------------------------------------------
9923 if (__tls && tls_shutdown) {
9924 if (SSL_shutdown(__tls_fd) == 0) {
9925 SSL_shutdown(__tls_fd);
9926 } // -x- if SSL_shutdown -x-
9927 } // -x- if __tls -x-
9929 // --------------------------------------------------------------------------
9930 // Shut down the raw socket.
9931 // --------------------------------------------------------------------------
9932 __rc_check(::shutdown(__socket_fd, how));
9933 //__socket_connected = false; // TODO: Figure out when to change this to false
9936 } // -x- rsocket& shutdown -x-
9938 /*======================================================================*//**
9940 Complete the configuration of an rsocket that was previously initialized
9941 without any parameters (a.k.a., an "empty rsocket").
9942 @copydetails rsocket()
9943 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
9944 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
9945 @throws randolf::rex::xEALREADY If this socket() method was already used, or
9946 it was used after rsocket() initialized with at least one parameter
9947 @throws randolf::rex::xEINVAL Protocal family invalid or not available
9948 @throws randolf::rex::xEINVAL Invalid flags in type
9949 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
9950 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
9951 @throws randolf::rex::xENOBUFS Insufficient memory
9952 @throws randolf::rex::xENOMEM Insufficient memory
9953 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
9954 supported within the specified family (a.k.a., communication domain)
9955 @returns The same rsocket object so as to facilitate stacking
9956 @see get_socket_family()
9957 @see get_socket_fd()
9958 @see get_socket_protocol()
9959 @see get_socket_type()
9965 *///=========================================================================
9967 /// Communication domain; usually one of:@n
9968 /// AF_INET (IPv4)@n
9969 /// AF_INET6 (IPv6)@n
9970 /// AF_UNIX (UNIX domain sockets)
9972 /// Communication semantics; usually one of:@n
9973 /// SOCK_STREAM (common for TCP)@n
9974 /// SOCK_DGRAM (common for UDP)
9975 const int type = SOCK_STREAM,
9976 /// Network protocol; usually one of:@n
9977 /// IPPROTO_TCP / IPPROTO_MPTCP@n
9980 /// PF_UNSPEC (auto-detect)
9981 const int protocol = PF_UNSPEC) {
9982 __socket(family, type, protocol);
9984 } // -x- rsocket& socket -x-
9986 /*======================================================================*//**
9988 Set underlying socket descriptor/handle (to one that is presumed to be open).
9990 This method is only available while an underlying socket has not been created
9991 or previously assigned, such as after an empty @ref rsocket instantiation.
9992 @throws randolf::rex::xEALREADY If this socket_fd() method was already used,
9993 or it was used after socket() initialized it, or if rsocket() had
9994 initialized with at least one parameter that resulted in the creation
9995 of an underlying socket
9996 @returns The same rsocket object so as to facilitate stacking
9998 @see get_socket_family()
9999 @see get_socket_fd()
10000 @see get_socket_protocol()
10001 @see get_socket_type()
10003 *///=========================================================================
10004 rsocket& socket_fd(
10005 /// New socket descriptor/handle
10006 const int new_socket_fd) {
10007 if (__debug) debug("socket_fd(socket{0x" + randolf::rtools::to_hex(new_socket_fd) + "}"
10009 if (__socket_fd != 0) throw randolf::rex::xEALREADY("EALREADY");
10010 __socket_fd = new_socket_fd;
10011 __socket_addr->ss_family = getsockopt_int(SOL_SOCKET, SO_DOMAIN);
10012 __socket_type = getsockopt_int(SOL_SOCKET, SO_TYPE);
10013 __socket_protocol = getsockopt_int(SOL_SOCKET, SO_PROTOCOL);
10014 __socket_open = true;
10016 } // -x- rsocket& socket_fd -x-
10018 /*======================================================================*//**
10020 Find out whether the underlying socket is at the out-of-band (OOB) mark.
10022 @throws randolf::rex::xEBADF The underlying socket is not open
10023 @throws randolf::rex::xEINVAL The underlying socket file descriptor is not a
10024 type to which @ref sockatmark() can be applied
10026 @returns TRUE = at OOB mark
10027 @returns FALSE = not at OOB mark
10030 *///=========================================================================
10031 bool sockatmark() {
10032 return __rc_check(::sockatmark(__socket_fd)) ? true : false;
10033 } // -x- bool sockatmark -x-
10035 /*======================================================================*//**
10037 Configure the receive (read) or send (write/transmit) timeout on the
10040 Since setting the read timeout is such a common operation, this specialized
10041 method was created to ease software development efforts; internally we're
10042 just calling setsockopt(SOL_SOCKET, SO_RCVTIMEO, timeval).
10044 Although a timeout of 100,000 microseconds (1/10 of one second) may suffice
10045 in healthy and efficient networks, a more conservative setting of 1 second
10046 tends to minimally yield more reliable results. Many end-user applications
10047 use defaults of 3 to 5 seconds to cover a wider variety of unpredictable
10048 network connections (such as over shared wireless connections that are slow),
10049 and this setting should ultimately be configurable by users/administrators.
10052 The default timeout for new sockets is normally 0 (no timeout).
10054 @throws randolf::rex::xEBADF The underlying socket is not open
10055 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
10056 part of the user address space
10057 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
10058 valid for this socket's family (a.k.a., communication domain)
10059 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
10061 @throws randolf::rex::xENOTDIR If the @c direction parameter is supplied with
10062 an incorrect value (not @c SO_RCVTIMEO or @c SO_SNDTIMEO); this
10063 exception is normally a file-system related error, so we're using it
10064 here instead of EINVAL to make detecting this problem simpler for
10065 software developers (plus, "DIR" relates well to "direction"); this
10066 exception shouldn't need to be caught in the vast majority of uses,
10067 although one use where it should be caught is if the end-user has
10068 free reign to set the @c direction parameter to any value (e.g., a
10069 customizable software debugging interface)
10070 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
10071 doesn't refer to a socket
10073 @returns The same rsocket object so as to facilitate stacking
10077 @see timeout_recvline
10079 *///=========================================================================
10081 /// timeval structure
10082 const struct timeval tv,
10084 /// @c SO_RCVTIMEO (default) @n
10086 const int direction = SO_RCVTIMEO) {
10087 // TODO: Debug output
10089 // --------------------------------------------------------------------------
10090 // Set timeout for the specified direction.
10091 // --------------------------------------------------------------------------
10092 if (direction == SO_RCVTIMEO || direction == SO_SNDTIMEO) {
10093// setsockopt(SOL_SOCKET, direction, tv);
10094 __rc_check(::setsockopt(__socket_fd, SOL_SOCKET, direction, &tv, sizeof(tv)));
10096 } // -x- if direction -x-
10098 // --------------------------------------------------------------------------
10099 // Throw exception because direction is invalid.
10100 // --------------------------------------------------------------------------
10101 throw randolf::rex::xENOTDIR("invalid timeout direction (must be SO_RCVTIMEO or SO_SNDTIMEO)");
10103 } // -x- rsocket& timeout -x-
10105 /*======================================================================*//**
10106 @copydoc timeout(const struct timeval, const int)
10110 @see timeout_recvline
10112 *///=========================================================================
10114 /// Timeout in seconds
10116 /// Timeout in microseconds
10117 const long tv_usec = 0,
10119 /// @c SO_RCVTIMEO (default) @n
10121 const int direction = SO_RCVTIMEO) {
10122 // TODO: Debug output
10124 // --------------------------------------------------------------------------
10125 // Set timeout for the specified direction.
10126 // --------------------------------------------------------------------------
10127 if (direction == SO_RCVTIMEO || direction == SO_SNDTIMEO) {
10128 struct timeval tv{tv_sec, tv_usec};
10129// setsockopt(SOL_SOCKET, direction, tv);
10130 __rc_check(::setsockopt(__socket_fd, SOL_SOCKET, direction, &tv, sizeof(tv)));
10132 } // -x- if direction -x-
10134 // --------------------------------------------------------------------------
10135 // Throw exception because direction is invalid.
10136 // --------------------------------------------------------------------------
10137 throw randolf::rex::xENOTDIR("invalid timeout direction (must be SO_RCVTIMEO or SO_SNDTIMEO)");
10139 } // -x- rsocket& timeout -x-
10141 /*======================================================================*//**
10143 Set the read timeout for the @ref recvline() method (the @ref recvline()
10144 method's @c timeout parameter can override this setting).
10147 The default timeout for this recvline_timeout setting is 0 (no timeout).
10149 @throws randolf::rex::xERANGE if the timeout parameter is below 0
10151 @returns The same rsocket object so as to facilitate stacking
10152 @see get_timeout_recvline
10156 @see timeout_recvline
10158 *///=========================================================================
10159 rsocket& timeout_recvline(
10160 /// timeval structure
10161 const long timeout) {
10162 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
10163 __recvline_timeout = timeout;
10165 } // -x- rsocket& timeout_recvline -x-
10167 /*======================================================================*//**
10169 Enable or disable encrypted communications (from the OpenSSL library).
10172 TLS cannot be enabled before a non-encrypted rsocket is open (an rsocket is
10173 typically opened with the @ref socket() method, the @ref connect() method, or
10174 one of the @ref accept() methods). If calling @c tls(true) on an rsocket
10175 that isn't open, an exception will be thrown.
10177 If needed, a new TLS context will be instantiated and TLS will be initialized
10178 (if this hasn't already been done). TLS instantiation can be done first by
10179 calling the @ref tls_ctx() method (regardless of whether encryption is being
10180 enabled or disabled). If the default @ref TLS_FLAGS aren't sufficient for
10181 the needs of your application, then the @ref tls_ctx() method facilitates
10182 this regardless of wehther rsocket is open.
10185 The reason a TLS context is instantiated and TLS is initialized even when the
10186 status is being set to FALSE is to facilitate TLS ingress from an unencrypted
10187 connection later in the session (see the @ref TLS_NO_INGRESS flag for more
10191 If a client attempts to upgrade a TLS connection to TLS (e.g., by using a
10192 command such as @c STARTTLS, which is commonly transmitted in unencrypted
10193 form, like @c telnet-ssl does -- using `telnet-ssl -z ssl` prevents this
10194 condition), the following error that's difficult to track down may be
10195 triggered when calling any of the @c recv methods (I hope that including this
10196 information here in this documentation will be helpful):
10198 SSL_ERROR_UNKNOWN: error:0A00010B:SSL routines::wrong version number
10200 This is most likely not a programming error, but rather a problem with how
10201 users may attempt to mis-use a connection based on a misunderstanding of the
10202 communications requirements (e.g., connecting unencrypted and attempting to
10203 upgrade to TLS over a connection that's expecting TLS encrypted data from the
10204 very beginning, without involving any ingress).
10206 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10207 OpenSSL library doesn't document which errors may be returned)
10209 @returns The same rsocket object so as to facilitate stacking
10213 *///=========================================================================
10215 /// TRUE = Enable encrypted communications@n FALSE = Disable encrypted communications
10216 const bool status = true,
10217 /// Configuration parameters
10218 const int flags = TLS_FLAGS::TLS_DEFAULT) { // | TLS_FLAGS::TLS_CLIENT
10219 if (__debug) debug("tls(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
10220 + ", " + (status ? "true" : "false")
10223 // --------------------------------------------------------------------------
10224 // Create default context (with "flags" passthrough), unless it was already
10225 // created (usually by one of the tls_ctx() methods).
10226 // --------------------------------------------------------------------------
10227 if (__tls_ctx == nullptr) tls_ctx((SSL_CTX*)nullptr, flags);
10229 // --------------------------------------------------------------------------
10230 // Make sure tls_fd (ssl_fd) is allocated. If not, then it needs to be
10231 // allocated and configured.
10232 // --------------------------------------------------------------------------
10233 if (status == true && __tls_fd == nullptr) {
10234 __tls_fd = SSL_new(__tls_ctx);
10235 if (__debug) debug("SSL_set_fd(<tls_fd>, socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
10238 // --------------------------------------------------------------------------
10239 // Associate OpenSSL file descriptor with underlying socket file descriptor.
10240 // --------------------------------------------------------------------------
10241 __rc_check_tls(SSL_set_fd(__tls_fd, __socket_fd));
10243 // --------------------------------------------------------------------------
10244 // Enable read-ahead so that SSL_peek will work more efficiently.
10245 // --------------------------------------------------------------------------
10246 if (!(flags & TLS_NO_READ_AHEAD)) {
10247 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
10248 SSL_set_read_ahead(__tls_fd, 1); // 0=disable / 1=enable
10249 } // -x- if !TLS_NO_READ_AHEAD -x-
10251// SSL_CTX_set_max_pipelines(__tls_ctx, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
10252// SSL_set_max_pipelines(__tls_fd, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
10254// SSL_CTX_set_mode(__tls_ctx, SSL_MODE_AUTO_RETRY);
10255// SSL_set_mode(__tls_fd, SSL_MODE_AUTO_RETRY);
10257 } // -x- if !__tls_fd -x-
10260 } // -x- rsocket& tls -x-
10262 /*======================================================================*//**
10264 Return the current TLS context (multiple TLS contexts are supported, although
10265 typically needed to support SNI with inbound connections).
10266 @returns Pointer to OpenSSL's context (normally labelled @c SSL_CTX* in the
10267 documentation for OpenSSL), or nullptr if this context was never
10268 assigned to (or created by) this rsocket
10271 *///=========================================================================
10272 SSL_CTX* tls_ctx() noexcept {
10274 } // -x- SSL_CTX* tls_ctx -x-
10276 /*======================================================================*//**
10278 Copy the source rsocket's TLS context map and add it to this rsocket's
10279 collection; or, if the source doesn't have any TLS contexts and this rsocket
10280 doesn't have any TLS contexts in its collection, then initialize TLS and
10281 instantiate a new TLS context. In either scenario, the source rsocket will
10282 be treated as a template as all TLS flags duplicated to enable encrypted
10283 socket I/O for use in this rsocket().
10286 At least one TLS context is needed to enable encrypted socket I/O for use in
10290 Encrypted socket I/O is only possible after a TLS context has been
10291 initialized (this is not a global setting as it has per-rsocket specificity).
10294 The only @ref TLS_FLAGS flag that doesn't get transferred is @ref TLS_SERVER
10295 when no flags are specified. Specifying any flag(s) will cause this method
10296 to ignore the source rsocket's TLS flags so as to defer to this override.
10298 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10299 OpenSSL library doesn't document which errors may be returned)
10301 @returns The same rsocket object so as to facilitate stacking
10304 *///=========================================================================
10306 /// An @c rsocket object from which to duplicate OpenSSL's TLS context, plus
10307 /// any @c TLS_FLAGS that were set differently from @c rsocket defaults
10308 rsocket& rtemplate,
10309 /// Configuration parameters
10310 const int flags = TLS_FLAGS::TLS_DEFAULT) {
10311 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
10313 + ", " + std::to_string(flags)
10316 // --------------------------------------------------------------------------
10317 // TLS-related variables (OpenSSL).
10319 // Note: The __tls_fd variable cannot be allocated until after __socket_fd
10320 // has been, hence the "post" note in the documentation.
10321 // --------------------------------------------------------------------------
10322 __tls = (bool)rtemplate.__tls; // Preserve TLS mode setting
10323 if (rtemplate.__tls_ctx != nullptr) { // If TLS context is defined, then do these important things:
10324 __tls_ctx = rtemplate.__tls_ctx; // 1. copy the pointer to SSL_CTX
10325 SSL_CTX_up_ref(__tls_ctx); // 2. increment SSL_CTX's internal reference count
10326 } // -x- if __tlx_ctx -x-
10328 // --------------------------------------------------------------------------
10329 // Copy or override TLS flags.
10330 // --------------------------------------------------------------------------
10331 if (flags == TLS_FLAGS::TLS_DEFAULT) {
10332 __tls_exclusive = rtemplate.__tls_exclusive; // TLS policy
10333 __tls_egress = rtemplate.__tls_egress; // TLS policy
10334 __tls_ingress = rtemplate.__tls_ingress; // TLS policy
10335 __tls_no_read_ahead = rtemplate.__tls_ingress; // TLS policy
10336 } else { // Save flags
10337 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
10338 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
10339 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
10340 __tls_server_mode = flags & TLS_FLAGS::TLS_SERVER;
10341 __tls_no_read_ahead = flags & TLS_FLAGS::TLS_NO_READ_AHEAD;
10342 } // -x- if flags -x-
10345 } // -x- rsocket& tls_ctx -x-
10347 /*======================================================================*//**
10349 Initialize TLS and instantiate a TLS context, and add it to this rsocket's
10350 current collection of TLS contexts, and set it as the currently active TLS
10351 context (so that a certificate chain and private key may be added to it).
10353 At least one TLS context is needed to enable encrypted socket I/O for use in
10356 Encrypted socket I/O is only possible after a TLS context has been
10357 initialized (this is not a global setting as it has per-rsocket specificity).
10359 This is the default TLS context for this @c rsocket, which will also be used
10360 for non-SNI handshakes.
10362 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10363 OpenSSL library doesn't document which errors may be returned)
10365 @returns The same rsocket object so as to facilitate stacking
10368 *///=========================================================================
10370 /// OpenSSL's TLS context to use (if not provided because @c nullptr was
10371 /// specified, a new TLS context will be created automatically that uses
10372 /// OpenSSL's defaults, which should be fine for most scenarios)
10374 /// Configuration parameters
10375 const int flags = TLS_FLAGS::TLS_DEFAULT) {
10376 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
10378 + ", " + std::to_string(flags)
10381 // --------------------------------------------------------------------------
10382 // Ignore repeated calls to this method.
10383 // --------------------------------------------------------------------------
10384 if (__tls_ctx != nullptr) return *this;
10386 // --------------------------------------------------------------------------
10387 // Fire up OpenSSL's algorithms and pre-load its error strings.
10389 // These two functions have been deprecated since OpenSSL v1.1.0, so we don't
10390 // need to call them. If someone needs them, then they can always call them
10391 // in their own code.
10392 // --------------------------------------------------------------------------
10393 //OpenSSL_add_all_algorithms(); // Load and register cryptography algorithms
10394 //SSL_load_error_strings(); // Load all error messages into memory
10396 // --------------------------------------------------------------------------
10397 // Save (ctx != nullptr) or create (ctx == nullptr) OpenSSL context.
10398 // --------------------------------------------------------------------------
10399 __tls_ctx = ctx == nullptr ? SSL_CTX_new(flags & TLS_FLAGS::TLS_SERVER ? TLS_server_method() : TLS_client_method()) // Create anew (default)
10400 : ctx; // Use OpenSSL context that was provided
10401 if (__tls_ctx == nullptr) randolf::rex::mk_exception("Cannot create TLS context", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
10403 // --------------------------------------------------------------------------
10404 // Enable read-ahead so that SSL_peek will work properly.
10405 // --------------------------------------------------------------------------
10406 if (!(flags & TLS_NO_READ_AHEAD)) {
10407 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
10408 } // -x- if !TLS_NO_READ_AHEAD -x-
10410 // --------------------------------------------------------------------------
10412 // --------------------------------------------------------------------------
10413 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
10414 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
10415 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
10416 __tls_server_mode = flags & TLS_FLAGS::TLS_SERVER;
10417 __tls_no_read_ahead = flags & TLS_FLAGS::TLS_NO_READ_AHEAD;
10420 } // -x- rsocket& tls_ctx -x-
10422 /*======================================================================*//**
10424 Check the private key it to ensure it's consistent with the corresponding TLS
10427 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10428 OpenSSL library doesn't document which errors may be returned)
10430 @returns The same rsocket object so as to facilitate stacking
10431 @see tls_ctx_use_privatekey_file
10432 @see tls_ctx_use_privatekey_pem
10434 *///=========================================================================
10435 rsocket& tls_ctx_check_privatekey() {
10436 if (__debug) debug("tls_ctx_check_privatekey();");
10437 if (!SSL_CTX_check_private_key(__tls_ctx)) randolf::rex::mk_exception("Cannot validate consistency between certificate chain and private key", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
10439 } // -x- rsocket& tls_ctx_check_privatekey -x-
10441 /*======================================================================*//**
10443 Load a TLS certificate chain and private key in PEM format from text files
10444 and use them in the TLS context.
10446 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10447 OpenSSL library doesn't document which errors may be returned)
10449 @returns The same rsocket object so as to facilitate stacking
10450 @see tls_ctx_use_certificate_chain_and_privatekey_pems
10451 @see tls_ctx_use_certificate_chain_file
10452 @see tls_ctx_use_certificate_chain_pem
10453 @see tls_ctx_use_privatekey_file
10454 @see tls_ctx_use_privatekey_pem
10455 @see tls_ctx_check_privatekey
10457 *///=========================================================================
10458 rsocket& tls_ctx_use_certificate_chain_and_privatekey_files(
10459 /// Pointer to ASCIIZ path and filename to certificate chain file (@c nullptr
10460 /// will simply be ignored)
10461 const char* chain_file,
10462 /// Pointer to ASCIIZ path and filename to private key file (@c nullptr will
10463 /// simply be ignored)
10464 const char* key_file) {
10465 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
10466 + std::string(chain_file)
10467 + ", " + std::string( key_file)
10469 if (chain_file != nullptr) tls_ctx_use_certificate_chain_file(chain_file);
10470 if ( key_file != nullptr) tls_ctx_use_privatekey_file( key_file);
10472 } // -x- rsocket& tls_ctx_use_certificate_chain_and_privatekey_files -x-
10474 /*======================================================================*//**
10476 Load a TLS certificate chain and private key in PEM format from text files
10477 and use them in the TLS context.
10479 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10480 OpenSSL library doesn't document which errors may be returned)
10482 @returns The same rsocket object so as to facilitate stacking
10483 @see tls_ctx_use_certificate_chain_and_privatekey_pems
10484 @see tls_ctx_use_certificate_chain_file
10485 @see tls_ctx_use_certificate_chain_pem
10486 @see tls_ctx_use_privatekey_file
10487 @see tls_ctx_use_privatekey_pem
10488 @see tls_ctx_check_privatekey
10490 *///=========================================================================
10491 rsocket& tls_ctx_use_certificate_chain_and_privatekey_files(
10492 /// Pointer to ASCIIZ path and filename to certificate chain file (an empty
10493 /// string will simply be ignored)
10494 const std::string chain_file,
10495 /// Pointer to ASCIIZ path and filename to private key file (an empty string
10496 /// will simply be ignored)
10497 const std::string key_file) {
10498 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
10502 if (!chain_file.empty()) tls_ctx_use_certificate_chain_file(chain_file);
10503 if ( !key_file.empty()) tls_ctx_use_privatekey_file( key_file);
10505 } // -x- rsocket& tls_ctx_use_certificate_chain_and_privatekey_files -x-
10507 /*======================================================================*//**
10509 Load a TLS certificate chain and a TLS private key in PEM format from memory
10510 and use them in the TLS context.
10512 Although this functionality doesn't exist in OpenSSL (at the time of writing
10513 this method), it's provided here in a manner that has exactly the same effect
10514 as the @ref tls_ctx_use_certificate_chain_and_privatekey_files() methods, but
10515 without needing the PEM-formatted certificate chain stored in files
10519 The @c cert_pem_data and key_pem_data parameters are pointers to the memory
10520 locations that holds the PEM formatted certificate chain data and private key
10521 data, respectively. If the corresponding lengths of each of these data aren't
10522 specified or are set to zero (default), then they will be treated as multiline
10525 Behind the scenes, we're just writing the cert_pem_data and key_pem_data
10526 memory to temporary files with severely-limited permissions (), then
10527 optionally overwriting those temporary files with random data prior to
10528 deleting them (this is the default, since better security practices should be
10529 the default, but on a secured system it may not be necessary and so this
10530 option can also be disabled to save CPU cycles and reduce overall disk-write
10533 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10534 OpenSSL library doesn't document which errors may be returned)
10536 @returns The same rsocket object so as to facilitate stacking
10537 @see tls_ctx_use_certificate_chain_and_privatekey_files
10538 @see tls_ctx_use_certificate_chain_file
10539 @see tls_ctx_use_certificate_chain_pem
10540 @see tls_ctx_use_privatekey_file
10541 @see tls_ctx_use_privatekey_pem
10542 @see tls_ctx_check_privatekey
10544 *///=========================================================================
10545 rsocket& tls_ctx_use_certificate_chain_and_privatekey_pems(
10546 /// Pointer to certificate chain data in PEM format
10547 const char* cert_pem_data,
10548 /// Pointer to private key data in PEM format
10549 const char* key_pem_data,
10550 /// Length of cert_pem_data (in bytes), or 0 to auto-detect length if cert_pem_data is an ASCIIZ string
10551 size_t cert_len = 0,
10552 /// Length of key_pem_data (in bytes), or 0 to auto-detect length if key_pem_data is an ASCIIZ string
10553 size_t key_len = 0,
10554 /// Whether to overwrite the temporary files with random data before deleting them
10555 const bool random_fill = true) {
10556 tls_ctx_use_certificate_chain_pem(cert_pem_data, cert_len, random_fill);
10557 tls_ctx_use_privatekey_pem( key_pem_data, key_len, random_fill);
10559 } // -x- rsocket& tls_ctx_use_certificate_chain_and_privatekey_pems -x-
10561 /*======================================================================*//**
10563 Load a TLS certificate chain in PEM format from a text file and use it in the
10566 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10567 OpenSSL library doesn't document which errors may be returned)
10569 @returns The same rsocket object so as to facilitate stacking
10570 @see tls_ctx_use_certificate_chain_file
10571 @see tls_ctx_use_certificate_chain_pem
10572 @see tls_ctx_check_privatekey
10574 *///=========================================================================
10575 rsocket& tls_ctx_use_certificate_chain_file(
10576 /// Pointer to ASCIIZ path and filename to certificate chain file
10577 const char* file) {
10578 if (__debug) debug("tls_ctx_use_certificate_chain_file("
10579 + std::string(file)
10581 if (SSL_CTX_use_certificate_chain_file(__tls_ctx, file) != 1) randolf::rex::mk_exception(std::string("Cannot use certificate chain file ") + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
10583 } // -x- rsocket& tls_ctx_use_certificate_chain_file -x-
10585 /*======================================================================*//**
10587 Load a TLS certificate chain in PEM format from a text file and use it in the
10590 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10591 OpenSSL library doesn't document which errors may be returned)
10593 @returns The same rsocket object so as to facilitate stacking
10594 @see tls_ctx_use_certificate_chain_file
10595 @see tls_ctx_use_certificate_chain_pem
10596 @see tls_ctx_check_privatekey
10598 *///=========================================================================
10599 rsocket& tls_ctx_use_certificate_chain_file(
10600 /// Path and filename to certificate chain file
10601 const std::string& file) {
10602 if (__debug) debug("tls_ctx_use_certificate_chain_file("
10605 if (SSL_CTX_use_certificate_chain_file(__tls_ctx, file.data()) != 1) randolf::rex::mk_exception("Cannot use certificate chain file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
10607 } // -x- rsocket& tls_ctx_use_certificate_chain_file -x-
10609 /*======================================================================*//**
10611 Load a TLS certificate chain in PEM format from memory and use it in the TLS
10614 Although this functionality doesn't exist in OpenSSL (at the time of writing
10615 this method), it's provided here in a manner that has exactly the same effect
10616 as the @ref tls_ctx_use_certificate_chain_file() methods, but without needing
10617 the PEM-formatted certificate chain stored in a file beforehand.
10620 The @c pem_data parameter is a pointer to the memory location that holds
10621 the PEM formatted certificate chain data. If the length of this data isn't
10622 specified or is set to zero (default), then it will be treated as a multiline
10625 Behind the scenes, we're just writing the pem_data memory to a temporary
10626 file with severely-limited permissions (), then optionally overwriting that
10627 temporary file with random data prior to deleting it (this is the default,
10628 since better security practices should be the default, but on a secured
10629 system it may not be necessary and so this option can also be disabled to
10630 save CPU cycles and reduce overall disk-write I/O operations).
10632 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10633 OpenSSL library doesn't document which errors may be returned)
10635 @returns The same rsocket object so as to facilitate stacking
10636 @see tls_ctx_use_certificate_chain_file
10637 @see tls_ctx_check_privatekey
10639 *///=========================================================================
10640 rsocket& tls_ctx_use_certificate_chain_pem(
10641 /// Pointer to certificate chain data in PEM format
10642 const char* pem_data,
10643 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
10645 /// Whether to overwrite the temporary file with random data before deleting it
10646 const bool random_fill = true) {
10647 if (__debug) debug("tls_ctx_use_certificate_chain_pem(<buf>, " + std::to_string(len)
10648 + ", " + (random_fill ? "true" : "false")
10651 // --------------------------------------------------------------------------
10652 // Measure size of certificate chain if an ASCIIZ string was indicated.
10653 // --------------------------------------------------------------------------
10654 if (len == 0) len = std::strlen(pem_data);
10656 // --------------------------------------------------------------------------
10657 // Generate filename for temporary use.
10658 // --------------------------------------------------------------------------
10659 std::string file = std::filesystem::temp_directory_path();
10660 file.append("/rsocket.")
10661 .append(std::to_string(::getpid()))
10663 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
10665 // --------------------------------------------------------------------------
10666 // Open temporary file.
10667 // --------------------------------------------------------------------------
10668 FILE* fp = fopen(file.data(), "w+");
10669 if (fp == nullptr) randolf::rex::mk_exception("Cannot open temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
10671 ioctl(fileno(fp), FS_IOC_GETFLAGS, &attr);
10672 attr |= FS_NOATIME_FL // Don't update access time attribute
10673 | FS_NODUMP_FL // Don't include in filesystem backup dumps
10674 | FS_SECRM_FL; // Mark file for secure deletion (where supported)
10675 ioctl(fileno(fp), FS_IOC_SETFLAGS, &attr);
10677 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
10678 if (fputs(pem_data, fp) == EOF) {
10680 randolf::rex::mk_exception("Cannot write to temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
10681 } // -x- if !fputs -x-
10684 // --------------------------------------------------------------------------
10685 // Attempt to load certificate chain file, but save the error code for later
10686 // because we need to clean up the temporary file before possibly throwing an
10688 // --------------------------------------------------------------------------
10689 int rc = SSL_CTX_use_certificate_chain_file(__tls_ctx, file.data());
10691 // --------------------------------------------------------------------------
10692 // Overwrite the contenst of the temporary file before deleting it so as to
10693 // sabotage a simple attempt to undelete the file and access the certificate.
10695 // We're also re-using the "len" local variable because it's not needed once
10696 // we get the loop started, and it's more optimal to not allocate yet another
10697 // local variable while "len" goes to waste. :D
10698 // --------------------------------------------------------------------------
10699 if (random_fill) { // This option is configurable
10700 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
10701 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
10702 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
10703 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
10704 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
10706 } // -x- if randfill -x-
10707 fchmod(fileno(fp), 0); // Remove all permissions
10708 fclose(fp); // Close file handle
10709 unlink(file.data()); // Delete temporary file
10711 // --------------------------------------------------------------------------
10712 // Error check ... was delayed here until after temporary file cleanup.
10713 // --------------------------------------------------------------------------
10714 if (rc != 1) randolf::rex::mk_exception("Cannot use certificate chain file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
10717 } // -x- rsocket& tls_ctx_use_certificate_chain_pem -x-
10719 /*======================================================================*//**
10721 Load a TLS private key in PEM format from a text file and use it in the TLS
10724 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10725 OpenSSL library doesn't document which errors may be returned)
10727 @returns The same rsocket object so as to facilitate stacking
10728 @see tls_ctx_use_privatekey_file
10729 @see tls_ctx_use_privatekey_pem
10730 @see tls_ctx_check_privatekey
10732 *///=========================================================================
10733 rsocket& tls_ctx_use_privatekey_file(
10734 /// Pointer to ASCIIZ path-and-filename of private key file
10735 const char* file) {
10736 if (__debug) debug("tls_ctx_use_privatekey_file("
10737 + std::string(file)
10739 if (SSL_CTX_use_PrivateKey_file(__tls_ctx, file, SSL_FILETYPE_PEM) != 1) randolf::rex::mk_exception(std::string("Cannot use private key file ") + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
10742 } // -x- rsocket& tls_ctx_use_privatekey_file -x-
10744 /*======================================================================*//**
10746 Load a TLS private key in PEM format from a text file and use it in the TLS
10749 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10750 OpenSSL library doesn't document which errors may be returned)
10752 @returns The same rsocket object so as to facilitate stacking
10753 @see tls_ctx_use_privatekey_file
10754 @see tls_ctx_use_privatekey_pem
10755 @see tls_ctx_check_privatekey
10757 *///=========================================================================
10758 rsocket& tls_ctx_use_privatekey_file(
10759 /// Path and filename to private key file
10760 const std::string& file) {
10761 if (__debug) debug("tls_ctx_use_privatekey_file("
10764 if (SSL_CTX_use_PrivateKey_file(__tls_ctx, file.data(), SSL_FILETYPE_PEM) != 1) randolf::rex::mk_exception("Cannot use private key file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
10767 } // -x- rsocket& tls_ctx_use_privatekey_file -x-
10769 /*======================================================================*//**
10771 Load a TLS private key in PEM format from memory and use it in the TLS
10774 Although this functionality doesn't exist in OpenSSL (at the time of writing
10775 this method), it's provided here in a manner that has exactly the same effect
10776 as the @ref tls_ctx_use_privatekey_file() methods, but without needing the
10777 PEM-formatted private key stored in a file beforehand.
10780 The @c pem_data parameter is a pointer to the memory location that holds the
10781 PEM formatted private key data. If the length of this data isn't specified
10782 or is set to zero (default), then it will be treated as a multiline ASCIIZ
10785 Behind the scenes, we're just writing the pem_data memory to a temporary
10786 file (with severely-limited permissions), then optionally overwriting that
10787 temporary file with random data prior to deleting it (this is the default,
10788 since better security practices should be the default, but on a secured
10789 system it may not be necessary and so this option can also be disabled to
10790 save CPU cycles and reduce overall disk-write I/O operations).
10792 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10793 OpenSSL library doesn't document which errors may be returned)
10795 @returns The same rsocket object so as to facilitate stacking
10796 @see tls_ctx_use_privatekey_file
10797 @see tls_ctx_check_privatekey
10799 *///=========================================================================
10800 rsocket& tls_ctx_use_privatekey_pem(
10801 /// Pointer to private key data in PEM format
10802 const char* pem_data,
10803 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
10805 /// Whether to overwrite the temporary file with random data before deleting it
10806 const bool random_fill = true) {
10807 if (__debug) debug("tls_ctx_use_privatekey_pem(<buf>, " + std::to_string(len)
10808 + ", " + (random_fill ? "true" : "false")
10811 // --------------------------------------------------------------------------
10812 // Measure size of private key if an ASCIIZ string was indicated.
10813 // --------------------------------------------------------------------------
10814 if (len == 0) len = std::strlen(pem_data);
10816 // --------------------------------------------------------------------------
10817 // Generate filename for temporary use.
10818 // --------------------------------------------------------------------------
10819 std::string file = std::filesystem::temp_directory_path();
10820 file.append("/rsocket.")
10821 .append(std::to_string(::getpid()))
10823 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
10825 // --------------------------------------------------------------------------
10826 // Open temporary file.
10827 // --------------------------------------------------------------------------
10828 FILE* fp = fopen(file.data(), "w+");
10829 if (fp == nullptr) randolf::rex::mk_exception("Cannot cannot open temporary file (to use private key) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
10830 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
10831 if (fputs(pem_data, fp) == EOF) {
10833 randolf::rex::mk_exception("Cannot cannot write to temporary file (to use private key) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
10834 } // -x- if !fputs -x-
10837 // --------------------------------------------------------------------------
10838 // Attempt to load private key file, but save the error code for later
10839 // because we need to clean up the temporary file before possibly throwing an
10841 // --------------------------------------------------------------------------
10842 int rc = SSL_CTX_use_PrivateKey_file(__tls_ctx, file.data(), SSL_FILETYPE_PEM);
10844 // --------------------------------------------------------------------------
10845 // Overwrite the contenst of the temporary file before deleting it so as to
10846 // sabotage a simple attempt to undelete the file and access the certificate.
10848 // We're also re-using the "len" local variable because it's not needed once
10849 // we get the loop started, and it's more optimal to not allocate yet another
10850 // local variable while "len" goes to waste. :D
10851 // --------------------------------------------------------------------------
10852 if (random_fill) { // This option is configurable
10853 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
10854 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
10855 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
10856 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
10857 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
10859 } // -x- if randfill -x-
10860 fchmod(fileno(fp), 0); // Remove all permissions
10861 fclose(fp); // Close file handle
10862 unlink(file.data()); // Delete temporary file
10864 // --------------------------------------------------------------------------
10865 // Error check ... was delayed here until after temporary file cleanup.
10866 // --------------------------------------------------------------------------
10867 if (rc != 1) randolf::rex::mk_exception("Cannot use private key file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
10870 } // -x- rsocket& tls_ctx_use_privatekey_pem -x-
10872 /*======================================================================*//**
10874 Initiate the TLS handshake with the endpoint (which is presumed to be a
10876 This method makes it easier to support application-level commands such as @c
10877 STARTTLS (which are implemented in protocols like SMTP, POP3, IMAP4, MEOW,
10878 FTP, NNTP, LDAP, XMPP, etc.).
10880 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10881 OpenSSL library doesn't document which errors may be returned)
10883 @returns The same rsocket object so as to facilitate stacking
10885 @see connect(std::string, int)
10886 @see TLS_NO_INGRESS
10888 *///=========================================================================
10889 rsocket& tls_do_handshake() {
10890 if (__debug) debug("tls_handshake(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
10892 __rc_check_tls(SSL_do_handshake(__tls_fd));
10894 } // -x- rsocket& tls_do_handshake -x-
10896 /*======================================================================*//**
10898 Get OpenSSL's TLS structure.
10899 @returns TLS structure
10900 @returns nullptr = TLS structure not yet allocated
10902 *///=========================================================================
10903 const SSL* tls_fd() noexcept {
10905 } // -x- int tls_fd -x-
10907 /*======================================================================*//**
10909 Return the current @ref rsocket_sni object that this @c rsocket will use when
10910 accepting incoming encrypted connections.
10911 @returns Pointer to @c rsocket_sni object
10912 @returns nullptr = SNI is not assigned
10914 @see tls_sni(rsocket_sni*)
10916 *///=========================================================================
10917 rsocket_sni* tls_sni() noexcept {
10919 } // -x- rsocket_sni* tls_sni -x-
10921 /*======================================================================*//**
10923 Set the current @ref rsocket_sni object that this @c rsocket will use when
10924 accepting incoming encrypted connections.
10926 Use the @ref name() method to find out which server name was supplied by the
10927 endpoint that triggered the SNI callback, regardless of whether it matches
10928 any of the TLS certificates used with this rsocket object or the rsocket_sni
10929 object that's associated with this rsocket object. If an SNI callback wasn't
10930 triggered, or if the endpoint didn't provide a server name, then it will
10931 remain unaffected (and the default {empty string} will remain unchanged).
10932 @returns The same rsocket object so as to facilitate stacking
10935 @see is_tls_sni_match
10937 *///=========================================================================
10939 /// Pointer to the @ref rsocket_sni object to use, or specify @c nullptr to
10940 /// remove SNI support from this rsocket object
10941 rsocket_sni* sni) noexcept {
10943 // --------------------------------------------------------------------------
10944 // Remove SNI support.
10945 // --------------------------------------------------------------------------
10946 if (sni == nullptr) {
10948 // --------------------------------------------------------------------------
10949 // Only remove SNI support if it's configured.
10950 // --------------------------------------------------------------------------
10951 if (__tls_sni != nullptr) SSL_CTX_set_client_hello_cb(__tls_ctx, nullptr, this);
10953 // --------------------------------------------------------------------------
10954 // Add or set SNI support.
10955 // --------------------------------------------------------------------------
10958 // --------------------------------------------------------------------------
10959 // Configure SNI callbacks for TLS. This callback will occur in the early
10960 // stages, which is triggered by the SSL_accept() method.
10961 // --------------------------------------------------------------------------
10962 SSL_CTX_set_client_hello_cb(__tls_ctx, tls_sni_callback, this);
10964 } // -x- if !sni -x-
10966 // --------------------------------------------------------------------------
10967 // Update internal pointer to the SNI map, or nullptr if it's being removed.
10968 // --------------------------------------------------------------------------
10969 __tls_sni = sni; // TODO: Do we need to free/delete the old __tls_sni as memory leak prevention?
10972 } // -x- rsocket_sni& tls_sni -x-
10975 /*======================================================================*//**
10977 Get OpenSSL's TLS structure.
10978 @returns TLS structure
10979 @returns nullptr = TLS structure not yet allocated
10981 *///=========================================================================
10982 int static tls_sni_callback(
10983 /// OpenSSL's socket descriptor/handle
10985 /// Where to store the @c alert value
10987 /// Context-specific argument
10988 void* arg) noexcept {
10990 // --------------------------------------------------------------------------
10991 // Internal variables.
10992 // --------------------------------------------------------------------------
10993 rsocket* r = (rsocket*)arg; // We may be updating the TLS context (this is normally the new client's rsocket object)
10994 const unsigned char* out = nullptr;
10995 size_t out_size = 0;
10997 // --------------------------------------------------------------------------
10998 // Obtain a pointer to newly-allocated ClientHello fields data. If *out is
10999 // nullptr, it means that no TLSEXT_TYPE_server_name was received from the
11000 // client, and so the default TLS context will suffice. If out_size is less
11001 // than or equal to 2, then it also won't have what we need.
11002 // --------------------------------------------------------------------------
11003 if (!SSL_client_hello_get0_ext(tls_fd, TLSEXT_TYPE_server_name, &out, &out_size)
11005 || out_size <= 2) {
11006 //std::cout << "no server_name / out_size=" << out_size << std::endl;
11007 r->__tls_sni_has_name = false; // No SNI name
11008 r->__tls_sni_match = false; // No SNI match
11009 return SSL_CLIENT_HELLO_SUCCESS; // 1
11011// if (!SSL_client_hello_get0_ext(tls_fd, TLSEXT_TYPE_server_name, &out, &out_size)
11012// || out == nullptr || out_size <= 2) return SSL_CLIENT_HELLO_SUCCESS; // 1
11014 // --------------------------------------------------------------------------
11015 // Scan for TLSEXT_NAMETYPE_host_name, but if we can't find it then we'll
11016 // just leave the default TLS context as is.
11017 // --------------------------------------------------------------------------
11018 unsigned char* pos = (unsigned char*)out;
11019 size_t len = (*(pos++) << 8); // Extract first MSB
11020 len += *(pos++); // Add first LSB
11021//std::cout << "scan for host_name / out_size=" << out_size << " len=" << len << std::endl;
11022 if (len + 2 != out_size) return SSL_CLIENT_HELLO_SUCCESS; // 1 // goto finish;
11025 // --------------------------------------------------------------------------
11026 // We're taking a shortcut by examining only the first element in the list,
11027 // but in the future we need to make this more robust in case other types of
11028 // elements precede what we're looking for.
11030 // Unfortunately, there's no documentation that properly-explains the format
11031 // of the list, so some deeper research into OpenSSL's source code will be
11032 // needed (a cursory look so far has not yielded the necessary insight).
11034 // TODO: Turn this into a loop that supports future clients that provide
11035 // multiple SNI server names in their requests. (Although this isn't
11036 // occuring at present with common end-user tools such as web browsers
11037 // and eMail software, it may happen in the future as client/server
11038 // software becomes more sophisticated to support users, some of whom
11039 // are gradually becoming more savvy.)
11040 // --------------------------------------------------------------------------
11041 if (out_size <= 3 || *pos++ != TLSEXT_NAMETYPE_host_name) return SSL_CLIENT_HELLO_SUCCESS; // 1 // goto finish;
11042 out_size--; // Avoid a buffer overrun caused by corrupt or prematurely-truncated data
11044//std::cout << "out_size=" << out_size << std::endl;
11046 // --------------------------------------------------------------------------
11047 // Extract and use the hostname (SNI server name) that was supplied by the
11048 // endpoint so that the correct TLS certificate can be selected and assigned.
11049 // --------------------------------------------------------------------------
11050 len = (*(pos++) << 8); // Extract second MSB
11051 len += *(pos++); // Add second LSB
11052 if (!(len + 2 > out_size)) { // Only process SNI server name string if it's at least 2 bytes long
11053 // Debug: std::cout << "Server name: " << (const char*)p << std::endl; // Debug
11055//std::cout << "out_size=" << out_size << " len=" << len << std::endl;
11057 // --------------------------------------------------------------------------
11058 // Obtain the correct TLS context (wildcards supported) that is associated
11059 // with the hostname (SNI server name) that was supplied by the endpoint.
11060 // --------------------------------------------------------------------------
11061 const char* sni_name = (const char*)pos;
11062 r->__name_sni.assign(sni_name, len);
11063 SSL_CTX* new_ctx = r->__tls_sni->get_ctx(r->__name_sni.data(), true); // Third parameter (nullptr by default) is the default context (e.g., r->tls_ctx()) if SNI does not match
11064 r->__tls_sni_has_name = sni_name[0] != 0; // If the ASCIIZ string is empty, then SNI didn't receive a hostname or the hostname entry was empty
11065 r->__tls_sni_match = new_ctx != nullptr; // Track whether sni_name matches any of the certificates that are stored in rsocket_sni
11067 // --------------------------------------------------------------------------
11068 // Change TLS context so that encryption with the endpoint uses the correct
11069 // TLS certificate (otherwise the endpoint will indicate security risk errors
11070 // to end users, log files, etc., and may {should} reject the connection).
11071 // --------------------------------------------------------------------------
11072 new_ctx = SSL_set_SSL_CTX(tls_fd, new_ctx); // Returns new_ctx or, if new_ctx is nullptr then it returns the original CTX
11073 r->tls_ctx(new_ctx); // If nullptr, then tls_ctx() will get OpenSSL to create a new SSL_CTX
11075 } // -x- if len -x-
11078 // --------------------------------------------------------------------------
11079 // Free the ClientHello fields data, then return SSL_CLIENT_HELLO_SUCCESS.
11080 // Even if we encountered a problem with the ClientHello fields data, we
11081 // still return SSL_CLIENT_HELLO_SUCCESS so that the TLS context will be
11082 // accepted as valid.
11084 // If we return SSL_CLIENT_HELLO_ERROR the connection will fail unnecessarily
11085 // for valid TLS certificates. If the ClientHello fields data is malformed
11086 // to a degree that doesn't satisfy OpenSSL, then OpenSSL will reject it, so
11087 // there's really no point in duplicating what OpenSSL already does properly,
11088 // which OpenSSL will pass through the standard error channels with normal
11089 // error details-and-diagnostics anyway.
11091 // OpenSSL documentation says to do the following...
11092 // OPENSSL_free((char*)out);
11093 // ...but it's not necessary here because we'd be freeing a local variable,
11094 // which will fail and cause a crash.
11095 // --------------------------------------------------------------------------
11096 //std::cout << "out=" << randolf::rtools::to_hex(out[0], 8) << std::endl; // Debug
11097 //OPENSSL_free((unsigned char*)out);
11099 return SSL_CLIENT_HELLO_SUCCESS; // 1
11100 } // -x- int tls_sni_callback -x-
11103 /*======================================================================*//**
11105 Convert a 48-bit (6-byte) integer to a machine address in the form of @c
11106 xx:xx:xx:xx:xx:xx where every instance of @c xx is a hexadecimal
11107 representation of each respective 8-bit byte portion.
11109 This method is needed because we don't want to bring in the heavy fmt::format
11110 class as a dependency.
11111 @returns Mac address as 17-character in the typical format expected by system
11115 *///=========================================================================
11116 static std::unique_ptr<char[]> to_mac(
11117 /// Pointer to 48-bit integer
11118 const void* addr) noexcept {
11119 std::unique_ptr<char[]> h = std::make_unique_for_overwrite<char[]>(18); // 48-bit mac address needs 6 hexadecimal pairs of nybbles delimited by colons plus a NULL terminator
11122 "%02x:%02x:%02x:%02x:%02x:%02x",
11123 ((u_char*)addr)[0],
11124 ((u_char*)addr)[1],
11125 ((u_char*)addr)[2],
11126 ((u_char*)addr)[3],
11127 ((u_char*)addr)[4],
11128 ((u_char*)addr)[5]); // Convert, and truncate NULL terminator
11130 } // -x- std::unique_ptr<char[]> to_mac -x-
11132 /*======================================================================*//**
11134 Convert a 48-bit (6 byte) integer to a node address in the form of @c
11135 xxxx:xxxx:xxxx where every instance of @c xxxx is a hexadecimal
11136 representation of each respective 16-bit word portion.
11138 This method is needed because we don't want to bring in the heavy fmt::format
11139 class as a dependency.
11140 @returns Node address as 14-character in the typical format expected by
11141 network administrators
11144 *///=========================================================================
11145 static std::unique_ptr<char[]> to_node(
11146 /// Pointer to 48-bit integer
11147 const void* addr) noexcept {
11148 std::unique_ptr<char[]> h = std::make_unique_for_overwrite<char[]>(15); // 48-bit node address needs 3 hexadecimal sets of words delimited by colons plus a NULL terminator
11152 ((u_int16_t*)addr)[0],
11153 ((u_int16_t*)addr)[1],
11154 ((u_int16_t*)addr)[2]); // Convert, and truncate NULL terminator
11156 } // -x- std::unique_ptr<char[]> to_node -x-
11158 /*======================================================================*//**
11160 Send a formatted string to the @ref rsocket endpoint.
11162 The @c format is described in the documentation for the POSIX or Standard C
11163 Library @c printf() function.
11164 @throws randolf::rex::xEBADF The underlying socket is not open
11165 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
11166 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
11167 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
11168 @throws randolf::rex::xENOMEM Insufficient memory
11169 @returns The same rsocket object so as to facilitate stacking
11170 @see eol_fix_printf
11171 @see is_eol_fix_printf
11178 *///=========================================================================
11180 /// Format string to use
11181 const char* format,
11182 /// Variadic arguments in @c va_list format
11185 int rc = ::vasprintf(&buf, format, args);
11186 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
11187 if (__eol_fix_printf && !__eol.empty()) { // We need to edit the string and then send it
11188 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
11190 __send(str.data(), str.length());
11191 } else { // No need to edit the string, so just send it as is
11195 } catch (std::exception& e) { // Free buf then re-throw the exception
11196 ::free(buf); // Prevent memory leak when an exception is thrown
11199 } // -x- if __eol_fix_printf -x-
11201 } // -x- rsocket& vprintf -x-
11203 /*======================================================================*//**
11205 Send a formatted string to the @ref rsocket endpoint, and append an EoL
11208 The @c format is described in the documentation for the POSIX or Standard C
11209 Library @c printf() function.
11210 @throws randolf::rex::xEBADF The underlying socket is not open
11211 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
11212 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
11213 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
11214 @throws randolf::rex::xENOMEM Insufficient memory
11215 @returns The same rsocket object so as to facilitate stacking
11217 @see eol_fix_printf
11218 @see is_eol_fix_printf
11225 *///=========================================================================
11226 rsocket& vprintfline(
11227 /// Format string to use
11228 const char* format,
11229 /// Variadic arguments in @c va_list format
11232 int rc = ::vasprintf(&buf, format, args);
11233 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
11234 if (__eol_fix_printf && !__eol.empty()) {
11235 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
11238 __send(str.data(), str.length());
11241 __sendline(buf, rc);
11243 } catch (std::exception& e) { // Free buf then re-throw the exception
11244 ::free(buf); // Prevent memory leak when an exception is thrown
11247 } // -x- if __eol_fix_printf -x-
11249 } // -x- rsocket& vprintfline -x-
11252 /*======================================================================*//**
11253 Track unencrypted bytes received. When the number of bytes is negative or
11254 zero, it isn't recorded.
11255 This is an internal function.
11256 @returns same value provided in @ref n
11258 *///=========================================================================
11259 int __track_bytes_rx(
11260 /// Number of bytes transferred
11262 if (n > 0) __bytes_rx.fetch_add(n, std::memory_order_relaxed);
11264 } // -x- int __track_bytes_rx -x-
11266 /*======================================================================*//**
11267 Track unencrypted bytes transmitted. When the number of bytes is negative or
11268 zero, it isn't recorded.
11269 This is an internal function.
11270 @returns same value provided in @ref n
11272 *///=========================================================================
11273 int __track_bytes_tx(
11274 /// Number of bytes transferred
11276 if (n > 0) __bytes_tx.fetch_add(n, std::memory_order_relaxed);
11278 } // -x- int __track_bytes_tx -x-
11280 /*======================================================================*//**
11281 Track encrypted bytes received. When the number of bytes is negative or
11282 zero, it isn't recorded.
11283 This is an internal function.
11284 @returns same value provided in @ref n
11286 *///=========================================================================
11287 int __track_crypt_rx(
11288 /// Number of bytes transferred
11290 if (n > 0) __crypt_rx.fetch_add(n, std::memory_order_relaxed);
11292 } // -x- int __track_crypt_rx -x-
11294 /*======================================================================*//**
11295 Track encrypted bytes transmitted. When the number of bytes is negative or
11296 zero, it isn't recorded.
11297 This is an internal function.
11298 @returns same value provided in @ref n
11300 *///=========================================================================
11301 int __track_crypt_tx(
11302 /// Number of bytes transferred
11304 if (n > 0) __crypt_tx.fetch_add(n, std::memory_order_relaxed);
11306 } // -x- int __track_crypt_tx -x-
11308 /*======================================================================*//**
11309 Specialized private timeout-handling class that sets a new timeout on the
11310 specified socket, then relies on RAII to activate the destructor to restore
11311 the original timeout (which was acquired in the constructor).
11312 *///=========================================================================
11313 class RAII_timeout {
11316 // --------------------------------------------------------------------------
11317 // Prepare timeout structures so that we can override it (if timeout != 0)
11318 // and also restore it to what it used to be once we're finished. We need to
11319 // set up the "timeout_target" variable and few timeval structures, backup
11320 // the current non-recvline timeout settings, and set the recvline timeout.
11321 // --------------------------------------------------------------------------
11322 randolf::rsocket* r; // TODO: Add support for debug() method
11323 struct timeval old_tv{0, 0}; // {tv_sec, tv_usec};
11324 struct timeval new_tv{0, 0}; // {tv_sec, tv_usec};
11327 /*======================================================================*//**
11329 Change the specified rsocket's @c timeout, after first saving the current
11330 timeout settings for later restoration.
11332 If both @c sec or @c usec are set to 0, then this class will not make any
11333 changes to the underlying socket's timeout.
11334 *///=========================================================================
11336 /// The rsocket class to which to apply the timeout
11337 randolf::rsocket& r,
11338 /// Timeout in seconds
11340 /// Timeout in microseconds
11341 const int usec = 0) {
11342 if (sec == 0 && usec == 0) return; // Do nothing if timeout is 0
11344 if (r.__debug) r.debug("RAII_timeout(socket{0x" + randolf::rtools::to_hex(r.__socket_fd) + "}"
11345 + ", " + std::to_string(sec)
11346 + ", " + std::to_string(usec)
11348 socklen_t otv{sizeof(old_tv)};
11349 r.__rc_check(::getsockopt(r.__socket_fd, SOL_SOCKET, SO_RCVTIMEO, &old_tv, &otv)); // getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO);
11350 new_tv.tv_sec = sec;
11351 new_tv.tv_usec = usec;
11352 r.__rc_check(::setsockopt(r.__socket_fd, SOL_SOCKET, SO_RCVTIMEO, &new_tv, sizeof(new_tv))); // setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
11353//std::cout << "Set timeout to " << sec << std::endl; // Debug
11354 } // -x- consturctor __temporary_timeout -x-
11356 /*======================================================================*//**
11358 Restore the underlying socket's original @c timeout setting.
11359 *///=========================================================================
11360 ~RAII_timeout() noexcept {
11361 if (new_tv.tv_sec == 0 && new_tv.tv_usec == 0) return; // Do nothing if timeout is 0
11362 if (r->__debug) r->debug("~RAII_timeout(socket{0x" + randolf::rtools::to_hex(r->__socket_fd) + "}"
11363 + ", " + std::to_string(old_tv.tv_sec)
11364 + ", " + std::to_string(old_tv.tv_usec)
11366 // This shouldn't fail, but if it does it won't matter since it can only mean that the socket is screwed
11367 ::setsockopt(r->__socket_fd, SOL_SOCKET, SO_RCVTIMEO, &new_tv, sizeof(new_tv)); // setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
11368//std::cout << "Restore timeout" << std::endl; // Debug
11369 } // -x- destructor __temporary_timeout -x-
11371 }; // -x- class __RAII_timeout -x-
11373 }; // -x- class rsocket -x-
11375}; // -x- namespace randolf -x-
11377// Save this for a future sendlines() methods.
11378// const void* msg_ptr = msg.data(); // Prevent repeated calls to data() method
11379// const int len = msg.length(); // Prevent repeated calls to length() method