4#include <randolf/rline>
5#include <randolf/rsocket_io>
6#include <randolf/rsocket_sni>
7#include <randolf/rtools>
8#include <randolf/sockaddr_dl.h>
11#include <bit> // std::endian
12#include <cstdarg> // std::va_list
13#include <cstring> // std::strlen
14#include <ctime> // std::strftime
15#include <exception> // std::exception
16#include <filesystem> // std::filesystem::temp_directory_path
17#include <initializer_list>
18#include <iostream> // std::put_date
19#include <map> // std::map
20#include <memory> // std::shared_ptr
21#include <mutex> // std::mutex
24//#include <sstream> // std::ostringstream
25#include <unordered_map> // std::unordered_map
31#include <string.h> // strerror()
36#include <linux/fs.h> // Flags for ioctl()
38#include <net/if.h> // ifreq structure used by bind()
40#include <netinet/icmp6.h>
41#include <netinet/in.h>
43#include <netpacket/packet.h> // struct sockaddr_ll
45#include <openssl/err.h>
46#include <openssl/ossl_typ.h>
47#include <openssl/ssl.h>
50#include <sys/socket.h>
51#include <sys/stat.h> // fchmod()
52#include <sys/time.h> // TIMEVAL_TO_TIMESPEC macro
56static_assert((sizeof(__time_t) * CHAR_BIT) >= 64, "64-bit __time_t is required");
60 /*======================================================================*//**
62 This @ref rsocket class provides an easy-to-use and thoroughy-implemented
63 object-oriented socket I/O interface for C++, intended to make socket I/O
64 programming (with or without TLS encryption) easier and more enjoyable.
66 Here's a short list of benefits that are helpful in developing high quality
67 code that's consistent-and-reliable, and improves overall productivity:
69 - eliminating the need to repeatedly write blocks of code that check for
70 errors, by throwing exceptions instead (see @ref randolf::rex::rex class
71 for details and the long list of exceptions that are supported)
72 - eliminating the need to track socket descriptors
73 - eliminating the need to handle encrypted I/O separately (most functions)
74 - eliminating the need to manage memory for many common structures used to
75 interface with socket options, etc.
76 - eliminating the need to record socket I/O statistics with every call to
77 underlying socket I/O functions (see @ref randolf::rsocket_io for
79 - text-line reading/writing with an adapative approach (invented by
80 Randolf Richardson in the 1980s for a custom BBS software project) to
81 automatically detect EoL (End-of-Line) character sequences (unless the
82 developer provides a specific sequence via an @ref eol method) that can
83 determine whether an endpoint is sending Linux/UNIX (standard), MacOS,
84 DOS CR/LF, or even broken LF/CR (reverse of DOS) EoL sequences
85 - transparent support for encryption with many additional features,
86 including STARTTLS, ingress/egress policy enforcement, and SNI
87 - eliminating the complexity of handling events with poll(), select(), and
88 related functions (see the @ref randolf::rsocket_mux class for details)
89 - providing a variety of other useful features that make it easier to
90 communicate with socket endpoints, such as receiving/sending an entire
91 structure via a single call to the new-and-specialized @ref recv_struct
92 or @ref send_struct methods, respectively
94 An rsocket is either the endpoint that our underlying socket will connect to,
95 or it's the server daemon that our underying socket will listen() to and
96 accept() [inbound] connections from.
100 Using the C interface, the programming must check for errors by testing the
101 response codes (most of which are consistent, with a few subtle outliers),
102 which leads to a lot of additional error-checking code with the potential for
103 unintended errors (a.k.a., bugs). This style is necessary in C, but with C++
104 the way to handle errors is with exceptions, so I created this rsocket class
105 to handle all these tedious details behind-the-scenes and, for any socket
106 errors, to generate exceptions so that source code can be greatly simplified
107 (and, as a result, also easier to read and review).
109 Pre-allocating buffers is also handled internally, which is particularly
110 useful when making repeated calls to recv() and related methods. These
111 methods return std::shared_ptr<@c structure > (a C++ smart pointer that aids
112 in the prevention of resource leaks) or std::vector<char> (resized to the
113 actual number of bytes received) as appropriate which eliminates the need to
114 track @c size_t separately.
117 Lower-case letter "r" is regularly used in partial example code to represent
118 an instantiated rsocket object.
120 An ASCIIZ string is a C-string (char* array) that includes a terminating null
121 (0) character at the end.
123 The following custom qualifiers are incorporated into headings by Doxygen
124 alongside method titles throughout the documentation:
125 - @c POSIX denotes a method that is based on POSIX functions by the same
126 name and don't deviate significantly from the POSIX function arguments
127 (intended to be helpful to developers transitioning to/from rsocket or
128 working on source code that utilizes @ref rsocket and POSIX functions)
129 - @c TLS denotes that a method works properly with TLS-encrypted sockets
130 (most of the POSIX functions have been made to work properly with TLS,
131 but for the few rare cases of functions that can't be made to work with
132 TLS an effort has also been made to mention this using Doxygen's
133 "warning" sections in addition to omitting the TLS qualifier)
135 @par Getting started with a few simple examples
137 This is an example of connecting to an HTTP server, using the "GET" command
138 to request the home page (using HTTP/1.0), then receiving-and-displaying the
139 resulting web page's contents via STDOUT (or sending an error message to
140 STDERR). Finally, we exit with an EXIT_SUCCESS (or EXIT_FAILURE) code.
143 #include <iostream> // std::cout, std::cerr, std::endl, etc.
144 #include <randolf/rex>
145 #include <randolf/rsocket>
147 int main(int argc, char *argv[]) {
149 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
150 r.connect("www.example.com", 80);
151 r.sendline("GET / HTTP/1.0");
152 r.sendline("Host: www.example.com");
153 r.sendline("Connection: close");
155 while (r.is_open()) {
156 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
157 } // -x- while data -x-
159 } catch (const randolf::rex::xALL e) {
160 std::cerr << "Socket exception: " << e.what() << std::endl;
162 } catch (const std::exception e) {
163 std::cerr << "Other exception: " << e.what() << std::endl;
167 } // -x- int main -x-
170 Parameter stacking is supported (with methods that return @c rsocket*); in
171 this example, notice that semicolons (";") and "r." references are omittted
172 (when compared with the above):
175 #include <iostream> // std::cout, std::cerr, std::endl, etc.
176 #include <randolf/rex>
177 #include <randolf/rsocket>
179 int main(int argc, char *argv[]) {
181 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
182 r.connect("www.example.com", 80)
183 ->sendline("GET / HTTP/1.0")
184 ->sendline("Host: www.example.com")
185 ->sendline("Connection: close")
187 while (r.is_open()) {
188 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
189 } // -x- while data -x-
191 } catch (const randolf::rex::xALL e) {
192 std::cerr << "Socket exception: " << e.what() << std::endl;
194 } catch (const std::exception e) {
195 std::cerr << "Other exception: " << e.what() << std::endl;
199 } // -x- int main -x-
204 This is meant to be a comprehensive socket class for C++, which is intended
205 to make socket I/O coding easier for developers with some key features:
207 - easy conversion from C-style POSIX sockets due to API consistency
208 - transparent TLS support (OpenSSL dependency)
209 - keys and certificate chains can also be loaded from memory
210 - SNI support (see the @ref rsocket_sni class for more information)
211 - underlying socket handle is accessible (via the @ref socket_fd() method)
212 - socket options are easier to get and to set
213 - sensible support for future or unknown socket options
214 - errors are presented as ~100 separate exception classes: @ref rex::rex
215 - one parent exception class makes it easier to catch all socket errors
216 - a few exceptions groups are also provided to catch groups of errors
217 - new buffers are returned as std::shared_ptr's to eliminate memory leaks
218 - constructors with sensible defaults help to simplify coding
219 - documentation includes code samples (with @c \#include lines as needed)
220 - debug output with helpful output (and option to set output file handle)
221 - low-overhead is considered (this is why there's a bit more overloading)
222 - thread-safety is noted where it is absolutely available (if any caution
223 is warranted, it will also be noted)
224 - can send ASCIIZ (C-strings) without needing to specify string length
225 - can send @c std::string (which tracks its own string length)
226 - each socket can optionally have a @ref name arbitrarily assigned (e.g.,
227 by an algorithm, or even populated with the connecting user's name, or
228 whatever purpose the development goals find it useful for)
230 Additional features that are not part of the typical POSIX standard, but
231 deserve special mention because they are needed so often:
233 - easy access to internal I/O counters (see @ref rsocket_io for details)
234 - your rsocket_io structure can be updated automatically by rsocket's
235 destructor after underlying socket is closed (see @c net_io_final()
237 - printf(), printfline(), vprintf(), vprintfline() // Formatted strings
238 - with automatic EoL sequence substitution (and/or the addition of)
239 - recv_asciiz(), send_asciiz() // ASCIIZ string I/O operations
240 - recv_byte() , send_byte() // 8-bit byte operations (LSB/MSB is N/A)
241 - recv_struct(), send_struct() // Multi-byte operations
242 - recv_uint16(), send_uint16() // 16-bit operations with LSB/MSB variants
243 - recv_uint32(), send_uint32() // 32-bit operations with LSB/MSB variants
244 - recv_uint64(), send_uint64() // 64-bit operations with LSB/MSB variants
245 - recvline(), recv_rline(), sendline() // ASCII text line I/O operations
246 - class-wide configurable newline sequence (defaults to @e autodetect)
247 - @ref rsocket_group class for grouping rsockets (and automatic inclusion
248 by accept() and accept4() methods; this may be a different group from
249 whichever group the parent rsocket is in, if it's even in one)
250 - option to send data to all rsocket objects in the rsocket_group
251 - with support from the rsocket_mux class (for multiplexing operations)
252 - automatic naming policies (possibly like net_io() formatting style)
254 Some advanced features are planned that exceed what the basic socket I/O
255 functions provide, but are also needed:
257 - recv_uint128()/send_uint128() // 128-bit operations w/ LSB/MSB variants
258 - recv_uint(n)/send_uint(n) where "n" specifies the number of bits (which
259 must be a multiple of 8), with LSB/MSB variants
260 - auto-detection of inbound TLS connection (this is turned off by default)
261 - This is not the same as STARTTLS (an application-level command, for
262 which the @ref tls_do_handshake() method will likely be used)
263 - simple timing tracking options using timing_start() and timing_stop()
264 methods, the results of which can be retrieved with timing_get() or a
265 similarly-named group of methods
267 Other features that are not a high priority:
269 - internal support for portability to Microsoft Windows, which is a major
270 undertaking that I know will be time-consuming since Windows Sockets
271 exhibit some nuanced behaviours and are not consistent with POSIX
272 sockets APIs that are used by Linux, UNIX, MacOS, and pretty much all
273 other Operating Systems. Because of this, MS-Windows portability just
274 isn't a high priority for me (without sufficient demand and sufficient
275 funding so I can commit my time without missing mortgage payments,
276 student loan payments {for my kids}, various living expenses, etc.).
280 I use the term "ASCIIZ string" to indicate an array of characters that's
281 terminated by a 0 (a.k.a., null). Although this is very much the same as a
282 C-string, the difference is that in many API functions a C-string must often
283 be accompanied by its length value. When referring to an ASCIIZ string, I'm
284 intentionally indicating that the length of the string is not needed because
285 the string is null-terminated. (This term was also commonly used in assembly
286 language programming in the 1970s, 1980s, and 1990s, and as far as I know is
287 still used by machine language programmers today.)
289 UTF-8 works without any problems as it is backward-compatible to 8-bit ASCII,
290 and because @c std::string uses 8-bit bytes to store strings internally. Do
291 keep in mind that the manipulation of UTF-8 substrings will require working
292 with UTF-8 codepoints that may consume one or more bytes, which is beyond the
293 scope of the impartial (to UTF-8 and ASCII) functionality that this rsocket
296 UTF-8 newline codepoints NEL (c2 85), LS (e2 80 a8), and PS (e2 80 a9) garner
297 no special handling at this time - unlike CR/LF (0d/0a) - and are merely
298 treated as non-newline codepoints. There is a possibility of adding support
299 for this in the future, but additional research and planning is required to
300 make sure this works properly. What is most likely is that some UTF-8 flags
301 will be added to support each of these (which will probably be disabled by
302 default) that will be integrated into the readline() methods. This also
303 depends on how widely used these particular codepoints are, and pending
304 further research to determine whether these really are supposed to be used
305 functionally as newlines...
307 So far, there are two UTF-8 codepoints that absolutely are not functional,
308 yet which a small number of people have mistakenly assumed are:
309 - <sup>C</sup><sub>R</sub> = superscript "C" with subscript "R" (e2 90 9d)
310 - <sup>N</sup><sub>L</sub> = superscript "N" with subscript "L" (e2 90 a4)
312 The special characters above are intended to represent the Carriage-Return
313 and New-Line respectively in documentation such as ASCII character reference
314 charts, which were used mostly by IBM in the 1970s and 1980s for instruction
315 manuals (and in other documentation), and also on a few keyboard overlays.
319 I created this class to make it easier to write internet server daemons. I
320 started out using C-style socket functions (because C++ doesn't come with a
321 socket class), but I found that I didn't enjoy mixing something as important
322 and detailed as socket I/O in a procedural way into the object-oriented
323 paradigm that C++ provides.
325 After looking for existing solutions (none of which served as comprehensive
326 replacements for socket I/O), I embarked on creating the rsocket class, and
327 then I began to understand why this probably hadn't been done -- it's a
328 massive undertaking, primarily because there are a lot of functions that are
329 needed to handle socket I/O. Further, [at the time of this writing] the @c
330 sockaddr_storage structure wasn't as widely used as it should be, and so
331 information about it tended to be scarce, incomplete, or incorrect (further
332 research, and diving down into some pretty deep "rabbit holes," was required
333 to understand this properly, which was worthwhile because it resulted in
334 having transparent support for IPv4 and IPv6 without breaking backward
335 compatibility for code expecting specific structures).
337 Moving error codes into exceptions is also a major effort because they are
338 diverse and plentiful, and there are so many errors that can occur at various
339 stages for many different reasons. There are also a few outlier functions
340 that require slightly different approaches to error handling due to subtly
341 different rules for handling their errors, and so the exception-generation
342 wasn't as straight-forward as one might optimistically expect, but this is
343 one of the many benefits of the object-oriented programming pardigm because
344 handling edge cases internally results in a consistent error-handling
345 interface using exceptions that also simplifies the source code. (Need to
346 handle a specific set of conditions? Catch the relevant exceptions for those
347 cases in an inner set of exceptions, and just catch all the others in a more
348 general way without the added complexity of repeatedly checking for errors
349 every step along the way.)
351 So, I dedicated time to make this work, and with the intention of making it
352 an open source project once I got it into a state that's ready for the
353 general public. This required putting my other C++ projects on hold, which
354 was fine because they didn't have strict deadlines and using this socket
355 class in them will speed up development in the long-term anyway, so it's
356 clearly worth the effort to me ... and I sincerely hope that my efforts will
357 be helpful to others too.
359 My background in programming began when I was a young child, teaching myself
360 BASIC and then machine language (when I found BASIC to be too limited) before
361 moving on to other languages like Perl and Java many years later. Eventually
362 I circled around to C (which I chose to learn the hard way by writing some
363 PostgreSQL extensions) and then C++ a few years after that. I have a lot of
364 experience with socket communications, including fully-featured DNS resolver
365 library code in machine language for Novell's NetWare that used C calling
366 conventions and supported varargs, which worked well for the few developers
367 who needed or wanted it.
370 - 2022-Nov-09 v1.00 Initial version
371 - 2022-Nov-26 v1.00 Moved exception handling to a separate class
372 - 2022-Nov-28 v1.00 Completed readline/send functionality
373 - 2022-Dec-03 v1.00 Added endianness transparency
374 - 2022-Dec-04 v1.00 Added printf() support
375 - 2022-Dec-24 v1.00 Added socket MUXing
376 - 2023-Feb-22 v1.00 Added TLS/SSL support
377 - 2023-Mar-10 v1.00 Added TLS-SNI support
378 - 2023-Apr-19 v1.00 Added TLS certificate loading from memory
379 - 2023-May-24 v1.00 Added support for clang++ compilation
380 - 2023-Jun-09 v1.00 Improvements to dynamic internal read buffering
381 - 2023-Oct-31 v1.00 Improvements to various classes
382 - 2024-Feb-21 v1.00 Added is_buffered() method
383 - 2024-Mar-31 v1.00 Completed @ref recvline (implemented a work-around for
384 a subtle SSL_peek failure to extract additional data
385 when a user at an end-point is communicating with
386 "icanon" mode enabled)
387 - 2024-May-24 v1.00 Changed source code to accomodate @c clang++ compiler
388 - 2024-Jun-04 v1.00 Added @c POSIX and @c TLS qualifiers to all applicable
389 methods (Doxygen incorporates into the documentation)
390 - 2024-Oct-23 v1.00 Various minor improvements to the documentation since
392 - 2024-Nov-05 v1.00 Added eol_consumed_seq() method
393 - 2024-Nov-10 v1.00 Added discard() method
394 - 2024-Nov-17 v1.00 Added recv_rline() method
395 - 2024-Nov-19 v1.00 Added recv_as_string() method
397 @author Randolf Richardson
398 *///=========================================================================
401 // --------------------------------------------------------------------------
402 // The rsocket_group class needs access to some of our internal variables.
403 // --------------------------------------------------------------------------
404 friend class rsocket_group;
406 // --------------------------------------------------------------------------
408 // --------------------------------------------------------------------------
410 int __socket_type = 0;
411 int __socket_protocol = 0;
412 struct sockaddr_storage __socket_addr = {}; // Initialize to all elements to their default values
413 socklen_t __socket_addr_size = sizeof(__socket_addr); // We need to point to this (and it might be modified)
414 int __socket_backlog = SOMAXCONN; // Default backlog is 4096 on Linux, and 128 on older systems
416 // --------------------------------------------------------------------------
417 // Socket flags (internal, but with get-methods to provide read-only access).
418 // --------------------------------------------------------------------------
419 std::atomic_bool __socket_open = false; // Socket is open by way of socket() function
420 std::atomic_bool __socket_connected = false; // Socket is connected
422 // --------------------------------------------------------------------------
423 // TLS flags and variables.
424 // --------------------------------------------------------------------------
425 std::atomic_bool __tls = false; // Indicates whether socket I/O is encrypted with OpenSSL
426 SSL_CTX* __tls_ctx = nullptr; // TLS context (nullptr == no encryption initialized)
427 rsocket_sni* __tls_sni = nullptr; // SNI maps
428 bool __tls_sni_match = false; // Set to TRUE only if SNI matched in the SNI callback
429 SSL* __tls_fd = nullptr; // TLS connection structure (nullptr == no connection)
430 BIO* __tls_rbio = nullptr; // TLS BIO input stream
431 BIO* __tls_wbio = nullptr; // TLS BIO output stream
432 bool __tls_exclusive = false; // See enum TLS_FLAGS::TLS_EXCLUSIVE
433 bool __tls_egress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_EGRESS
434 bool __tls_ingress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_INGRESS
435 bool __tls_server_mode = true; // See enum TLS_FLAGS::TLS_SERVER
436 inline static std::atomic_int __fn_counter = 0; // Used in generating unique-and-threadsafe temporary filenames
437 rsocket* __tls_new_endpoint = nullptr; // Weak pointer to newly-spawned endpoint's rsocket object
439 // --------------------------------------------------------------------------
440 // Keep track of test for whether this host stores integers using big endian.
441 // --------------------------------------------------------------------------
442 inline static const bool __endian_is_msb = htons(42) == 42; // TRUE==MSB / FALSE==LSB
444 // --------------------------------------------------------------------------
445 // General variables. Atomic variables are used for variables as needed to
446 // support operations in a thread-safe manner.
447 // --------------------------------------------------------------------------
448 std::atomic_bool __debug = false;
449 std::atomic<std::FILE*> __debug_fd = stderr;
450 std::string __debug_prefix = "rsocket-debug";
451 #define RSOCKET_BUFFER_SIZE 1024 // Default buffer size
452 size_t __buffer_size = RSOCKET_BUFFER_SIZE; // Buffer size used by recv() when a buffer size isn't provided
453 char* __buffer = nullptr; // Initialized automatically when recvline() is used
454 char* __buffer_head = nullptr; // Newest data in buffer (a pointer is faster than calculating offsets)
455 char* __buffer_tail = nullptr; // Oldest data in buffer (a pointer is faster than calculating offsets)
456 char* __buffer_boundary = nullptr; // Size-wrapped boundary (a pointer is faster than calculating lengths)
457 std::string __name; // Arbitrary name of this rsocket (used by some applications)
458 std::string __name_sni; // SNI hostname (when tls_sni is configured)
460 // --------------------------------------------------------------------------
461 // EoL sequence variables.
462 // --------------------------------------------------------------------------
463 static const char __CR = (char)13; // CTRL-M / ASCII 13 (0Dh) / "\r" / Used by eol() methods
464 static const char __LF = (char)10; // CTRL-J / ASCII 10 (0Ah) / "\n" / Used by eol() methods
465 static constexpr const char* __CRLF = "\x0d\x0a"; // Used by eol() methods
466 std::string __eol; // Used by recvline() method; maintained by eol() methods
467 std::string __eol_consumed_seq; // The EoL sequence that was successfully received by the most recent use of the recvline() method
468 bool __eol_adoption = true; // Whether to adopt the dynamically detected EoL sequence when __eol is empty (the default)
469 std::string __eol_out = std::string(__CRLF); // Used by sendline() method; maintained by eol() methods; also used by sendline() method
470 bool __eol_fix_printf = true; // Whether to replace "\n" sequence in printf() methods to EoL sequence
472 // --------------------------------------------------------------------------
473 // Fine-tuning for recvline(). This is used with nanosleep().
474 // --------------------------------------------------------------------------
475 struct timespec __recvline_idle = {0, 999999999/3}; // Used by recvline() method; time to sleep between keystrokes (default to 1/8 of a second) to mitigate tight CPU high utilization loops
476 long __recvline_timeout = 0; // Number of seconds (0 = unlimited; maximum for "long" equates to approximately 292 billion years since the UNIX/Linux epoch)
478 // --------------------------------------------------------------------------
479 // Statistical variables.
480 // --------------------------------------------------------------------------
481 rsocket_io* __io_final_addr = nullptr; // Used by ~rsocket() destructor, net_io_final(), and net_io_update()
482 std::atomic_ulong __bytes_rx = 0; // Total number of bytes received
483 std::atomic_ulong __bytes_tx = 0; // Total number of bytes transmitted
484 std::atomic_ulong __crypt_rx = 0; // Total number of encrypted bytes recevied
485 std::atomic_ulong __crypt_tx = 0; // Total number of encrypted bytes transmitted
488 /*======================================================================*//**
490 Optional flags used with various methods to determine whether they will throw
491 an @ref randolf::rex::xETIMEDOUT exception or merely return a @c nullptr, an
492 empty set, or a 0 (zero) when a timeout duration elapses.
494 If you're not sure of whether to use @ref TIMEOUT_EMPTY, @ref TIMEOUT_NULL,
495 or @ref TIMEOUT_ZERO in your code, then I suggest using @ref TIMEOUT_NULL
496 since @c NULL will likely be the most recognizable in most code reviews.
499 You'll know when this is an option because the method will support this.
500 *///=========================================================================
501 enum TIMEOUT_BEHAVIOUR: bool {
503 /*----------------------------------------------------------------------*//**
504 Indicate that an @ref randolf::rex::xETIMEDOUT exception should be thrown
505 when the timeout duration elapses.
506 *///-------------------------------------------------------------------------
507 TIMEOUT_EXCEPTION = true,
509 /*----------------------------------------------------------------------*//**
510 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
511 when the timeout duration elapses.
512 *///-------------------------------------------------------------------------
513 TIMEOUT_EMPTY = false,
515 /*----------------------------------------------------------------------*//**
516 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
517 when the timeout duration elapses.
518 *///-------------------------------------------------------------------------
519 TIMEOUT_NULL = false,
521 /*----------------------------------------------------------------------*//**
522 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
523 when the timeout duration elapses.
524 *///-------------------------------------------------------------------------
525 TIMEOUT_ZERO = false,
527 }; // -x- enum TIMEOUT_BEHAVIOUR -x-
529 /*======================================================================*//**
531 Optional flags used with rsocket's @ref tls() and @ref tls_ctx() methods to
532 specify relevant policies/semantics.
533 *///=========================================================================
534 enum TLS_FLAGS: int {
536 /*----------------------------------------------------------------------*//**
537 The TLS_DEFAULT flag isn't necessary, but it's included here for completeness
538 as it accomodates programming styles that prefer to emphasize when defaults
539 are being relied upon.
540 *///-------------------------------------------------------------------------
543 /*----------------------------------------------------------------------*//**
544 Only encrypted connections are permitted, initially, and all attempts to
545 begin with unencrypted connections will consistently fail.
547 Encrypted connections must begin with a cryptographic handshake packet, or
548 else the connection will be rejected as due to being reasonably assumed to be
551 If this flag isn't set, an application-level mechanism can be used to upgrade
552 to TLS encryption (this is common with connections that begin unencrypted,
553 and issue a proprietary command {such as the @c STARTTLS command in SMTP} to
554 upgrade to TLS later on {of which the @ref tls() method is used to affect an
555 ingress; see the @ref TLS_NO_INGRESS flag for additional information}).
557 Creating an exclusively @c unencrypted connection is accomplished by not
560 @see TLS_NO_EGRESS to prevent support for application-level downgrades to
561 unencrypted connections
562 *///-------------------------------------------------------------------------
565 /*----------------------------------------------------------------------*//**
566 Mid-stream upgrades to encrypted connections are not permitted (e.g., via
567 application-level initiations like the @c STARTTLS command as seen in SMTP),
568 which will also cause calls to the @ref tls() method to fail fast after plain
569 non-encrypted data has already been sent or received.
571 This flag is not necessary when the @ref TLS_EXCLUSIVE flag is set.
572 @see is_tls_ingress_okay()
573 @see tls_do_handshake()
575 *///-------------------------------------------------------------------------
578 /*----------------------------------------------------------------------*//**
579 Mid-stream downgrades to unencrypted connections are not permitted (e.g., via
580 application-level initiations like a hypothetical @c STOPTLS command as seen
581 in FTPS), which will also cause calls to the @ref tls() method to fail fast
582 after encrypted data has already been sent or received.
584 This flag may be combined with the @ref TLS_EXCLUSIVE flag to prevent a
585 connection from being downgraded programatically.
587 Although egress to an unencrypted connection doesn't occur automatically
588 (since egress can only be affected programatically to support commands at the
589 application level), this flag is useful to prevent third-party code from
590 downgrading an encrypted @ref rsocket to unencrypted.
592 Supporting unencrypted communications is strongly discouraged over public
593 networks (e.g., the internet) because unencrypted streams are trivially
594 susceptible to man-in-the-middle attacks that can alter the contents of the
595 data in both directions (which is a particularly dangerous prospect for
596 sending/receiving sensitive information).
597 @see is_tls_egress_okay()
599 *///-------------------------------------------------------------------------
602 /*----------------------------------------------------------------------*//**
603 This is a convenience flag that provides an option for developers to be more
604 clear in their use of the @ref tls() and @ref tls_ctx() methods to indicate
605 intent to rely on what is already the default.
606 @see tls_do_handshake()
608 *///-------------------------------------------------------------------------
611 /*----------------------------------------------------------------------*//**
612 Indicates that this rsocket will be for a server daemon, and to initialize a
613 new TLS context (when one isn't being provided) using OpenSSL's
614 @c TLS_server_method() function instead of OpenSSL's @c TLS_client_method()
615 function (the latter is the default because most code is anticipated to be
619 Setting the CLIENT/SERVER flag incorrectly will typically cause the following
620 error that's difficult to track down, which is usually triggered by calling
621 @ref accept, @ref accept4(), or @ref connect(), and so I hope that including
622 this information here in this documentation will be helpful:
624 error:140C5042:SSL routines:ssl_undefined_function:called a function you should not call
627 The absence of this flag has the same effect as specifying the @ref
630 *///-------------------------------------------------------------------------
633 }; // -x- enum TLS_FLAGS -x-
636 /*======================================================================*//**
637 Return Code check, and throws an rsocket-specific exception if an error
638 occurred, which is indicated by @c -1 (a lot of example code shows @c < @c 1
639 comparisons, but we specifically test for @c -1 because the documentation
640 clearly states @c -1. This is important because a system that can support an
641 extremely high number of socket handles might, in theory, assign handles with
642 values that get interpreted as negative integers, and so less-than-zero tests
643 would result in dropped packets or dropped sockets (any such socket code that
644 allocates such handles obviously must not ever allocate @c -1 since this
645 would definitely be misinterpreted as an error).
647 If rc is not @c -1, then it is simply returned as is.
649 If n is nonzero and rc is 0, then n will override errno. (This option is
650 provided to accomodate the few socket library functions that return values
651 that are never errors, and expect the developer to rely on other means of
652 detecting whether an error occurred. This is an example of where Object
653 Oriented Programming is helpful in making things better.)
654 @returns Original value of @c rc (if no exceptions were thrown)
655 *///=========================================================================
656 const int __rc_check(
659 /// Override @c errno (if not 0)
661 /// Exception handling flags (see @ref rex::REX_FLAGS for details)
662 const int flags = rex::rex::REX_FLAGS::REX_DEFAULT) {
663 if (rc == -1 || (rc == 0 && n != 0)) {
664 const int socket_errno = n == 0 ? errno : n; // If "n" is not zero, use it instead
665 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
666 randolf::rex::mk_exception("", socket_errno, flags); // This function doesn't return (this code ends here)
669 }; // -x- int __rc_check -x-
671 /*======================================================================*//**
672 Similar to @ref __rc_check(), but specialized for handling OpenSSL return
674 *///=========================================================================
675 const int __rc_check_tls(
676 /// Return code (from OpenSSL's API functions)
679 if (__debug) debug("__rc_check_tls(" + std::to_string(rc) + ") throwing exception"); // TODO: Remove this
680 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)
683 }; // -x- int __rc_check -x-
685 /*======================================================================*//**
686 Internal function that opens the socket. This is used by the constructors
687 and their accompanying socket() methods.
689 Throws randolf::rex::xEALREADY exception if the socket is already open to
690 prevent multiple calls to @c open, which can be particularly problematic if
691 any of the additional function calls specify different parameters -- this
692 helps developer(s) to avoid unexpected results if they're inadvertently using
693 the same rsocket object when they intend to use different rsocket objects.
694 *///=========================================================================
696 /// Communication domain; usually one of:@n
698 /// AF_INET6 (IPv6)@n
699 /// AF_UNIX (UNIX domain sockets)
701 /// Communication semantics; usually one of:@n
702 /// SOCK_STREAM (common for TCP)@n
703 /// SOCK_DGRAM (common for UDP)
705 /// Network protocol; usually one of:@n
709 const int protocol) {
710 if (__debug) debug("socket(" + std::to_string(family)
711 + ", " + std::to_string(type)
712 + ", " + std::to_string(protocol)
715 // --------------------------------------------------------------------------
717 // --------------------------------------------------------------------------
718 if (__socket_open) throw randolf::rex::xEALREADY("EALREADY: Socket is already open"); // Socket has already been opened
719 if (family == AF_UNSPEC) throw randolf::rex::xEAFNOSUPPORT("EAFNOSUPPORT: AF_UNSPEC family (SO_DOMAIN) is not supported by socket()");
721 // --------------------------------------------------------------------------
722 // Build minimum parts of __socket_addr structure.
723 // --------------------------------------------------------------------------
724 __socket_addr.ss_family = family;
725 __socket_type = type;
726 __socket_protocol = protocol;
728 // --------------------------------------------------------------------------
729 // Create new socket handle, and save it, then set internal variable to
730 // indicate that the socket is open.
731 // --------------------------------------------------------------------------
732 __socket_fd = __rc_check(::socket(__socket_addr.ss_family, __socket_type, __socket_protocol));
733 __socket_open = true;
735 }; // -x- void __socket -x-
738 /*======================================================================*//**
740 Instantiate an empty rsocket without actually opening a socket, and therefore
741 also without throwing any exceptions (useful in header-file definitions).
744 Instantiating an empty rsocket is particularly useful for header-file
745 definitions since exceptions can't be handled outside of subroutines, and
746 it's also useful for enabling debug() mode @em before setting the socket's
747 configuration with one of the socket() methods; for example:
749 randolf::rsocket r; // Empty rsocket (empty / incomplete initialization)
750 r.debug(true); // Enable debug mode
751 r.socket(...); // Required to complete rsocket initialization
756 The built-in defaults, when not provided, are as follows ("family" is also
757 known as the "communication domain"):
758 - @c family = AF_INET
759 - @c type = SOCK_STREAM
760 - @c protocol = PF_UNSPEC
762 You will need to use one of the socket(...) methods to specify socket details
763 after defining rsocket objects with empty constructors so that you can catch
764 runtime exceptions. (This also provides you with an option to enable debug
765 mode during runtime prior to attempting to open an rsocket.)
770 #include <iostream> // std::cout, std::cerr, std::endl, etc.
771 #include <randolf/rex>
772 #include <randolf/rsocket>
774 randolf::rsocket r; // <-- Empty rsocket initialization (no exceptions)
776 int main(int argc, char *argv[]) {
778 r.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Required to complete rsocket initialization
779 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
780 // ... other socket I/O operations
782 } catch (const randolf::rex::xALL e) {
783 std::cerr << "Socket exception: " << e.what() << std::endl;
785 } catch (const std::exception e) {
786 std::cerr << "Other exception: " << e.what() << std::endl;
790 } // -x- int main -x-
796 @see socket_protocol()
799 *///=========================================================================
800 rsocket() noexcept {}; // -x- constructor rsocket -x-
802 /*======================================================================*//**
804 Instantiate an rsocket based on a minimal subset of the settings in the
805 specified rsocket (using it as a template), without actually opening a
806 socket, and therefore also without throwing any exceptions.
809 This constructor does not suffice as a full clone()-like operation, and is
810 minimal because it's used internally by the @ref accept() and @ref accept4()
813 Details that are absorbed from the template/source rsocket (which eliminates
814 the need to assign, set, and configure various parameters (TLS and TLS SNI
815 parameters will be copied in a passive way by default):
816 - Socket family (SO_DOMAIN)
817 - Socket type (SO_TYPE)
818 - Socket protocol (SO_PROTOCOL)
819 - TLS details (status, context {which includes loaded certificates},
820 policies specified with @ref TLS_FLAGS, etc.)
822 - TLS context (you'll still need to call `tls_ctx(r->tls_ctx())`)
823 - TLS SNI map (you'll still need to call `tls_sni(r->tls_sni())`)
826 The TLS Context will not be initialized because it needs a real socket to
827 draw from. If using TLS, you'll need to use the @ref tls() method after
828 the underlying socket has been initiated.
830 When @c flag_create_socket is set to FALSE, no exceptions will be thrown.
836 @see socket_protocol()
839 *///=========================================================================
841 /// Source rsocket object to use as a template to absorb settings from
842 const rsocket* rtemplate,
843 /// TRUE = create a new socket handle (default)@n
844 /// FALSE = don't create a new socket because a new one will be assigned or
845 /// created later (all variants of the @ref accept() methods do this)
846 const bool flag_create_socket = true) {
848 // --------------------------------------------------------------------------
849 // General socket variables.
850 // --------------------------------------------------------------------------
851 if (flag_create_socket) {
852 __socket(rtemplate->__socket_addr.ss_family,
853 rtemplate->__socket_type,
854 rtemplate->__socket_protocol);
855 } else { // !flag_create_socket
856 __socket_addr.ss_family = rtemplate->__socket_addr.ss_family;
857 __socket_type = rtemplate->__socket_type;
858 __socket_protocol = rtemplate->__socket_protocol;
859 } // -x- if flag_create_socket -x-
860 __name = rtemplate->__name;
862 // --------------------------------------------------------------------------
863 // TLS and SNI settings, but not whether TLS is enabled.
864 // --------------------------------------------------------------------------
865 __tls_ctx = rtemplate->__tls_ctx;
866 __tls_sni = rtemplate->__tls_sni;
868 // --------------------------------------------------------------------------
870 // --------------------------------------------------------------------------
871 __eol.assign( rtemplate->__eol); // Copy source std::string's contents (not a reference)
872 __eol_fix_printf = rtemplate->__eol_fix_printf;
874 }; // -x- constructor rsocket -x-
876 /*======================================================================*//**
878 Instantiate an rsocket with IP/host address and [optional] port number.
880 This is either the endpoint that our underlying socket will be connecting to,
881 or it's the local address of the server daemon that our socket will listen()
882 to and accept() inbound connections from.
886 The built-in defaults, when not provided, are as follows ("family" is also
887 known as the "communication domain"):
888 - @c family = AF_INET
889 - @c type = SOCK_STREAM
890 - @c protocol = PF_UNSPEC
892 The socket() methods do the same work as the constructors with matching
893 arguments, and are provided as convenience methods intended to augment
894 empty rsocket constructors used in header files, but do require an address to
895 be specified (for protocols that need port numbers, such as TCP or UDP, a
896 "port" number also needs to be specified since the default port 0 will result
897 in the dynamic allocation of a port number by the system).
899 For UNIX domain sockets use family AF_UNIX, type SOCK_STREAM, and protocol
900 IPPROTO_IP when instantiating or opening an rsocket.
905 #include <iostream> // std::cout, std::cerr, std::endl, etc.
906 #include <randolf/rex>
907 #include <randolf/rsocket>
909 int main(int argc, char *argv[]) {
911 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Same as socket() method
912 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
913 r.bind("127.0.0.1", 32768);
914 // ... other socket I/O operations
916 } catch (const randolf::rex::xALL e) {
917 std::cerr << "Socket exception: " << e.what() << std::endl;
919 } catch (const std::exception e) {
920 std::cerr << "Other exception: " << e.what() << std::endl;
924 } // -x- int main -x-
927 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
928 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
929 @throws randolf::rex::xEINVAL Protocal family invalid or not available
930 @throws randolf::rex::xEINVAL Invalid flags in type
931 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
932 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
933 @throws randolf::rex::xENOBUFS Insufficient memory
934 @throws randolf::rex::xENOMEM Insufficient memory
935 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
936 supported within the specified family (a.k.a., communication domain)
940 *///=========================================================================
942 /// Communication domain; usually one of:@n
944 /// AF_INET6 (IPv6)@n
945 /// AF_UNIX (UNIX domain sockets)
947 /// Communication semantics; usually one of:@n
948 /// SOCK_STREAM (common for TCP)@n
949 /// SOCK_DGRAM (common for UDP)
950 const int type = SOCK_STREAM,
951 /// Network protocol; usually one of:@n
955 /// PF_UNSPEC (auto-detect)
956 const int protocol = PF_UNSPEC) {
957 __socket(family, type, protocol);
958 }; // -x- constructor rsocket -x-
960 /*======================================================================*//**
962 Destructor, which closes any underlying sockets, frees any TLS structures
963 that were allocated by OpenSSL, and performs any other necessary clean-up
964 before finally copying the I/O statistics to a designated structure (if one
965 was specified with the @ref net_io_final() method).
967 Developers should take care to check that the @ref rsocket_io::is_final flag
968 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
969 since there's no guarantee that the destructor will necessarily be executed
970 in a timely manner (this flag is set last, after all other statistics are
971 copied into the structure while in a mutex-locked state).
974 *///=========================================================================
975 ~rsocket() noexcept {
977 // --------------------------------------------------------------------------
979 // --------------------------------------------------------------------------
981 debug("destructor(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
983 debug(false); // Disable debug mode (might trigger extra clean-up in future)
984 } // -x- if _debug -x-
986 // --------------------------------------------------------------------------
987 // Free memory and resources and close handles that need to be freed/closed.
988 // --------------------------------------------------------------------------
989 if (__tls_fd != nullptr) SSL_free(__tls_fd);
990// if (__tls_sni != nullptr) tls_sni(nullptr); // Remove any SNI callbacks // WE DON'T NEED THIS
992// if (__tls_ctx != nullptr) SSL_CTX_free(__tls_ctx); // TODO: Research using SSL_up_ref(__tls_ctx)
993 if (__socket_fd != 0) ::close(__socket_fd);
994 if (__buffer != nullptr) ::free(__buffer);
995// if (__buffer != nullptr) delete []__buffer;
997 // --------------------------------------------------------------------------
998 // Copy statistics to final location. (We do this last in case there's an
999 // issue with locking; there shouldn't be, but if there is then at least we
1000 // already we're not inadvertently hanging on to resources at this point that
1001 // need to be freed/closed anyway.)
1002 // --------------------------------------------------------------------------
1003 if (__io_final_addr != nullptr) {
1004 __io_final_addr->lock();
1005 __io_final_addr->bytes_rx = __bytes_rx;
1006 __io_final_addr->bytes_tx = __bytes_tx;
1007 // ->bytes_xx = <bytes in buffer; these are lost bytes that should be subtracted from __bytes_rx>
1008 __io_final_addr->crypt_rx = __crypt_rx;
1009 __io_final_addr->crypt_tx = __crypt_tx;
1010 // ->crypt_xx = <bytes in buffer; these are lost bytes that should be subtracted from __crypt_rx>
1011 __io_final_addr->is_final = true;
1012 __io_final_addr->unlock();
1013 } // -x- if __io_final_addr -x-
1015 }; // -x- destructor ~rsocket -x-
1017 /*======================================================================*//**
1019 Accept new [inbound] socket connetions. (This is typically used in a loop.)
1022 The resulting rsocket object is created before the actual call to the @c
1026 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1027 select(), and accept()/accept4() when using multiple sockets.
1030 To prevent resource leaks, the resulting rsocket needs to be deleted after
1031 it's no longer needed.
1033 @throws randolf::rex::xEBADF The underlying socket is not open
1034 @throws randolf::rex::xECONNABORTED The connection was aborted
1035 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1036 part of the user address space
1037 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1038 @throws randolf::rex::xEHOSTUNREACH No route to host
1039 @throws randolf::rex::xEINTR Interrupted by a signal
1040 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1041 length of the address is invalid
1042 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1043 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1044 @throws randolf::rex::xENETUNREACH No route to network
1045 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1046 @throws randolf::rex::xENOBUFS Insufficient memory
1047 @throws randolf::rex::xENOMEM Insufficient memory
1048 @throws randolf::rex::xENONET Requested host is not reachable on the network
1049 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1051 @throws randolf::rex::xENOSR System ran out of stream resources
1052 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1053 doesn't refer to a socket
1054 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1055 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1056 @throws randolf::rex::xEPROTO Protocol error
1057 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1058 supported within the specified family (a.k.a., communication domain)
1059 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1060 a system call is restartable and can be intercepted-and-redirected
1061 (there is no need to catch this exception unless you are an advanced
1062 developer, in which case you likely still won't need to catch it in
1064 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1065 supported within the specified family (a.k.a., communication domain)
1066 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1067 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1068 no inbound connections are waiting
1070 @returns Newly-created rsocket object representing the connection received
1077 *///=========================================================================
1079 if (__debug) debug("accept(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1082 // --------------------------------------------------------------------------
1083 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1084 // new incoming connection.
1085 // --------------------------------------------------------------------------
1086 __tls_new_endpoint = new rsocket(this, false);
1088 // --------------------------------------------------------------------------
1089 // Wait for incoming connection, and update internal flags in client rsocket.
1090 // --------------------------------------------------------------------------
1091 __tls_new_endpoint->__socket_fd = ::accept(__socket_fd, (sockaddr*)&__tls_new_endpoint->__socket_addr, &__tls_new_endpoint->__socket_addr_size);
1092 if (__tls_new_endpoint->__socket_fd == -1) {
1093 delete __tls_new_endpoint; // Memory management
1094 //__tls_new_endpoint = nullptr;
1095 __rc_check(-1); // This part throws the exception
1097 __tls_new_endpoint->__socket_open = true;
1098 __tls_new_endpoint->__socket_connected = true;
1100 // --------------------------------------------------------------------------
1101 // Perform TLS accept() as well, but only if TLS is enabled.
1103 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1104 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1105 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1106 // --------------------------------------------------------------------------
1108 __tls_new_endpoint->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1109 __tls_new_endpoint->tls(true); // Make sure __tls_fd is configured correctly
1110 __rc_check_tls(SSL_accept(__tls_new_endpoint->__tls_fd));
1111 } // -x- if __tls -x-
1113 return __tls_new_endpoint;
1114 }; // -x- rsocket* accept -x-
1116 /*======================================================================*//**
1118 Accept new [inbound] socket connetions. (This is typically used in a loop.)
1121 The resulting rsocket object is created before the actual call to the @c
1125 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1126 select(), and accept()/accept4() when using multiple sockets.
1129 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1130 aids in the prevention of resource leaks).
1132 @throws randolf::rex::xEBADF The underlying socket is not open
1133 @throws randolf::rex::xECONNABORTED The connection was aborted
1134 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1135 part of the user address space
1136 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1137 @throws randolf::rex::xEHOSTUNREACH No route to host
1138 @throws randolf::rex::xEINTR Interrupted by a signal
1139 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1140 length of the address is invalid
1141 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1142 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1143 @throws randolf::rex::xENETUNREACH No route to network
1144 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1145 @throws randolf::rex::xENOBUFS Insufficient memory
1146 @throws randolf::rex::xENOMEM Insufficient memory
1147 @throws randolf::rex::xENONET Requested host is not reachable on the network
1148 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1150 @throws randolf::rex::xENOSR System ran out of stream resources
1151 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1152 doesn't refer to a socket
1153 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1154 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1155 @throws randolf::rex::xEPROTO Protocol error
1156 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1157 supported within the specified family (a.k.a., communication domain)
1158 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1159 a system call is restartable and can be intercepted-and-redirected
1160 (there is no need to catch this exception unless you are an advanced
1161 developer, in which case you likely still won't need to catch it in
1163 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1164 supported within the specified family (a.k.a., communication domain)
1165 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1166 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1167 no inbound connections are waiting
1169 @returns Newly-created rsocket object representing the connection received,
1170 wrapped in std::shared_ptr (a C++ smart pointer)
1176 *///=========================================================================
1177 std::shared_ptr<rsocket> accept_sp() {
1178 if (__debug) debug("accept_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1181 // --------------------------------------------------------------------------
1182 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1183 // new incoming connection.
1184 // --------------------------------------------------------------------------
1185 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1186 __tls_new_endpoint = r.get(); // The SNI callback needs this
1188 // --------------------------------------------------------------------------
1189 // Wait for incoming connection, and update internal flags in client rsocket.
1190 // --------------------------------------------------------------------------
1191 r->__socket_fd = __rc_check(::accept(__socket_fd, (sockaddr*)&r->__socket_addr, &r->__socket_addr_size));
1192 r->__socket_open = true;
1193 r->__socket_connected = true;
1195 // --------------------------------------------------------------------------
1196 // Perform TLS accept() as well, but only if TLS is enabled.
1198 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1199 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1200 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1201 // --------------------------------------------------------------------------
1202 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1203 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1204 r->tls(true); // Make sure __tls_fd is configured correctly
1205 __rc_check_tls(SSL_accept(r->__tls_fd));
1206 } // -x- if __tls -x-
1209 }; // -x- std::shared_ptr<rsocket> accept_sp -x-
1211 /*======================================================================*//**
1213 Accept new [inbound] socket connetions, with socket flags specified. (This
1214 is typically used in a loop.)
1217 The resulting rsocket object is created before the actual call to the @c
1221 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1222 select(), and accept()/accept4() when using multiple sockets.
1225 To prevent resource leaks, the resulting rsocket needs to be deleted after
1226 it's no longer needed.
1228 @throws randolf::rex::xEBADF The underlying socket is not open
1229 @throws randolf::rex::xECONNABORTED The connection was aborted
1230 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1231 part of the user address space
1232 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1233 @throws randolf::rex::xEHOSTUNREACH No route to host
1234 @throws randolf::rex::xEINTR Interrupted by a signal
1235 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1236 length of the address is invalid
1237 @throws randolf::rex::xEINVAL Invalid value in flags
1238 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1239 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1240 @throws randolf::rex::xENETUNREACH No route to network
1241 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1242 @throws randolf::rex::xENOBUFS Insufficient memory
1243 @throws randolf::rex::xENOMEM Insufficient memory
1244 @throws randolf::rex::xENONET Requested host is not reachable on the network
1245 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1247 @throws randolf::rex::xENOSR System ran out of stream resources
1248 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1249 doesn't refer to a socket
1250 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1251 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1252 @throws randolf::rex::xEPROTO Protocol error
1253 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1254 supported within the specified family (a.k.a., communication domain)
1255 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1256 a system call is restartable and can be intercepted-and-redirected
1257 (there is no need to catch this exception unless you are an advanced
1258 developer, in which case you likely still won't need to catch it in
1260 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1261 supported within the specified family (a.k.a., communication domain)
1262 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1263 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1264 no inbound connections are waiting
1266 @returns Newly-created rsocket object representing the connection received
1273 *///=========================================================================
1275 /// SOCK_NONBLOCK@n SOCK_CLOEXEC
1276 const int flags = 0) {
1277 if (__debug) debug("accept4(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1278 + ", " + std::to_string(flags)
1281 // --------------------------------------------------------------------------
1282 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1283 // new incoming connection.
1284 // --------------------------------------------------------------------------
1285 rsocket* __tls_new_endpoint = new rsocket(this, false);
1287 // --------------------------------------------------------------------------
1288 // Wait for incoming connection, and update internal flags in client rsocket.
1289 // --------------------------------------------------------------------------
1290 __tls_new_endpoint->__socket_fd = ::accept4(__socket_fd, (sockaddr*)&__tls_new_endpoint->__socket_addr, &__tls_new_endpoint->__socket_addr_size, flags);
1291 if (__tls_new_endpoint->__socket_fd == -1) {
1292 delete __tls_new_endpoint; // Memory management
1293 __rc_check(-1); // This part throws the exception
1295 __tls_new_endpoint->__socket_open = true;
1296 __tls_new_endpoint->__socket_connected = true;
1298 // --------------------------------------------------------------------------
1299 // Perform TLS accept() as well, but only if TLS is enabled.
1301 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1302 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1303 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1304 // --------------------------------------------------------------------------
1305 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1306 __tls_new_endpoint->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1307 __tls_new_endpoint->tls(true); // Make sure __tls_fd is configured correctly
1308 __rc_check_tls(SSL_accept(__tls_new_endpoint->__tls_fd));
1309 } // -x- if __tls -x-
1311 return __tls_new_endpoint;
1312 }; // -x- rsocket* accept4 -x-
1314 /*======================================================================*//**
1316 Accept new [inbound] socket connetions, with socket flags specified. (This
1317 is typically used in a loop.)
1320 The resulting rsocket object is created before the actual call to the @c
1324 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1325 select(), and accept()/accept4() when using multiple sockets.
1328 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1329 aids in the prevention of resource leaks).
1331 @throws randolf::rex::xEBADF The underlying socket is not open
1332 @throws randolf::rex::xECONNABORTED The connection was aborted
1333 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1334 part of the user address space
1335 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1336 @throws randolf::rex::xEHOSTUNREACH No route to host
1337 @throws randolf::rex::xEINTR Interrupted by a signal
1338 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1339 length of the address is invalid
1340 @throws randolf::rex::xEINVAL Invalid value in flags
1341 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1342 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1343 @throws randolf::rex::xENETUNREACH No route to network
1344 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1345 @throws randolf::rex::xENOBUFS Insufficient memory
1346 @throws randolf::rex::xENOMEM Insufficient memory
1347 @throws randolf::rex::xENONET Requested host is not reachable on the network
1348 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1350 @throws randolf::rex::xENOSR System ran out of stream resources
1351 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1352 doesn't refer to a socket
1353 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1354 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1355 @throws randolf::rex::xEPROTO Protocol error
1356 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1357 supported within the specified family (a.k.a., communication domain)
1358 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1359 a system call is restartable and can be intercepted-and-redirected
1360 (there is no need to catch this exception unless you are an advanced
1361 developer, in which case you likely still won't need to catch it in
1363 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1364 supported within the specified family (a.k.a., communication domain)
1365 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1366 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1367 no inbound connections are waiting
1369 @returns Newly-created rsocket object representing the connection received,
1370 wrapped in std::shared_ptr (a C++ smart pointer)
1376 *///=========================================================================
1377 std::shared_ptr<rsocket> accept4_sp(
1378 /// SOCK_NONBLOCK@n SOCK_CLOEXEC
1379 const int flags = 0) {
1380 if (__debug) debug("accept4_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1381 + ", " + std::to_string(flags)
1384 // --------------------------------------------------------------------------
1385 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1386 // new incoming connection.
1387 // --------------------------------------------------------------------------
1388 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1389 __tls_new_endpoint = r.get(); // The SNI callback needs this
1391 // --------------------------------------------------------------------------
1392 // Wait for incoming connection, and update internal flags in client rsocket.
1393 // --------------------------------------------------------------------------
1394 r->__socket_fd = __rc_check(::accept4(__socket_fd, (sockaddr*)&r->__socket_addr, &r->__socket_addr_size, flags));
1395 r->__socket_open = true;
1396 r->__socket_connected = true;
1398 // --------------------------------------------------------------------------
1399 // Perform TLS accept() as well, but only if TLS is enabled.
1401 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1402 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1403 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1404 // --------------------------------------------------------------------------
1405 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1406 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1407 r->tls(true); // Make sure __tls_fd is configured correctly
1408 __rc_check_tls(SSL_accept(r->__tls_fd));
1409 } // -x- if __tls -x-
1410// if (__tls_ctx != nullptr) { r->tls(true); r->__tls = (bool)__tls; } // Make sure __tls_fd is allocated appropriately
1413 }; // -x- std::shared_ptr<rsocket> accept4_sp -x-
1415 /*======================================================================*//**
1417 Override the default @ref listen backlog for this rsocket.
1420 The default backlog is SOMAXCONN (4096 on Linux, and 128 on older systems).
1422 @returns The same rsocket object so as to facilitate stacking
1425 *///=========================================================================
1427 /// Backlog queue size (0 = use SOMAXCONN, which is the system default of
1428 /// 4096 on Linux, and 128 on older systems)
1429 int backlog = 0) noexcept {
1430 __socket_backlog = backlog == 0 ? SOMAXCONN : backlog;
1432 }; // -x- rsocket* backlog -x-
1434 /*======================================================================*//**
1436 Find out what this rsocket's default listen backlog is.
1437 @returns The default listen backlog
1440 *///=========================================================================
1441 int backlog() noexcept {
1442 return __socket_backlog;
1443 }; // -x- int backlog -x-
1445 /*======================================================================*//**
1447 Bind this socket to the specified network address (and port number if the
1448 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1449 used for server deamons, but can also be used to control the address from
1450 which the local host will make an outbound connection via the @ref connect()
1451 method (this bound address is the address from which the endpoint will
1452 recieve your connection).
1454 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
1455 (or 1023 on some Operating Systems that test only for below 1024) in
1456 the absence of elevated access or the absence of a capability flag
1458 @throws randolf::rex::xEACCES If binding to an interface on systems that
1459 require elevated access for direct interface binding in absence of
1460 said elevated access
1461 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1462 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
1463 address is not available on any local interface
1464 @throws randolf::rex::xEBADF The underlying socket is not open
1465 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1466 part of the user address space
1467 @throws randolf::rex::xEINVAL Socket is already bound
1468 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
1469 valid for this socket's family (a.k.a., communication domain)
1470 @throws randolf::rex::xENOMEM Insufficient memory
1471 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1472 doesn't refer to a socket
1474 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1475 @throws randolf::rex::xEACCES Write permission or search permission denied
1476 on a component of the path prefix (@c AF_UNIX family)
1477 @throws randolf::rex::xELOOP Too many symbolic links encountered while
1478 resolving address (@c AF_UNIX family)
1479 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
1480 @throws randolf::rex::xENOENT One of the path's directory components doesn't
1481 exist (@c AF_UNIX family)
1482 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
1483 directory (@c AF_UNIX family)
1484 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
1486 @returns The same rsocket object so as to facilitate stacking
1487 @see bind(std::string, int)
1492 *///=========================================================================
1494 /// Socket address structure
1495 const struct sockaddr* addr,
1496 /// Length of socket structure
1497 const socklen_t addrlen = sizeof(sockaddr)) {
1498 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1500 + ", size=" + std::to_string(addrlen)
1502 if (addrlen > sizeof(sockaddr_storage)) randolf::rex::mk_exception("addlen is too large for bind()", EINVAL);
1503 __rc_check(::bind(__socket_fd, addr, addrlen));
1504 std::memcpy(&__socket_addr, addr, addrlen); // No errors were returned by bind(), so now we'll copy addr to __socket_addr
1506 } // -x- rsocket* bind -x-
1508 //===========================================================================
1510 // TODO: Document usage of IP_BIND_ADDRESS_NO_PORT to support connecting
1511 // from a specific IP address without losing ephemeral port selection.
1512 // Notes: https://idea.popcount.org/2014-04-03-bind-before-connect/
1514 // TODO: Support AF_BLUETOOTH addresses
1516 // https://man7.org/linux/man-pages/man7/address_families.7.html
1518 // TODO: Support AF_IPX addresses
1520 // https://man7.org/linux/man-pages/man7/address_families.7.html
1522 // TODO: Support AF_APPLETALK addresses
1523 // Reference: https://man7.org/linux/man-pages/man7/ddp.7.html
1525 // TODO: Support AF_PACKET addresses
1526 // Reference: https://man7.org/linux/man-pages/man7/packet.7.html
1528 // TODO: Support AF_X25 addresses
1529 // Reference: https://man7.org/linux/man-pages/man7/x25.7.html
1531 // TODO: Support AF_NETLINK addresses
1532 // Reference: https://man7.org/linux/man-pages/man7/netlink.7.html
1534 /*======================================================================*//**
1536 Bind this socket to the specified network address (and port number if the
1537 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1538 used for server deamons, but can also be used to control the address from
1539 which the local host will make an outbound connection via the @ref connect()
1540 method (this bound address is address from which the endpoint will recieve
1543 In addition to the standard IPv4 (AF_INET family) and IPv6 (AF_INET6 family)
1544 support, rsocket also supports a few other binding options in a manner that
1545 makes it easy to utilize, without having to handle special implementation
1546 details (because they're handled behind-the-scenese by this rsocket class).
1548 The address string supports a number of different notations and formats,
1549 which are documented, hereunder, with examples:
1552 - Network interfaces
1553 - UNIX domain sockets
1556 @par IPv4 address notations (address family @c AF_INET)
1557 Takes the form of @c x.x.x.x where each "x" represents an octet in the range
1558 of @c 0 through @c 255 (e.g., @c 127.0.0.1).
1560 Under a proper implenetation of TCP/IP, an IPv4 address can be abbreviated
1561 to fewer octets. The following examples demonstrate this (an unabbreviated
1562 IPv4 address is included for completeness):
1563 - @c 0.0.0.1 may be abbreviated to @c 1
1564 - @c 4.0.0.1 may be abbrevaited to @c 4.1
1565 - @c 4.3.0.1 may be abbreviated to @c 4.3.1
1566 - @c 4.3.2.1 is not abbreviated (this is the most common usage)
1568 @par Example of binding to IPv4 localhost
1571 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1572 #include <randolf/rex>
1573 #include <randolf/rsocket>
1575 int main(int argc, char *argv[]) {
1577 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1578 r.bind("127.0.0.1", 32768); // <-- You are here
1579 // ... other socket I/O operations
1581 } catch (const randolf::rex::xEACCES e) {
1582 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1583 return EXIT_FAILURE;
1584 } catch (const randolf::rex::xALL e) {
1585 std::cerr << "Socket exception: " << e.what() << std::endl;
1586 return EXIT_FAILURE;
1587 } catch (const std::exception e) {
1588 std::cerr << "Other exception: " << e.what() << std::endl;
1589 return EXIT_FAILURE;
1591 return EXIT_SUCCESS;
1592 } // -x- int main -x-
1596 Specifying the IP address of @c 0.0.0.0 will bind to all IPv4 addresses that
1597 are assigned to all of the host machine's network interfaces with IPv4
1600 Specifying the IP address of @c 127.0.0.1 (localhost) is useful for serving
1601 only those applications that are running on the local host and use an IPv4
1602 socket to communicate.
1605 @par IPv6 address notations (address family @c AF_INET6)
1606 Takes the form of @c x:x:x:x:x:x:x:x where each "x" represents a segment in
1607 the range of @c 0 through @c ffff in hexadecimal (e.g., `0:0:0:0:0:0:0:1`),
1608 or `x:x:x:x:x:x:y.y.y.y` where `y.y.y.y` represents an [unabbreviated] IPv4
1609 address (which merely replaces the last two IPv6 segments).
1611 Under a proper implenetation of TCP/IP, an IPv6 address can be abbreviated
1612 to fewer segments by using a sequence of two colons (`::`) to indicate that
1613 the undefined segments are @c 0 (this abbreviation can only be used once,
1614 and may represent segments at the beginning or end, or anywhere in between).
1615 The following examples demonstrate this (an unabbreviated IPv6 address is
1616 included for completeness):
1617 - `0:0:0:0:0:0:0:1` may be abbreviated to `::1`
1618 - `0:0:0:0:0:0:2:1` may be abbreviated to `::2:1`
1619 - `8:0:0:0:0:0:2:1` may be abbreviated to `8::2:1`
1620 - `8:7:0:0:0:0:2:1` may be abbreviated to `8:7::2:1`
1621 - `8:7:0:0:0:0:0:0` may be abbreviated to `8:7::`
1622 - `8:0:0:0:0:0:0:0` may be abbreviated to `8::`
1623 - `8:7:6:5:4:3:2:1` is not abbreviated
1625 @par Example of binding to IPv6 localhost
1628 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1629 #include <randolf/rex>
1630 #include <randolf/rsocket>
1632 int main(int argc, char *argv[]) {
1634 randolf::rsocket r(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1635 r.bind("::1", 32768); // <-- You are here
1636 // ... other socket I/O operations
1638 } catch (const randolf::rex::xEACCES e) {
1639 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1640 return EXIT_FAILURE;
1641 } catch (const randolf::rex::xALL e) {
1642 std::cerr << "Socket exception: " << e.what() << std::endl;
1643 return EXIT_FAILURE;
1644 } catch (const std::exception e) {
1645 std::cerr << "Other exception: " << e.what() << std::endl;
1646 return EXIT_FAILURE;
1648 return EXIT_SUCCESS;
1649 } // -x- int main -x-
1653 Specifying the IP address of @c :: will bind to all IPv6 addresses that are
1654 assigned to all of the host machine's network interfaces with IPv6 bindings.
1656 Specifying the IP address of @c ::1 (localhost) is useful for serving only
1657 those applications that are running on the local host and use an IPv6 socket
1661 @par Interface address notation (address families @c AF_INET and @c AF_INET6)
1662 Takes the form of @c if=name where "name" represents the name of a local
1665 Interface binding is useful when binding to interfaces that aren't configured
1666 with a static IP address because they were dymamically configured via the
1667 Dynamic Host Configuration Protocol (DHCP) or Stateless Address Configuration
1668 (SLAAC), or the configuration was changed manually by an administrator and
1669 you don't want your software to handle such changes gracefully, even if the
1670 new IP address is within the scope of an entirely different CIDR. (Changing
1671 between the IPv4 and IPv6 addresses is not supported, however, due to the
1672 fundamental differences between these two address families that includes
1673 differences beyond that of IP address format, although under a proper
1674 implementation of TCP/IP the binding should survive such changes when the IP
1675 address is reverted to the initial IP address family of the bound interface.)
1677 Examples of interfaces include:
1678 - `if=lo` typical for localhost virtual network adapter
1679 - `if=bond0` typical for the first bonded virtual network adapter (used in
1680 failover and load-balancing network configurations)
1681 - `if=br0` typical for the first bridge virtual network adapter
1682 - `if=eth0` typical for the first ethernet network adapter
1683 - `if=tap0` typical for the first virtual layer 2 ethernet switch (used by
1684 VPNs to extend a remote network into the local network stack)
1685 - `if=tun0` typical for the first virtual layer 3 ethernet tunnel (used by
1686 VPNs to provide access to a remote network)
1687 - `if=wlo1` typical for the first wireless network adapter
1689 @par Example of binding to interface localhost
1692 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1693 #include <randolf/rex>
1694 #include <randolf/rsocket>
1696 int main(int argc, char *argv[]) {
1698 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1699 r.bind("if=lo", 32768); // <-- You are here
1700 // ... other socket I/O operations
1702 } catch (const randolf::rex::xEACCES e) {
1703 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1704 return EXIT_FAILURE;
1705 } catch (const randolf::rex::xALL e) {
1706 std::cerr << "Socket exception: " << e.what() << std::endl;
1707 return EXIT_FAILURE;
1708 } catch (const std::exception e) {
1709 std::cerr << "Other exception: " << e.what() << std::endl;
1710 return EXIT_FAILURE;
1712 return EXIT_SUCCESS;
1713 } // -x- int main -x-
1717 This is not a standard feature of the POSIX bind() function. This rsocket
1718 class uses the setsockopt() function behind-the-scenes to configure (enable)
1719 the @c SO_BINDTODEVICE option, and then the bind() function is called with
1720 the IPv4 address @c 0.0.0.0 if the underlying socket was initialized to the
1721 @c AF_INET family, or the IPv6 address @c :: if the underlying socket was
1722 initialized to the @c AF_INET6 family.
1724 Specifying the interface address of `if=lo` (localhost) is useful for serving
1725 only those applications that are running on the local host and use an IPv4 or
1726 IPv6 socket to communicate.
1729 @par UNIX Domain Sockets address-notation (address family @c AF_UNIX)
1730 Takes the form of @c /path where "/path" represents an absolute path on the
1731 local file system (the path can also be relative, in which case it doesn't
1732 begin with a slash (`/`) character, but extra care is needed to ensure that
1733 the path will actually be at its expected location).
1735 @par Example of binding to UNIX domain sockets
1738 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1739 #include <randolf/rex>
1740 #include <randolf/rsocket>
1742 int main(int argc, char *argv[]) {
1744 randolf::rsocket r(AF_UNIX, SOCK_STREAM);
1745 r.bind("/var/run/rsocket-test-socket.tmp"); // <-- You are here
1746 // ... other socket I/O operations
1748 } catch (const randolf::rex::xEACCES e) {
1749 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1750 return EXIT_FAILURE;
1751 } catch (const randolf::rex::xALL e) {
1752 std::cerr << "Socket exception: " << e.what() << std::endl;
1753 return EXIT_FAILURE;
1754 } catch (const std::exception e) {
1755 std::cerr << "Other exception: " << e.what() << std::endl;
1756 return EXIT_FAILURE;
1758 return EXIT_SUCCESS;
1759 } // -x- int main -x-
1763 Normal socket I/O functionality is used to exchange data with another process
1764 that can open the same UNIX domain socket (normally on the same host,
1765 although it may not be outside the realm of possibility for future Linux
1766 kernels to support UNIX domain sockets on remote hosts).
1769 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
1770 (or 1023 on some Operating Systems that test only for below 1024) in
1771 the absence of elevated access or the absence of a capability flag
1773 @throws randolf::rex::xEACCES If binding to an interface on systems that
1774 require elevated access for direct interface binding in absence of
1775 said elevated access
1776 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1777 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
1778 address is not available on any local interface
1779 @throws randolf::rex::xEBADF The underlying socket is not open
1780 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1781 part of the user address space
1782 @throws randolf::rex::xEINVAL Socket is already bound
1783 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
1784 valid for this socket's family (a.k.a., communication domain)
1785 @throws randolf::rex::xENOMEM Insufficient memory
1786 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1787 doesn't refer to a socket
1789 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1790 @throws randolf::rex::xEACCES Write permission or search permission denied
1791 on a component of the path prefix (@c AF_UNIX family)
1792 @throws randolf::rex::xELOOP Too many symbolic links encountered while
1793 resolving address (@c AF_UNIX family)
1794 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
1795 @throws randolf::rex::xENOENT One of the path's directory components doesn't
1796 exist (@c AF_UNIX family)
1797 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
1798 directory (@c AF_UNIX family)
1799 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
1801 @returns The same rsocket object so as to facilitate stacking
1802 @see bind(const struct sockaddr*, const socklen_t)
1806 *///=========================================================================
1808 /// IPv4 address, IPv6 address, hostname, UNIX domain sockets path, or specialized variant (see notes, above)
1809 const std::string address,
1810 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
1811 const int port = 0) {
1812 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1814 + " port=" + std::to_string(port) + (port == 0 ? " (emphemeral or not-applicable)" : "")
1815 + ", size=" + std::to_string(__socket_addr_size)
1818//std::cout << " address: " << address << std::endl;
1819 if (address.starts_with("if=")) { // Bind to interface
1820// TODO: Use family() to simplify this section of code
1822 if (address.size() - 3 >= sizeof(ifr.ifr_name)) {} // Throw exception because ASCIIZ name will be too long
1823 address.copy(ifr.ifr_name, address.size() - 3, 3); // Copy address, excluding the "if=" portion
1824 __rc_check(::setsockopt(__socket_fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)));
1825 // TODO: Figure out how to use INADDR_ANY instead of 0 or :: (assuming this is even possible)
1826 // TODO: Test IPv6 more (telnet doesn't seem to be able to connect, and continues to work on IPv4 address)
1827 __socket_addr = *mk_sockaddr_storage(__socket_addr.ss_family == AF_INET6 ? "::" : "0", port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
1828 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
1830// } else if (address.starts_with("if_cidr=")) { // Bind to interface based on matching CIDR
1831// // REMINDER: Update static family() method as well
1832// getifaddrs: https://man7.org/linux/man-pages/man3/getifaddrs.3.html
1833// } else if (address.starts_with("if_mac=")) { // Bind to interface based on its physical/hardware machine address
1834// // REMINDER: Update static family() method as well
1835// // Resolve interface name, and then just do what if= does (above)
1838 __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
1839 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
1840// __rc_check(::bind(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
1843 } // -x- rsocket* bind -x-
1845 /*======================================================================*//**
1847 Find out what buffer size is used by the various recv() methods.
1848 @returns Buffer size (in bytes)
1849 @see buffer_size(const size_t nbytes)
1852 *///=========================================================================
1853 const int buffer_size() noexcept {
1854 return __buffer_size;
1855 }; // -x- int buffer_size -x-
1857 /*======================================================================*//**
1859 Override the default buffer size (typically 1024) used by the various recv()
1862 If resetting to the compiled-in default, use the buffer_size_reset() method
1863 instead of setting the value directly. This ensures that future versions,
1864 with a different compiled-in default, will be reset to the compiled-in value.
1865 @returns The same rsocket object so as to facilitate stacking
1867 @see buffer_size_reset
1869 *///=========================================================================
1870 rsocket* buffer_size(
1871 /// Size of the new buffer (in bytes)
1872 const size_t nbytes) noexcept {
1873 __buffer_size = nbytes;
1874 if (__debug) debug("buffer_size(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1875 + " nbytes=" + std::to_string(nbytes)
1878 }; // -x- rsocket* buffer_size -x-
1880 /*======================================================================*//**
1882 Reset the default buffer size (typically 1024) used by the various recv()
1885 This method is preferred for resetting to the compiled-in default instead of
1886 setting the value directly. This ensures that future versions, with a
1887 different compiled-in default, will be reset to the compiled-in value.
1888 @returns The same rsocket object so as to facilitate stacking
1889 @see buffer_size(const size_t nbytes)
1891 *///=========================================================================
1892 rsocket* buffer_size_reset() noexcept {
1893 __buffer_size = RSOCKET_BUFFER_SIZE;
1894 if (__debug) debug("buffer_size_reset(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1895 + " nbytes=" + std::to_string(RSOCKET_BUFFER_SIZE)
1898 }; // -x- rsocket* buffer_size_reset -x-
1900 //===========================================================================
1902 // TODO: If family is AF_UNIX then also unlink the domain socket file.
1904 /*======================================================================*//**
1906 Close this rsocket. (If this rsocket was already closed, then calling this
1907 method additional times will have no effect, and will not cause exceptions to
1910 This method may throw exceptions in some circumstances, which is extremely
1911 rare. If you prefer to close without having to contend with any exceptions
1912 (e.g., while calling close() from within a destructor), the close_passive()
1913 method will return an integer indicating success/failure instead of throwing
1914 an exception, and then you'll have to handle errno manually (which is useful
1915 in destructors because any error can merely be handled procedurally).
1917 @throws randolf::rex::xEBADF The underlying socket is not open
1918 @throws randolf::rex::xEINTR Interrupted by a signal
1919 @throws randolf::rex::xEIO An I/O error occurred
1921 @returns The same rsocket object so as to facilitate stacking
1925 *///=========================================================================
1927 if (__socket_open) {
1928 __rc_check(::close(__socket_fd));
1929 __socket_open = false;
1930 } // -x- if __socket_open -x-
1932 }; // -x- rsocket* close -x-
1934 /*======================================================================*//**
1936 Close this rsocket without throwing any exceptions (an error code is returned
1937 instead, which is useful while calling close_passive() from within a
1939 @returns 0 = success
1940 @returns -1 = error (errno will be set accordingly)
1943 *///=========================================================================
1944 int close_passive() noexcept {
1945 if (__socket_open) {
1946 int rc = ::close(__socket_fd);
1947 if (rc == 0) __socket_open = false;
1949 } // -x- if __socket_open -x-
1950 return 0; // Indicate success (because the socket was previously closed successfully)
1951 }; // -x- int close_passive -x-
1953 /*======================================================================*//**
1955 Connect this socket to a specific endpoint (which may differ from this
1956 rsocket's address that was previously configured by the @ref bind() method).
1958 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1959 @throws randolf::rex::xEADDRNOTAVAIL No ephemeral ports are available for
1960 assignment to unbound socket
1961 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
1962 @throws randolf::rex::xEAGAIN Insufficient entries in the routing cache
1963 @throws randolf::rex::xEALREADY Previous connection attempt not completed on
1965 @throws randolf::rex::xEBADF The underlying socket is not open
1966 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
1968 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1969 part of the user address space
1970 @throws randolf::rex::xEINPROGRESS Connection cannot be completed immediately
1971 on nonblocking socket (@c AF_UNIX family fails with xEAGAIN instead)
1972 @throws randolf::rex::xEINTR Interrupted by a signal
1973 @throws randolf::rex::xEISCONN Socket is already connected
1974 @throws randolf::rex::xENETUNREACH No route to network
1975 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1976 doesn't refer to a socket
1977 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1978 @throws randolf::rex::xEPERM Can't connect to a broadcast address when the
1979 socket's broadcast flag isn't enabled
1980 @throws randolf::rex::xEPROTOTYPE Socket type doesn't support the requested
1981 communications protocol (e.g., connecting a UNIX domain socket of
1982 type @c SOCK_STREAM to an endpoint expecting type @c SOCK_DGRAM)
1983 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1985 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1986 @throws randolf::rex::xEACCES Write permission or search permission denied
1987 on a component of the path prefix (@c AF_UNIX family)
1988 @throws randolf::rex::xEAGAIN Connection cannot be completed immediately on
1989 nonblocking socket (@c AF_UNIX family)
1991 @returns The same rsocket object so as to facilitate stacking
1992 @see tls_do_handshake()
1995 *///=========================================================================
1997 /// IPv4 address, IPv6 address, hostname, or UNIX domain sockets path
1998 const std::string address,
1999 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
2000 const int port = 0) {
2001 if (__debug) debug("connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2003 + " (port=" + std::to_string(port) + ")"
2004 + ", " + std::to_string(__socket_addr_size)
2007 __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
2008 __rc_check(::connect(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size), 0, rex::rex::REX_FLAGS::REX_ALT_EAGAIN);
2010// __rc_check(::connect(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
2011 __socket_connected = true;
2013 // --------------------------------------------------------------------------
2015 // --------------------------------------------------------------------------
2017 if (__debug) debug("tls_connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2019 __rc_check_tls(SSL_connect(__tls_fd));
2020 } // -x- if __tls -x-
2023 }; // -x- rsocket* connect -x-
2025 /*======================================================================*//**
2027 Find out whether debug mode is enabled.
2030 This method is threadsafe.
2031 @returns TRUE = enabled
2032 @returns FALSE = not enabled
2034 *///=========================================================================
2035 const bool debug() noexcept { return __debug; };
2037 /*======================================================================*//**
2039 Debug mode. When debug mode is enabled, output is sent to stderr by default,
2040 unless a second parameter specifies a different file handle (e.g., stdout, or
2043 debug(...) returns -1 if fd can't be written to (errno will be set in
2044 accordance with std::fprintf's behaviour since std::fprintf is used for
2045 writing debug output). Normally we'd throw an exception for such errors, but
2046 with debug is a special case because debugging needs to be as quick and
2047 convenient as possible for developers.
2049 debug(...) returns -2 if writing to stderr failed when attempting to announce
2050 that fd can't be written to (as described above).
2052 debug(...) returns -3 if some other error occurs (e.g., internal formatting
2053 error {which should not happen} or memory allocation failed, in which case
2054 there's a more serious problem that will be causing other problems elsewhere
2057 Developers may add their own debug messages by using debug(std::string),
2058 which will only be written out if debug mode is enabled. This same method is
2059 used internally, so messages will be indistinguishable from developer's
2063 This method is thread-safe.
2064 @returns 0 = success
2065 @returns -1 = error writing to stream (errno will be set accordingly)
2066 @returns -2 = error writing to stderr (errno will be set accordingly)
2067 @returns -3 = error (errno will be set accordingly)
2069 *///=========================================================================
2071 /// TRUE = enable debug mode@n
2072 /// FALSE = disable debug mode (does not close any file handles)
2073 const bool debug_flag,
2074 /// File descriptor/handle to use for debug output
2075 std::FILE* fd = stderr) noexcept {
2077 int rc = debug_fd(fd); // Attempt to change debug handle
2078 if (rc != 0) return rc; // Return error id new debug handle failed
2079 time_t tm = std::time(nullptr);
2080 std::string dt(27, 0);
2081 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2083 if (debug_flag || __debug) std::fprintf(__debug_fd,
2084 "[%s] %s: Debug mode is %s\n",
2086 __debug_prefix.c_str(),
2087 debug_flag ? "ON" : "OFF");
2088 } catch (const std::system_error& e) { // Error writing to fd
2090 std::fprintf(stderr,
2091 "[%s] %s: error writing to stream\n",
2093 __debug_prefix.c_str());
2094 } catch (const std::exception& e) {
2098 } catch (const std::exception& e) {
2101 __debug = debug_flag; // Save debug flag
2103 return 0; // Indicate success (no errors)
2104 }; // -x- int debug -x-
2106 /*======================================================================*//**
2108 Send the specified message as debug output (as long as debug mode is enabled;
2109 if disabled, no debug output will be sent).
2110 @returns 0 = success
2111 @returns -1 = error writing to stream (errno will be set accordingly)
2112 @returns -2 = error writing to stderr (errno will be set accordingly)
2113 @returns -3 = error (errno will be set accordingly)
2115 *///=========================================================================
2117 /// Debug message as an ASCIIZ string
2118 const char* msg) noexcept {
2119 return __debug ? __debug_out(std::string(msg)) : 0;
2120 }; // -x- int debug -x-
2122 /*======================================================================*//**
2123 @copydoc debug(const char*)
2125 *///=========================================================================
2127 /// Debug message as an std::string object
2128 const std::string msg) noexcept {
2129 return __debug ? __debug_out(msg) : 0;
2130 }; // -x- int debug -x-
2132 /*======================================================================*//**
2134 Find out which file descriptor/handle is used for debug output.
2135 @returns file handle/descriptor currently used for debug output
2136 *///=========================================================================
2137 const std::FILE* debug_fd() noexcept { return __debug_fd; }; // Get debug-output file handle
2139 /*======================================================================*//**
2141 Specify a different file descriptor/handle to use for debug output
2142 @returns 0 = success
2143 @returns -1 = error writing to stream (errno will be set accordingly)
2144 @returns -2 = error writing to stderr (errno will be set accordingly)
2145 @returns -3 = error (errno will be set accordingly)
2146 *///=========================================================================
2148 /// File descriptor/handle to use for debug output
2149 std::FILE* fd) noexcept { // Set debug-output file handle
2150 if (__debug_fd != fd) {
2151 debug("Current debug-output file handle unset"); // For this message, we only want to send this out if debug mode is enabled
2152 __debug_fd = fd; // Save new debug-output file handle
2153 return debug("New debug-output file handle set"); // For this message, we only want to send this out if debug mode is enabled
2155 return 0; // Indicate success (no errors)
2156 }; // -x- int debug_fd -x-
2158 /*======================================================================*//**
2160 Find out what the current prefix is set to that's used in debug output.
2162 This may be set at any time, including before or after enabling or disabling
2164 @returns debug prefix string
2166 *///=========================================================================
2167 const std::string debug_prefix() noexcept { return __debug_prefix; }; // -x- std::string debug_prefix -x-
2169 /*======================================================================*//**
2171 Change the prefix used in debug output.
2172 @returns The same rsocket object so as to facilitate stacking
2174 *///=========================================================================
2175 rsocket* debug_prefix(
2176 /// New debug prefix string
2177 const std::string prefix) noexcept {
2178 __debug_prefix = prefix;
2180 }; // -x- std::string debug_prefix -x-
2183 /*----------------------------------------------------------------------*//**
2185 Internal debug-output function. This doesn't check __debug flag because it's
2186 expected that the normal debug() methods are taking care of this.
2187 @returns 0 = success
2188 @returns -1 = error writing to stream (errno will be set accordingly)
2189 @returns -2 = error writing to stderr (errno will be set accordingly)
2190 @returns -3 = error (errno will be set accordingly)
2191 *///-------------------------------------------------------------------------
2192 const int __debug_out(
2193 /// Debugging message
2194 const std::string msg) noexcept {
2196 time_t tm = std::time(nullptr);
2197 std::string dt(27, 0);
2198 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2200 std::fprintf(__debug_fd,
2203 __debug_prefix.c_str(),
2205 } catch (const std::system_error& e) { // Error writing to fd
2207 std::fprintf(stderr,
2208 "[%s] %s: error writing to stream\n",
2210 __debug_prefix.c_str());
2211 } catch (const std::exception& e) {
2215 } catch (const std::exception& e) {
2219 return 0; // Indicate success (no errors)
2220 }; // -x- int __debug_out -x-
2222 // --------------------------------------------------------------------------
2223 // These are specialized internal debug output methods for presenting socket
2224 // options when they are get or set. These methods are marked as "private"
2225 // because they are really aren't useful outside of internal-use contexts.
2228 // Some of these methods are thread-safe.
2230 // These methods are thread-safe when they don't reference structures (e.g.,
2231 // integer {int}, unsigned integer {u_int}, and unsigned character {u_char}).
2233 // These methods are not necessarily thread-safe if they reference structures
2234 // unless those structures are defined with std::atomic, or they were
2235 // constructed on-the-fly as anonymous parameters.
2236 // --------------------------------------------------------------------------
2237 const int __debug_sockopt(
2238 /// Name of function or method
2239 const std::string func,
2240 /// The level at which the option resides; typically @c SOL_SOCKET
2242 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2244 /// The structure that this socket option will be set to
2246 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2247 + ", " + std::to_string(level)
2248 + ", " + std::to_string(option)
2249 + ", " + std::to_string(value)
2250 + ", size=" + std::to_string(sizeof(value))
2252 }; // -x- int __debug_sockopt -x-
2253 const int __debug_sockopt(
2254 /// Name of function or method
2255 const std::string func,
2256 /// The level at which the option resides; typically @c SOL_SOCKET
2258 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2260 /// The structure that this socket option will be set to
2261 const u_int value) {
2262 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2263 + ", " + std::to_string(level)
2264 + ", " + std::to_string(option)
2265 + ", u_int{" + std::to_string(value) + "}"
2266 + ", size=" + std::to_string(sizeof(value))
2268 }; // -x- int __debug_sockopt -x-
2269 const int __debug_sockopt(
2270 /// Name of function or method
2271 const std::string func,
2272 /// The level at which the option resides; typically @c SOL_SOCKET
2274 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2276 /// The structure that this socket option will be set to
2277 const u_char value) {
2278 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2279 + ", " + std::to_string(level)
2280 + ", " + std::to_string(option)
2281 + ", u_char{" + std::to_string(value) + "}"
2282 + ", size=" + std::to_string(sizeof(value))
2284 }; // -x- int __debug_sockopt -x-
2285 const int __debug_sockopt(
2286 /// Name of function or method
2287 const std::string func,
2288 /// The level at which the option resides; typically @c SOL_SOCKET
2290 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2292 /// The structure that this socket option will be set to
2293 const linger* value) {
2294 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2295 + ", " + std::to_string(level)
2296 + ", " + std::to_string(option)
2297 + ", linger{l_onoff=" + std::to_string(value->l_onoff)
2298 + ", l_linger=" + std::to_string(value->l_linger) + "}"
2299 + ", " + std::to_string(sizeof(value))
2301 }; // -x- int __debug_sockopt -x-
2302 const int __debug_sockopt(
2303 /// Name of function or method
2304 const std::string func,
2305 /// The level at which the option resides; typically @c SOL_SOCKET
2307 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2309 /// The structure that this socket option will be set to
2310 const timeval* value) {
2311 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2312 + ", " + std::to_string(level)
2313 + ", " + std::to_string(option)
2314 + ", timeval{tv_sec=" + std::to_string(value->tv_sec)
2315 + ", tv_usec=" + std::to_string(value->tv_usec) + "}"
2316 + ", " + std::to_string(sizeof(value))
2318 }; // -x- int __debug_sockopt -x-
2319 const int __debug_sockopt(
2320 /// Name of function or method
2321 const std::string func,
2322 /// The level at which the option resides; typically @c SOL_SOCKET
2324 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2326 /// The structure that this socket option will be set to
2327 const in_addr* value) {
2328 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2329 + ", " + std::to_string(level)
2330 + ", " + std::to_string(option)
2331 + ", in_addr{" + std::to_string(value->s_addr) + "}"
2332 + ", " + std::to_string(sizeof(value))
2334 }; // -x- int __debug_sockopt -x-
2335 const int __debug_sockopt(
2336 /// Name of function or method
2337 const std::string func,
2338 /// The level at which the option resides; typically @c SOL_SOCKET
2340 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2342 /// The structure that this socket option will be set to
2343 const ip_mreq* value) {
2344 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2345 + ", " + std::to_string(level)
2346 + ", " + std::to_string(option)
2347 + ", ip_mreq{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2348 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2349 + ", " + std::to_string(sizeof(value))
2351 }; // -x- int __debug_sockopt -x-
2352 const int __debug_sockopt(
2353 /// Name of function or method
2354 const std::string func,
2355 /// The level at which the option resides; typically @c SOL_SOCKET
2357 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2359 /// The structure that this socket option will be set to
2360 const ip_mreq_source* value) {
2361 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2362 + ", " + std::to_string(level)
2363 + ", " + std::to_string(option)
2364 + ", ip_mreq_source{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2365 + ", source=" + std::to_string(value->imr_sourceaddr.s_addr)
2366 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2367 + ", " + std::to_string(sizeof(value))
2369 }; // -x- int __debug_sockopt -x-
2370 const int __debug_sockopt(
2371 /// Name of function or method
2372 const std::string func,
2373 /// The level at which the option resides; typically @c SOL_SOCKET
2375 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2377 /// The structure that this socket option will be set to
2378 const icmp6_filter* value) {
2379 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2380 + ", " + std::to_string(level)
2381 + ", " + std::to_string(option)
2382 + ", icmp6_filter{[0]=" + std::to_string(value->icmp6_filt[0])
2383 + ", [1]=" + std::to_string(value->icmp6_filt[1])
2384 + ", [2]=" + std::to_string(value->icmp6_filt[2])
2385 + ", [3]=" + std::to_string(value->icmp6_filt[3])
2386 + ", [4]=" + std::to_string(value->icmp6_filt[4])
2387 + ", [5]=" + std::to_string(value->icmp6_filt[5])
2388 + ", [6]=" + std::to_string(value->icmp6_filt[6])
2389 + ", [7]=" + std::to_string(value->icmp6_filt[7]) + "}"
2390 + ", " + std::to_string(sizeof(value))
2392 }; // -x- int __debug_sockopt -x-
2393 const int __debug_sockopt(
2394 /// Name of function or method
2395 const std::string func,
2396 /// The level at which the option resides; typically @c SOL_SOCKET
2398 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2400 /// The structure that this socket option will be set to
2401 const sockaddr_in6* value) {
2402 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2403 + ", " + std::to_string(level)
2404 + ", " + std::to_string(option)
2405 + ", sockaddr_in6{family=" + std::to_string(value->sin6_family)
2406 + ", port=" + std::to_string(value->sin6_port)
2407 + ", flowinfo=" + std::to_string(value->sin6_flowinfo)
2408 + ", addr=" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[0])
2409 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[1])
2410 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[2])
2411 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[3])
2412 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[4])
2413 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[5])
2414 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[6])
2415 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[7])
2416 + ", scope_id=" + std::to_string(value->sin6_scope_id) + "}"
2417 + ", " + std::to_string(sizeof(value))
2419 }; // -x- int __debug_sockopt -x-
2420 const int __debug_sockopt(
2421 /// Name of function or method
2422 const std::string func,
2423 /// The level at which the option resides; typically @c SOL_SOCKET
2425 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2427 /// The structure that this socket option will be set to
2428 const ip6_mtuinfo* value) {
2429 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2430 + ", " + std::to_string(level)
2431 + ", " + std::to_string(option)
2432 + ", ip6_mtuinfo{addr=" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[0])
2433 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[1])
2434 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[2])
2435 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[3])
2436 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[4])
2437 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[5])
2438 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[6])
2439 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[7])
2440 + ", mtu=" + std::to_string(value->ip6m_mtu) + "}"
2441 + ", " + std::to_string(sizeof(value))
2443 }; // -x- int __debug_sockopt -x-
2444 const int __debug_sockopt(
2445 /// Name of function or method
2446 const std::string func,
2447 /// The level at which the option resides; typically @c SOL_SOCKET
2449 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2451 /// The structure that this socket option will be set to
2452 const ipv6_mreq* value) {
2453 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2454 + ", " + std::to_string(level)
2455 + ", " + std::to_string(option)
2456 + ", ipv6_mreq{addr=" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[0])
2457 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[1])
2458 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[2])
2459 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[3])
2460 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[4])
2461 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[5])
2462 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[6])
2463 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[7])
2464 + ", iface=" + std::to_string(value->ipv6mr_interface) + "}"
2465 + ", " + std::to_string(sizeof(value))
2467 }; // -x- int __debug_sockopt -x-
2468 const int __debug_sockopt(
2469 /// Name of function or method
2470 const std::string func,
2471 /// The level at which the option resides; typically @c SOL_SOCKET
2473 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2475 /// The structure that this socket option will be set to
2476 const group_req* value) {
2477 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2478 + ", " + std::to_string(level)
2479 + ", " + std::to_string(option)
2480 + ", group_req{iface=" + std::to_string(value->gr_interface)
2482 + ", " + std::to_string(sizeof(value))
2484 }; // -x- int __debug_sockopt -x-
2485 const int __debug_sockopt(
2486 /// Name of function or method
2487 const std::string func,
2488 /// The level at which the option resides; typically @c SOL_SOCKET
2490 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2492 /// The structure that this socket option will be set to
2493 const group_source_req* value) {
2494 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2495 + ", " + std::to_string(level)
2496 + ", " + std::to_string(option)
2497 + ", group_source_req{iface=" + std::to_string(value->gsr_interface)
2498 + ", <group_source_req>}"
2499 + ", " + std::to_string(sizeof(value))
2501 }; // -x- int __debug_sockopt -x-
2502 const int __debug_sockopt_other(
2503 /// Name of function or method
2504 const std::string func,
2505 /// The level at which the option resides; typically @c SOL_SOCKET
2507 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2509 /// The structure that this socket option will be set to
2510 const socklen_t size) {
2511 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2512 + ", " + std::to_string(level)
2513 + ", " + std::to_string(option)
2515 + ", " + std::to_string(size)
2517 }; // -x- int __debug_sockopt_other -x-
2520 /*======================================================================*//**
2522 Discards the specified number of 8-bit bytes efficiently, and without closing
2523 the stream, and without consuming excessive quantities of memory.
2524 @exception randolf::rex::xERANGE An invalid value was specified for either
2525 the @c nbytes or @c memory_size parameter (e.g., a negative value,
2526 except for -1 with the @c nbytes parameter)
2527 @returns Number of bytes that were successfully discarded
2531 *///=========================================================================
2533 /// Number of bytes to discard@n
2534 /// 0 = use internal @ref buffer_size()@n
2535 /// -1 = all remaining data waiting to be received (in other words, this
2536 /// method will repeatedly consume all data until @ref eos)
2538 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
2539 const int flags = 0,
2540 /// Maximum internal read size@n
2541 /// 0 = default to @ref buffer_size (when @c nbytes is larger than this size,
2542 /// multiple reads into this buffer will be utilized behind-the-scenes to
2543 /// consume the quantity of data specified by the @c nbytes parameter)
2544 const size_t memory_size = 0) {
2545 int buf_size = memory_size != 0 ? memory_size : __buffer_size;
2546 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2547 + ", " + std::to_string(nbytes)
2548 + ", " + std::to_string(flags)
2549 + ", " + std::to_string(buf_size)
2552 // --------------------------------------------------------------------------
2554 // --------------------------------------------------------------------------
2555 if (nbytes == 0) nbytes = __buffer_size;
2556 else if (nbytes < -1) throw randolf::rex::xERANGE( "nbytes parameter of " + std::to_string(nbytes) + " is below 0");
2557 if (memory_size < 0) throw randolf::rex::xERANGE("memory_size parameter of " + std::to_string(memory_size) + " is below 0");
2559 // --------------------------------------------------------------------------
2560 // Internal variables.
2561 // --------------------------------------------------------------------------
2562 int discarded = 0; // Total number of bytes discarded
2563 std::vector<char> buf(buf_size);
2564 buf.reserve(buf_size); // Pre-allocate underlying array to prevent memory corruption when __recv writes directly into it
2566 // --------------------------------------------------------------------------
2568 // --------------------------------------------------------------------------
2571 // --------------------------------------------------------------------------
2572 // Discard all remaining data waiting to be received.
2573 // --------------------------------------------------------------------------
2576 while ((n = __recv(buf.data(), buf_size, flags)) != 0) {
2578 }; // -x- while n -x-
2580 // --------------------------------------------------------------------------
2581 // Discard specific amount of data waiting to be received.
2582 // --------------------------------------------------------------------------
2584 int inbytes = nbytes;
2585 while (inbytes > 0) {
2586 int n = __recv(buf.data(), std::min(inbytes, buf_size), flags);
2589 } // -x- while nbytes -x-
2590 } // -x- if nbytes -x-
2592 // --------------------------------------------------------------------------
2593 // Ignore exception so that we can simply indicate how many bytes were
2594 // successfully received and discarded.
2595 // --------------------------------------------------------------------------
2596 } catch(const rex::xALL e) {}
2598 // --------------------------------------------------------------------------
2599 // Erase all data to prevent data from being unexpectedly leaked to any other
2600 // memory allocations later that use some or all of the same memory.
2601 // --------------------------------------------------------------------------
2602 for (int i = nbytes <= 0 ? buf_size : std::min(nbytes, buf_size); i > 0; i--) {
2607 }; // -x- int discard -x-
2609 /*======================================================================*//**
2611 Find out what the current EoL (End of Line) sequence is set to.
2613 To send an EoL sequence do not use `send(r.eol())` because it may not be
2614 initialized yet and the endpoint you're sending to may seem unresponsive or
2615 other unexpected behaviour may occur.@n
2617 To send an EoL sequence properly, use @ref sendline(); although specifying no
2618 parameters is more efficient than specifying an empty string (@c ""), the
2619 specialized @ref send_eol() method is the most efficient option for sending
2620 an EoL sequence separately.
2622 An empty EoL sequence means that CRLF will be sent by @ref sendline(), and
2623 that @ref recvline(), and @ref recv_rline() will automatically detect from
2624 one of @c CR, @c CRLF, @c LF, and @c LFCR.
2625 @returns Current EoL sequence
2635 *///=========================================================================
2636 const std::string eol() noexcept { return __eol; }; // -x- std::string eol -x-
2638 /*======================================================================*//**
2640 Set EoL (End of Line) sequence. This sequence is used by recvline(),
2641 recv_rline(), sendline(), and related functions, and it defaults to an empty
2642 string which results in the EoL sequence being detected automatically
2645 - @c "" (empty string) = automatically detect on-the-fly (default)
2646 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
2647 - @c \\r (CR) = Carriage Return (typical for MacOS)
2648 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
2651 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
2652 IMAP4, FINGER, NICNAME/WHOIS, etc.).
2653 @returns The same rsocket object so as to facilitate stacking
2663 *///=========================================================================
2665 /// EoL sequence as an ASCIIZ string
2666 const char* eol) noexcept {
2667 __eol = std::string(eol);
2668 __eol_out = __eol.empty() ? __CRLF : __eol;
2670 }; // -x- rsocket* eol -x-
2672 /*======================================================================*//**
2674 Set EoL (End of Line) sequence. This sequence is used by recvline(),
2675 recv_rline(), sendline(), and related functions, and it defaults to an empty
2676 string which results in the EoL sequence being detected automatically
2679 - @c "" (empty string) = automatically detect on-the-fly (default)
2680 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
2681 - @c \\r (CR) = Carriage Return (typical for MacOS)
2682 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
2685 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
2686 IMAP4, FINGER, NICNAME/WHOIS, etc.).
2687 @returns The same rsocket object so as to facilitate stacking
2696 *///=========================================================================
2698 /// EoL sequence as an std::string object
2699 const std::string eol) noexcept {
2702 }; // -x- rsocket* eol -x-
2704 /*======================================================================*//**
2706 Configure EoL adoption policy for the @ref recvline() and @ref recv_rline()
2707 methods. By default, @ref rsocket is configured with the EoL adoption policy
2708 enabled alongside an empty @ref eol() sequence, which results in the default
2709 operation being that the EoL sequence automatically gets detected and updated
2710 internally upon the first use of either the @ref recvline() or
2711 @ref recv_rline method.
2713 The EoL adoption policy is only effective when the @ref eol() sequence is not
2714 defined (which is indicated by an empty EoL sequence string).
2716 The EoL sequence is updated only when the EoL sequence string is empty, and
2717 when this EoL adoption policy is enabled.
2718 @returns The same rsocket object so as to facilitate stacking
2721 @see is_eol_adoption
2727 *///=========================================================================
2728 rsocket* eol_adoption(
2729 /// TRUE = enable EoL adoption (default)@n
2730 /// FALSE = disable EoL adoption
2731 const bool flag) noexcept {
2732 __eol_fix_printf = flag;
2734 }; // -x- rsocket* eol_adoption -x-
2736 /*======================================================================*//**
2738 Returns a String containing the EoL character sequence that was consumed by
2739 the most recent successful call to the @ref recvline() or @ref recv_rline
2740 method ("successful" in this context means that the received line was
2741 terminated by a valid EoL character sequence; otherwise the
2742 previous/unmodified value is returned).
2744 This method must not be used to determine whether the @ref recvline() method
2745 successfully consumed an EoL character sequence. The reason for this is
2746 that @ref recvline() doesn't update this string when it doesn't consume an
2747 EoL character sequence, hence interpreting its results can (and likely will)
2748 lead to false positives.
2749 @returns EoL character sequence
2755 *///=========================================================================
2756 const std::string eol_consumed_seq() noexcept { return __eol_consumed_seq; }; // -x- std::string eol_consumed_seq -x-
2758 /*======================================================================*//**
2760 Configure EoL substitution policy for the @ref printf(), @ref printfline(),
2761 @ref vprintf(), and @ref vprintfline() methods. By default, @ref rsocket
2762 substitutes printf's @c \\n sequence with the EoL sequence (if defined), but
2763 this method can be used to disable this behaviour.
2765 The @c \\n sequence used in the printf format string normally coincides with
2766 the local Operating System's newline standard, which is very likely different
2767 from the @ref rsocket endpoint's newline standard, and/or the newline
2768 standard of the protocol being implemented. This policy setting makes it
2769 possible to control whether to use the configured EoL sequence when sending a
2770 formatted string to the endpoint.
2771 @returns The same rsocket object so as to facilitate stacking
2773 @see is_eol_fix_printf
2779 *///=========================================================================
2780 rsocket* eol_fix_printf(
2781 /// TRUE = enable EoL substitution (default)@n
2782 /// FALSE = disable EoL substitution
2783 const bool flag) noexcept {
2784 __eol_fix_printf = flag;
2786 }; // -x- rsocket* eol_fix_printf -x-
2788 /*======================================================================*//**
2790 Finds the first instance of the EoL sequence and returns its offset (which is
2791 effectively the same as the size of the text, not including the characters
2792 that the EoL sequence is comprised of).
2795 This method is specialized primarily for internal use by the @ref recvline()
2796 and @ref recv_rline() methods, but is made available here in case there's a
2797 need to check in-memory text using this rsocket's EoL detection policy.
2798 @returns Size of EoL sequence
2799 @returns -1 if EoL sequence wasn't found
2803 *///=========================================================================
2804 const int eol_index(
2805 /// Buffer that probably contains at least one EoL sequence
2806 const std::string buffer,
2807 /// Size of string with EoL character sequence included (will be updated by this method)
2808 int* with_eol_size) noexcept {
2810 // --------------------------------------------------------------------------
2811 // An EoL character sequence was specified, a simple search will suffice.
2812 // --------------------------------------------------------------------------
2813 if (!__eol.empty()) {
2814//std::cout << "!__eol.empty() ------------------------------ String is not empty" << std::endl;
2815 int pos = buffer.find(__eol);
2816 if (pos >= 0) *with_eol_size = pos + __eol.size(); // Update size of EoL sequence
2817//std::cout << "------1------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2819 } // -x- if !__eol.empty() -x-
2820//std::cout << "__eol.empty() ------------------------------ String is empty" << std::endl;
2822 // --------------------------------------------------------------------------
2823 // Automatic detection of EoL sequence, so a more flexible approach will be
2826 // Search for all four possible EoL sequences (as indicated by an empty EoL
2827 // character sequence string):
2829 // CRLF: Common for text lines with internet protocols such as SMTP, POP3,
2830 // IMAP4, HTTP, FINGER, WHOIS, etc. Also: DOS and OS/2 EoL sequence.
2832 // CR: MacOS EoL character.
2834 // LF: UNIX/Linux EoL character.
2836 // LFCR: Extremely rare, but I've encountered multiple instances where the
2837 // intended CRLF was reversed to LFCR, possibly due to a programming
2838 // error or an artifact of inappropriate/unintended endian conversion.
2839 // --------------------------------------------------------------------------
2840 int pos = buffer.find(__CR); // CR or CRLF
2841 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)
2842 *with_eol_size = pos + 1;
2843// *with_eol_size = 1;
2844//std::cout << "------2------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2846 } else if (pos >= 0) { // EoL sequence found
2847 *with_eol_size = pos + (buffer[pos + 1] == __LF ? 2 : 1); // 2=CRLF, 1=CR
2848// *with_eol_size = buffer[pos + 1] == __LF ? 2 : 1; // 2=CRLF, 1=CR
2849 if (__eol_adoption) {
2850 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
2851 __eol.resize(*with_eol_size - pos); // Truncate extra characters
2852 } // -x- if __eol_adoption -x-
2853//std::cout << "------3------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2854 return pos; // Either way, we're done
2857 pos = buffer.find(__LF); // LF or LFCR
2858 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)
2859 *with_eol_size = pos + 1;
2860// *with_eol_size = 1;
2861//std::cout << "------4------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2863 } else if (pos >= 0) { // EoL sequence found
2864 *with_eol_size = pos + (buffer[pos + 1] == __CR ? 2 : 1); // 2=LFCR, 1=LF
2865// *with_eol_size = buffer[pos + 1] == __CR ? 2 : 1; // 2=LFCR, 1=LF
2866 if (__eol_adoption) {
2867 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
2868 __eol.resize(*with_eol_size - pos); // Truncate extra characters
2869 } // -x- if __eol_adoption -x-
2870//std::cout << "------5------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2871 return pos; // Either way, we're done
2874//std::cout << "------6------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2876 }; // -x- int eol_index -x-
2878 /*======================================================================*//**
2880 Find out if the stream is at its end and that this @ref rsocket's internal
2881 buffer (if one had been set up by the @ref recvline() method) is empty. This
2882 doesn't necessarily mean that the stream is closed; but rather that the
2883 endpoint just hasn't sent any more data (yet).
2885 If the stream isn't open, then this method will always return @c true to
2886 implicitly indicate that there's no more data.
2889 For this method to work properly, you need to specify a timeout either with
2890 the timeout parameter, or by setting the @ref timeout() for this rsocket (you
2891 can still override this rsocket's timeout by specifying a timeout here).
2893 It's better (more efficient) to use this method instead of the @c MSG_PEEK
2894 flag (with the various @c recv methods) to determine whether any data is
2895 waiting on the stream (e.g., data that's received by the sockets, but not by
2896 any @c recv methods yet) because this method is specialized in handling this
2897 particular condition and responds with an easy-to-use boolean flag.
2900 EoS is an acronym for: End of Stream
2902 @throws randolf::rex::xEBADF The underlying socket is not open
2903 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2904 part of the user address space
2905 @throws randolf::rex::xEINTR Interrupted by a signal
2906 @throws randolf::rex::xENOMEM Insufficient memory
2907 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2908 doesn't refer to a socket
2909 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
2910 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
2911 is a highly improbable chance that a timeout could still occur if the
2912 data is read by another thread before the `recv(..., MSG_PEEK)` call)
2913 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
2916 @returns TRUE = End of Stream (no data, or the stream @ref is_closed())
2917 @returns FALSE = More data is ready to be received
2921 *///=========================================================================
2922// TODO: Include exceptions from recv() in documentation (above) method (once they're ready)
2924 /// Number of milliseconds to wait
2925 const int timeout = 0) {
2927 // --------------------------------------------------------------------------
2928 // Check internal buffer first.
2929 // --------------------------------------------------------------------------
2930 if (__buffer != nullptr && __buffer_head != __buffer_tail) return false;
2932 // --------------------------------------------------------------------------
2933 // Check for data that's not stored in internal buffers.
2934 // --------------------------------------------------------------------------
2935 if (__socket_open) {
2937 // --------------------------------------------------------------------------
2938 // Timeout polling (in case a timeout was specified).
2939 // --------------------------------------------------------------------------
2942 poll(POLLIN, timeout);
2944 std::shared_ptr<timeval> tv = getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO);
2945 ppoll(POLLIN, tv->tv_sec, tv->tv_usec * 1000); // 1 microsecond = 1,000 nanoseconds
2947 } catch (const randolf::rex::xETIMEDOUT e) {
2949 } // This is a specialized catch situation -- the ::recv function could still return an ETIMEDOUT error
2951 // --------------------------------------------------------------------------
2952 // Non-blocking socket will return -1 and set errno == EAGAIN or EWOULDBLOCK
2953 // if there is simply no new data. In case of graceful connection shutdown
2954 // by the remote peer, recv() will return 0.
2955 // --------------------------------------------------------------------------
2956 char z; // Temporary throw-away variable
2957 if (::recv(__socket_fd, &z, 1, MSG_PEEK | MSG_DONTWAIT) == 0) return true; // No more data; this is EoS
2959 } // -x- if __socket_open -x-
2961 return !__socket_open; // Effectively, this is: return closed();
2962 }; // -x- bool eos -x-
2964 /*======================================================================*//**
2966 Find out what the specified IP address string's family (@c SO_DOMAIN) is.
2967 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
2968 address is not available on any local interface
2969 @throws randolf::rex::xEAI_NONAME If @c address is an empty string
2970 @returns @c AF_UNSPEC (if family couldn't be determined)
2971 @returns @c AF_INET (IPv4 address)
2972 @returns @c AF_INET6 (IPv6 address)
2973 @returns @c AF_UNIX (UNIX Domain address)
2974 @returns ...or other family as applicable
2976 *///=========================================================================
2978 /// Address, similar to @ref bind() addressing, including non-standard "if="
2979 /// variant that names a network interface
2980 const std::string address,
2981 /// Preferred family to return first (used only with interface mode where the
2982 /// network interface is specified after the "if=" prefix); the default value
2983 /// of @c AF_UNSPEC will return the first family interface found
2984 const int preferred_family = AF_UNSPEC) {
2986 // --------------------------------------------------------------------------
2987 // Simple checks first.
2988 // --------------------------------------------------------------------------
2989 if (address.size() == 0) randolf::rex::mk_exception("", EAI_NONAME);
2990 if (address.front() == '/') return AF_UNIX;
2992 // --------------------------------------------------------------------------
2993 // if=<interface-name>: Same "interface" option that we support in bind()
2994 // --------------------------------------------------------------------------
2995 if (address.starts_with("if=")) {
2996 //std::cout << "address=" << address.substr(3).c_str() << std::endl; // Debug
2997 struct ifaddrs *ifaddr; // Chained interface addresses structure
2998 if (::getifaddrs(&ifaddr) == -1) { // Error occurred, so we're done here
2999 freeifaddrs(ifaddr); // Memory management
3000 randolf::rex::mk_exception("getifaddrs() error (prequisite to searching for " + address + ")", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
3001 } // -x- if getifaddrs -x
3003 // --------------------------------------------------------------------------
3004 // Iterate through interface addresses. Each address should have a different
3005 // address family/domain, and is never AF_UNSPEC (this should never happen
3006 // because it wouldn't make sense, but if the network implementation was
3007 // broken and including an AF_UNSPEC family/domain, it would be useless for
3008 // the purposes of calling the ::socket() function so our code will just
3009 // ignore it anyway.
3010 // --------------------------------------------------------------------------
3011 int first_family = AF_UNSPEC; // We're using AF_UNSPEC (0) to indicate that there isn't a family/domain
3012 for (struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
3013 if (ifa->ifa_addr == nullptr) continue; // No address structure, so this isn't useful to us at all
3014 if (ifa->ifa_addr->sa_family == AF_UNSPEC) continue; // Skip AF_UNSPEC family/domain, as we can't use this
3015 if (address.compare(3, -1, ifa->ifa_name) != 0) continue; // Not the interface name we're looking for
3017 // --------------------------------------------------------------------------
3018 // Return current address's family if preferred_family is not specified (if
3019 // it's set to AF_UNSPEC {0}), or if it matches the current address.
3020 // --------------------------------------------------------------------------
3021 //std::cout << "if=" << ifa->ifa_name << " family=" << ifa->ifa_addr->sa_family << std::endl; // Debug
3022 if (preferred_family == AF_UNSPEC || preferred_family == ifa->ifa_addr->sa_family) {
3023 first_family = ifa->ifa_addr->sa_family; // Arbitrarily re-using first_family variable
3024 freeifaddrs(ifaddr); // Memory management
3025 return first_family; // Return current family/domain (re-used first_family variable)
3026 } // -x- if preferred_family -x-
3028 // --------------------------------------------------------------------------
3029 // Keep track of only the first_family/domain that we found; if we can't find
3030 // the preferred_family, then we'll be able to return the first one we found.
3031 // --------------------------------------------------------------------------
3032 if (first_family == AF_UNSPEC && ifa->ifa_addr->sa_family != AF_UNSPEC) first_family = ifa->ifa_addr->sa_family;
3033 //std::cout << " first_family=" << first_family << std::endl; // Debug
3034 } // -x- for *ifa -x-
3036 // --------------------------------------------------------------------------
3037 // Return first_family/domain, if there was one; otherwise, throw exception.
3038 // --------------------------------------------------------------------------
3039 freeifaddrs(ifaddr); // Memory management
3040 if (first_family != AF_UNSPEC) return first_family;
3041 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);
3042 } // -x- if /^if=/ -x-
3044 // --------------------------------------------------------------------------
3045 // Attempt to detect IPv4 or IPv6 using a simple shortcut with ::inet_pton(),
3046 // which requires some additional memory allocation (that the simple checks
3047 // from earlier don't need).
3048 // --------------------------------------------------------------------------
3049 char buf[sizeof(struct in6_addr)]; // Binary address storage
3050 if (::inet_pton(AF_INET, address.c_str(), buf) == 1) return AF_INET;
3051 if (::inet_pton(AF_INET6, address.c_str(), buf) == 1) return AF_INET6;
3053 // --------------------------------------------------------------------------
3054 // Throw xEAI_FAMILY exception because no family/domain was found.
3055 // --------------------------------------------------------------------------
3056 randolf::rex::mk_exception("No family/domain available for: " + address, EAI_FAMILY);
3057 }; // -x- int family -x-
3059 /*======================================================================*//**
3061 Get peer name returns the address of the socket as a sockaddr_storage
3064 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
3065 that aids in the prevention of resource leaks).
3067 @throws randolf::rex::xEBADF The underlying socket is not open
3068 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3069 part of the user address space
3070 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3071 valid for this socket's family (a.k.a., communication domain)
3072 @throws randolf::rex::xENOBUFS Insufficient memory
3073 @throws randolf::rex::xENOTCONN Underlying socket is not connected
3074 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3075 doesn't refer to a socket
3077 @returns sockaddr_storage structure
3080 *///=========================================================================
3081 const std::shared_ptr<sockaddr_storage> getpeername() {
3082 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3083 socklen_t len = sizeof(sockaddr_storage);
3084 __rc_check(::getpeername(__socket_fd, (struct sockaddr*)sa.get(), &len));
3086 }; // -x- std::shared_ptr<sockaddr_storage> getpeername -x-
3088 /*======================================================================*//**
3090 Get peer name returns the address of the socket as a std::string object.
3092 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3093 (e.g., because the @c family doesn't utilize or support an address
3094 {or the format isn't known}
3095 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3096 @throws randolf::rex::xEBADF The underlying socket is not open
3097 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3098 part of the user address space
3099 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3100 valid for this socket's family (a.k.a., communication domain)
3101 @throws randolf::rex::xENOBUFS Insufficient memory
3102 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3103 @throws randolf::rex::xENOTCONN Underlying socket is not connected
3104 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3105 doesn't refer to a socket
3107 @returns string representation of peer name
3109 *///=========================================================================
3110 const std::string getpeername_ntop() {
3111 sockaddr_storage sa;
3112 socklen_t len = sizeof(sockaddr_storage);
3113 __rc_check(::getpeername(__socket_fd, (sockaddr*)&sa, &len));
3114 return inet_ntop(&sa);
3115 }; // -x- std::string getpeername_ntop -x-
3117 /*======================================================================*//**
3119 Get specified "sockaddr_storage" structure's address as a "sockaddr"
3120 structure, for sockets in one of the supported families:
3124 - AF_UNIX (Domain socket path)
3125 - AF_PACKET (Ethernet node/mac. address)
3127 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3128 (e.g., because the @c family doesn't utilize or support an address
3129 {or the format isn't known}
3131 @returns pointer to sockaddr structure within provided sockaddr_storage
3133 @see mk_sockaddr_storage
3139 *///=========================================================================
3140 static sockaddr* getsockaddr(
3141 /// The @c sockaddr_storage structure wherein @c sockaddr will be referenced from
3142 const sockaddr_storage* sa) {
3143 switch (sa->ss_family) {
3144 case AF_INET: // IPv4 address
3145 return (sockaddr*)&(((struct sockaddr_in*)sa)->sin_addr);//->sin_addr);
3146 case AF_INET6: // IPv6 address
3147 return (sockaddr*)&(((struct sockaddr_in6*)sa)->sin6_addr);//->sin6_addr);
3148 case AF_UNIX: // UNIX (path) domain socket
3149 return (sockaddr*)&(((struct sockaddr_un*)sa)->sun_path);//->sun_path);
3150 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
3151 // case /*AF_LINK* /18: // Link layer interface (arp)
3153 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3154 return (sockaddr*)&(((struct sockaddr_ll*)sa)->sll_addr);//->sll_addr);
3155 } // -x- switch family -x-
3156 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY"); // Everything else
3157 }; // -x- sockaddr* getsockaddr -x-
3159 /*======================================================================*//**
3161 Get socket name returns the address of the socket as a "sockaddr_storage"
3164 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
3165 that aids in the prevention of resource leaks).
3167 @throws randolf::rex::xEBADF The underlying socket is not open
3168 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3169 part of the user address space
3170 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3171 valid for this socket's family (a.k.a., communication domain)
3172 @throws randolf::rex::xENOBUFS Insufficient memory
3173 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3174 doesn't refer to a socket
3176 @returns sockaddr_storage structure
3179 *///=========================================================================
3180 const std::shared_ptr<sockaddr_storage> getsockname() {
3181 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3182 socklen_t len = sizeof(sockaddr_storage);
3183 __rc_check(::getsockname(__socket_fd, (struct sockaddr*)sa.get(), &len));
3185 }; // -x- std::string getsockname -x-
3187 /*======================================================================*//**
3189 Get socket name returns the name of the socket as a std::string object.
3191 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3192 (e.g., because the @c family doesn't utilize or support an address
3193 {or the format isn't known}
3194 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3195 @throws randolf::rex::xEBADF The underlying socket is not open
3196 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3197 part of the user address space
3198 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3199 valid for this socket's family (a.k.a., communication domain)
3200 @throws randolf::rex::xENOBUFS Insufficient memory
3201 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3202 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3203 doesn't refer to a socket
3205 @returns string representation of socket name
3207 *///=========================================================================
3208 const std::string getsockname_ntop() {
3209 sockaddr_storage sa;
3210 socklen_t len = sizeof(sockaddr_storage);
3211 __rc_check(::getsockname(__socket_fd, (sockaddr*)&sa, &len));
3212 return inet_ntop(&sa);
3213 }; // -x- std::string getsockname_ntop -x-
3215 /*======================================================================*//**
3217 Get socket option details in the form of an integer.
3219 Most options return an integer, with the remaining options returning a
3220 pointer to a structure wrapped in a std::shared_ptr (a C++ smart pointer that
3221 aids in the prevention of resource leaks); the primitive types int, u_int,
3222 and u_char are not wrapped in C++ smart pointers because returning them by
3223 value is more efficient since allocating memory for an entire structure isn't
3227 It is up to the developer to know which return type is needed according to
3228 the socket option, otherwise an exception will likely be thrown -- in some
3229 cases where the wrong type will seem to work, this is due to the wrong type
3230 providing a minimally sufficient amount of memory for the storage of the
3231 resulting structure.
3234 The returned values/structures are not marked as "const" because they may
3235 need to be modified for unforseen purposes. Modifying the returend values or
3236 structures is fine because they are intended to be independent and are
3237 expected to have no direct impact on the rsocket's internal variables and
3240 Templates in C++ aren't used here because they don't work properly for our
3241 needs due to neccesity to handle both fundamental types and structures; it
3242 turns out that mixing these is impossible when using the same function name,
3243 so this just doesn't work as well as we'd like it to. (We may try to work on
3244 this again in the future as time permits to provide an additional method for
3245 obtaining socket options, but with the intention of never removing this
3246 current set of methods so as to ensure backward compatibility in the future.)
3248 @throws randolf::rex::xEBADF The underlying socket is not open
3249 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3250 part of the user address space
3251 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3252 valid for this socket's family (a.k.a., communication domain)
3253 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
3255 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3256 doesn't refer to a socket
3258 @returns socket option value
3260 *///=========================================================================
3262 /// The level at which the option resides; typically @c SOL_SOCKET
3264 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3267 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3268 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3270 }; // -x- int getsockopt_int -x-
3272 /*======================================================================*//**
3274 Get socket option details in the form of an unsigned integer.
3275 @copydetails getsockopt_int(const int, const int)
3276 @returns socket option value
3278 *///=========================================================================
3279 u_int getsockopt_u_int(
3280 /// The level at which the option resides; typically @c SOL_SOCKET
3282 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3285 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3286 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3288 }; // -x- u_int getsockopt_u_int -x-
3290 /*======================================================================*//**
3292 Get socket option details in the form of an unsigned character.
3293 @copydetails getsockopt_int(const int, const int)
3294 @returns socket option value
3296 *///=========================================================================
3297 u_char getsockopt_u_char(
3298 /// The level at which the option resides; typically @c SOL_SOCKET
3300 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3303 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3304 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3306 }; // -x- u_char getsockopt_u_char -x-
3308 /*======================================================================*//**
3310 Get socket option details in the form of a structure.
3311 @copydetails getsockopt_int(const int, const int);
3312 @returns socket option structure wrapped in std::shared_ptr
3314 *///=========================================================================
3315 std::shared_ptr<linger> getsockopt_linger(
3316 /// The level at which the option resides; typically @c SOL_SOCKET
3318 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3320 std::shared_ptr value = std::make_shared<linger>();
3321 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3322 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3324 }; // -x- linger getsockopt_linger -x-
3326 /*======================================================================*//**
3327 @copydoc getsockopt_linger(const int, const int)
3329 *///=========================================================================
3330 std::shared_ptr<timeval> getsockopt_timeval(
3331 /// The level at which the option resides; typically @c SOL_SOCKET
3333 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3335 std::shared_ptr value = std::make_shared<timeval>();
3336 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3337 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3339 }; // -x- timeval getsockopt_timeval -x-
3341 /*======================================================================*//**
3342 @copydoc getsockopt_linger(const int, const int)
3344 *///=========================================================================
3345 std::shared_ptr<in_addr> getsockopt_in_addr(
3346 /// The level at which the option resides; typically @c SOL_SOCKET
3348 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3350 std::shared_ptr value = std::make_shared<in_addr>();
3351 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3352 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3354 }; // -x- in_addr getsockopt_in_addr -x-
3356 /*======================================================================*//**
3357 @copydoc getsockopt_linger(const int, const int)
3359 *///=========================================================================
3360 std::shared_ptr<ip_mreq> getsockopt_ip_mreq(
3361 /// The level at which the option resides; typically @c SOL_SOCKET
3363 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3365 std::shared_ptr value = std::make_shared<ip_mreq>();
3366 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3367 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3369 }; // -x- ip_mreq getsockopt_ip_mreq -x-
3371 /*======================================================================*//**
3372 @copydoc getsockopt_linger(const int, const int)
3374 *///=========================================================================
3375 std::shared_ptr<ip_mreq_source> getsockopt_ip_mreq_source(
3376 /// The level at which the option resides; typically @c SOL_SOCKET
3378 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3380 std::shared_ptr value = std::make_shared<ip_mreq_source>();
3381 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3382 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3384 }; // -x- ip_mreq_source getsockopt_ip_mreq_source -x-
3386 /*======================================================================*//**
3387 @copydoc getsockopt_linger(const int, const int)
3389 *///=========================================================================
3390 std::shared_ptr<icmp6_filter> getsockopt_icmp6_filter(
3391 /// The level at which the option resides; typically @c SOL_SOCKET
3393 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3395 std::shared_ptr value = std::make_shared<icmp6_filter>();
3396 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3397 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3399 }; // -x- icmp6_filter getsockopt_icmp6_filter -x-
3401 /*======================================================================*//**
3402 @copydoc getsockopt_linger(const int, const int)
3404 *///=========================================================================
3405 std::shared_ptr<sockaddr_in6> getsockopt_sockaddr_in6(
3406 /// The level at which the option resides; typically @c SOL_SOCKET
3408 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3410 std::shared_ptr value = std::make_shared<sockaddr_in6>();
3411 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3412 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3414 }; // -x- sockaddr_in6 getsockopt_sockaddr_in6 -x-
3416 /*======================================================================*//**
3417 @copydoc getsockopt_linger(const int, const int)
3419 *///=========================================================================
3420 std::shared_ptr<ip6_mtuinfo> getsockopt_ip6_mtuinfo(
3421 /// The level at which the option resides; typically @c SOL_SOCKET
3423 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3425 std::shared_ptr value = std::make_shared<ip6_mtuinfo>();
3426 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3427 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3429 }; // -x- ip6_mtuinfo getsockopt_ip6_mtuinfo -x-
3431 /*======================================================================*//**
3432 @copydoc getsockopt_linger(const int, const int)
3434 *///=========================================================================
3435 std::shared_ptr<ipv6_mreq> getsockopt_ipv6_mreq(
3436 /// The level at which the option resides; typically @c SOL_SOCKET
3438 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3440 std::shared_ptr value = std::make_shared<ipv6_mreq>();
3441 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3442 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3444 }; // -x- ipv6_mreq getsockopt_ipv6_mreq -x-
3446 /*======================================================================*//**
3447 @copydoc getsockopt_linger(const int, const int)
3449 *///=========================================================================
3450 std::shared_ptr<group_req> getsockopt_group_req(
3451 /// The level at which the option resides; typically @c SOL_SOCKET
3453 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3455 std::shared_ptr value = std::make_shared<group_req>();
3456 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3457 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3459 }; // -x- group_req getsockopt_group_req -x-
3461 /*======================================================================*//**
3462 @copydoc getsockopt_linger(const int, const int)
3464 *///=========================================================================
3465 std::shared_ptr<group_source_req> getsockopt_group_source_req(
3466 /// The level at which the option resides; typically @c SOL_SOCKET
3468 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3470 std::shared_ptr value = std::make_shared<group_source_req>();
3471 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3472 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3474 }; // -x- group_source_req getsockopt_group_source_req -x-
3476 /*======================================================================*//**
3477 @copydoc getsockopt_linger(const int, const int)
3479 *///=========================================================================
3480 template<class T> std::shared_ptr<T> getsockopt_other(
3481 /// The level at which the option resides; typically @c SOL_SOCKET
3483 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3485 std::shared_ptr value = std::make_shared<T>();
3486 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3487 if (__debug) __debug_sockopt_other("getsockopt_other(socket{0x", level, option, socklen_t(sizeof(value)));
3489 }; // -x- T getsockopt_other -x-
3491 /*======================================================================*//**
3493 Get underlying socket's address as a std::string, for sockets in one of the
3498 - AF_UNIX (Domain socket path)
3499 - AF_PACKET (Ethernet node/mac. address)
3501 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3502 (e.g., because the @c family doesn't utilize or support an address
3503 {or the format isn't known}
3504 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3505 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3507 @returns string representation of underlying socket's address
3508 @see inet_ntop(sockaddr_storage*) static method
3511 *///=========================================================================
3512 const std::string inet_ntop() { return inet_ntop((sockaddr_storage*)&__socket_addr); } // -x- std::string inet_ntop -x-
3514 /*======================================================================*//**
3516 Get specified "sockaddr_storage" structure's address as a std::string, for
3517 sockets in one of the supported families:
3521 - AF_UNIX (Domain socket path)
3522 - AF_PACKET (Ethernet node/mac. address)
3524 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3525 (e.g., because the @c family doesn't utilize or support an address
3526 {or the format isn't known}
3527 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3528 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3530 @returns string representation of underlying socket's address
3531 @see inet_ntop() non-static method
3534 *///=========================================================================
3535 static const std::string inet_ntop(
3536 /// Source structure that [should] contain address data
3537 sockaddr_storage* sa) {
3539 switch (sa->ss_family) {
3540 case AF_INET: // IPv4 address
3542 char ntop[sizeof(sockaddr_in)]{0};
3543 const char* rc = ::inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), ntop, sizeof(ntop));
3544 str = std::string(ntop);
3547 case AF_INET6: // IPv6 address
3549 char ntop[sizeof(sockaddr_in6)]{0};
3550 const char* rc = ::inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), ntop, sizeof(ntop));
3551 str = std::string(ntop);
3554 case AF_UNIX: // UNIX (path) domain socket
3555 str = std::string(((struct sockaddr_un *)sa)->sun_path);
3557 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
3558 // case /*AF_LINK* /18: // Link layer interface (arp)
3560 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3561 str = to_mac(((struct sockaddr_ll *)sa)->sll_addr);
3563 default: // Everything else
3564 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY");
3565 } // -x- switch family -x-
3567 }; // -x- std::string inet_ntop -x-
3569 /*======================================================================*//**
3571 Find out whether an internal read buffer was allocated (this is most likely
3572 triggered by an attempt to read a line of text).
3574 The buffer_size() methods report on how much memory was allocated for the
3575 internal read buffer or to set its size (in bytes).
3576 @returns TRUE = an internal read buffer was allocated
3577 @returns FALSE = an internal read buffer was not allocated
3579 @see buffer_size(const size_t nbytes)
3581 *///=========================================================================
3582 const bool is_buffered() noexcept { return __buffer != nullptr; }; // -x- bool is_buffered -x-
3584 /*======================================================================*//**
3586 Find out whether the underlying socket is not open (which may not be the same
3587 as specifically "closed" since a newly instantiated empty socket begins in a
3588 "not open" state despite the underlying socket not explicitly having been
3590 @returns TRUE = not open
3591 @returns FALSE = open
3594 *///=========================================================================
3595 const bool is_closed() noexcept { return !__socket_open; }; // -x- bool is_closed -x-
3597 /*======================================================================*//**
3599 Find out whether the underlying socket is connected with/to an endpoint.
3600 @returns TRUE = open
3601 @returns FALSE = not open
3603 *///=========================================================================
3604 const bool is_connected() noexcept { return __socket_connected; }; // -x- bool is_connected -x-
3606 /*======================================================================*//**
3608 Find out whether the default byte order for this host is LSB (small endian).
3610 If you're trying to choose which endian type to use when designing a new
3611 internet protocol, then big endian is normally the better option. However,
3612 if your new protocol will only be used by hardware that all share the same
3613 endianness, then that endianness is probably the more optimal option since it
3614 will translate to an overall lesser consumption of CPU cycles by reducing or
3615 eliminating endianness conversions.
3616 @returns TRUE = LSB (little endian)
3617 @returns FALSE = MSB (big endian / network byte order)
3619 *///=========================================================================
3620 const bool is_endian_lsb() noexcept { return !__endian_is_msb; }; // -x- bool is_endian_lsb -x-
3622 /*======================================================================*//**
3624 Find out whether the default byte order for this host is MSB (big endian).
3626 Big endian is the standard known as "network byte order" that's also used in
3627 various header fields in internet packets.
3629 If you're trying to choose which endian type to use when designing a new
3630 internet protocol, then big endian is normally the better option. However,
3631 if your new protocol will only be used by hardware that all share the same
3632 endianness, then that endianness is probably the more optimal option since it
3633 will translate to an overall lesser consumption of CPU cycles by reducing or
3634 eliminating endianness conversions.
3635 @returns TRUE = MSB (big endian / network byte order)
3636 @returns FALSE = LSB (little endian)
3638 *///=========================================================================
3639 const bool is_endian_msb() noexcept { return __endian_is_msb; }; // -x- bool is_endian_msb -x-
3641 /*======================================================================*//**
3643 Find out if the EoL adoption policy is enabled for the @ref recvline() method
3644 (see the @ref eol_adoption method to find out how the dynamically-detected
3645 EoL sequence gets adopted, and under what conditions).
3646 @returns TRUE = EoL adoption is enabled
3647 @returns FALSE = EoL adoption is disabled
3655 *///=========================================================================
3656 const bool is_eol_adoption() noexcept {
3657 return __eol_adoption;
3658 }; // -x- bool is_eol_adoption -x-
3660 /*======================================================================*//**
3662 Find out if the EoL substitution policy is enabled for the @ref printf(),
3663 @ref printfline(), @ref vprintf(), and @ref vprintfline() methods.
3664 @returns TRUE = EoL substitution is enabled
3665 @returns FALSE = EoL substitution is disabled
3675 *///=========================================================================
3676 const bool is_eol_fix_printf() noexcept {
3677 return __eol_fix_printf;
3678 }; // -x- bool is_eol_fix_printf -x-
3680 /*======================================================================*//**
3682 Find out whether the underlying socket is open.
3683 @returns TRUE = open
3684 @returns FALSE = not open
3687 *///=========================================================================
3688 const bool is_open() noexcept { return __socket_open; }; // -x- bool is_open -x-
3690 /*======================================================================*//**
3692 Find out whether encrypted communications is enabled or disabled.
3693 @returns TRUE = encrypted communications is enabled
3694 @returns FALSE = encrypted communications is disabled
3695 @see tls(bool, TLS_FLAGS)
3697 *///=========================================================================
3698 const bool is_tls() noexcept { return __tls; }; // -x- bool is_tls -x-
3700 /*======================================================================*//**
3702 Find out whether TLS context is in TLS_CLIENT mode.
3703 @returns TRUE = TLS context is in TLS_CLIENT mode
3704 @returns FALSE = TLS context is in TLS_SERVER mode
3708 *///=========================================================================
3709 const bool is_tls_client_mode() noexcept { return !__tls_server_mode; }; // -x- bool is_tls_client_mode -x-
3711 /*======================================================================*//**
3713 Find out whether egress from encryption (to unencrypted mode) is allowed.
3714 @returns TRUE = egress from encrypted communications is allowed
3715 @returns FALSE = egress from encrypted communications is not allowed
3719 *///=========================================================================
3720 const bool is_tls_egress_okay() noexcept { return __tls_egress; }; // -x- bool is_tls_egress_okay -x-
3722 /*======================================================================*//**
3724 Find out whether encrypted communications is exclusive.
3725 @returns TRUE = encrypted communications is exclusive
3726 @returns FALSE = encrypted communications is not exclusive
3729 *///=========================================================================
3730 const bool is_tls_exclusive() noexcept { return __tls_exclusive; }; // -x- bool is_tls_exclusive -x-
3732 /*======================================================================*//**
3734 Find out whether ingress to encryption (from unencrypted mode) is allowed.
3735 @returns TRUE = ingress to encrypted communications is allowed
3736 @returns FALSE = ingress to encrypted communications is not allowed
3740 *///=========================================================================
3741 const bool is_tls_ingress_okay() noexcept { return __tls_ingress; }; // -x- bool is_tls_ingress_okay -x-
3743 /*======================================================================*//**
3745 Find out whether TLS context is in TLS_SERVER mode.
3746 @returns TRUE = TLS context is in TLS_SERVER mode
3747 @returns FALSE = TLS context is in TLS_CLIENT mode
3751 *///=========================================================================
3752 const bool is_tls_server_mode() noexcept { return __tls_server_mode; }; // -x- bool is_tls_server_mode -x-
3754 /*======================================================================*//**
3756 Find out whether SNI (Server Name Identifier) is enabled (configured, which
3757 implies that an internal callback function was also set up).
3758 @returns TRUE = SNI is enabled
3759 @returns FALSE = SNI is disabled
3762 *///=========================================================================
3763 const bool is_tls_sni() noexcept { return __tls_sni != nullptr; }; // -x- bool is_tls_sni -x-
3765 /*======================================================================*//**
3767 Find out whether SNI (Server Name Identifier) was matched, which means that
3768 we're using one of the supplementary TLS certificates that are included in
3769 the associated @ref rsocket_sni object as separate TLS contexts.
3771 When this method returns @c TRUE, it means the @c OpenSSL callback (which
3772 occurs during the TLS handshake process) completed the TLS handshake with one
3773 of the TLS certificates that's included in this @c rsocket's @ref rsocket_sni
3774 object instead of the primary TLS certificate assigned to this @c rsocket,
3775 and this @c rsocket is using the respective TLS context instead of the
3776 primary (default) one.
3777 @returns TRUE = SNI was matched
3778 @returns FALSE = SNI wasn't matched
3782 *///=========================================================================
3783 const bool is_tls_sni_match() noexcept { return __tls_sni_match; }; // -x- bool is_tls_sni_match -x-
3785 /*======================================================================*//**
3787 Enable listening mode for this rsocket to prepare it to accept() new inbound
3790 The backlog defaults to SOMAXCONN (4096 on Linux, and 128 on older systems),
3791 which is common on most systems. If a higher value is supplied that exceeds
3792 @c kern.somaxconn, the kernel will automatically override the backlog with
3793 the value stored in @c kern.somaxconn without generating any errors or
3796 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
3797 @throws randolf::rex::xEADDRINUSE No ephemeral ports are available for
3798 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
3799 but it really isn't)
3800 @throws randolf::rex::xEBADF The underlying socket is not open
3801 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3802 doesn't refer to a socket
3803 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
3804 @returns The same rsocket object so as to facilitate stacking
3813 *///=========================================================================
3815 /// Backlog queue size (0 = uses rsocket's default; see @ref backlog for more
3816 /// details about this); specifying a non-zero backlog also updates rocket's
3817 /// internal default (SOMAXCONN is 4096 on Linux, and 128 on older systems)
3819 if (__debug) debug("listen(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3820 + ", " + std::to_string(backlog)
3823 // --------------------------------------------------------------------------
3824 // Use rsocket's default backlog if not specified, otherwise update it
3825 // with the new value specified here.
3826 // --------------------------------------------------------------------------
3827 if (backlog = 0) backlog = __socket_backlog;
3828 else __socket_backlog = backlog;
3830 // --------------------------------------------------------------------------
3831 // Configure underlying socket to queue up to "backlog" inbound connections.
3832 // --------------------------------------------------------------------------
3833 __rc_check(::listen(__socket_fd, backlog));
3836 }; // -x- rsocket* listen -x-
3838 /*======================================================================*//**
3840 Convert an IPv4 address, IPv6 address, ethernet packet, or UNIX domain
3841 socket to a sockaddr_storage structure.
3843 If service_name is an absolute path (that begins with a "/" charcter) and the
3844 family is set to AF_UNSPEC (the default), then the resulting family will be
3848 This method utilizes the results of getaddrinfo().
3850 Other families like AF_LINK and AF_PACKET should work, but haven't been
3851 tested thoroughly. The additional support we provide for IPv4 and IPv6
3852 addresses is to copy the port number into the resulting structure (as a
3856 The resulting sockaddr_storage structure is wrapped in std::shared_ptr (a C++
3857 smart pointer that aids in the prevention of resource leaks).
3860 This method is thread-safe.
3862 @throws randolf::rex::xEAI_ADDRFAMILY If specified network host doesn't have
3863 any addresses in the specified address family
3864 @throws randolf::rex::xEAI_AGAIN Temporary failure code from DNS server (try
3866 @throws randolf::rex::xEAI_BADFLAGS Invalid flags in @c hints.ai_flags (or
3867 `hints.ai_flags` included @c AI_CANONNAME with @c nullptr as @c name)
3868 @throws randolf::rex::xEAI_FAIL Permanent failure code from DNS server
3869 @throws randolf::rex::xEAI_FAMILY The specified family is not supported
3870 @throws randolf::rex::xEAI_MEMORY Out of memory
3871 @throws randolf::rex::xEAI_NONAME If node_name is nullptr or an empty string
3872 @throws randolf::rex::xEAI_SERVICE The specified service is not available
3873 for the specified socket type
3874 @throws randolf::rex::xEAI_SOCKTYPE The specified socket type is not
3876 @throws randolf::rex::xEAI_SYSTEM Other system error (use errno to determine
3877 what the error is, then run use @ref randolf::rex::rex::mk_exception
3878 to throw the correct exception)
3880 @returns sockaddr_storage structure
3882 *///=========================================================================
3883 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
3884 /// IP address or UNIX domain socket address to convert
3885 const char* node_name,
3886 /// Port number (or service name used by some other families)
3887 const char* service_name = nullptr,
3888 /// Optional pointer to a helpful addrinfo structure
3889 const addrinfo* hints = nullptr) {
3891 // --------------------------------------------------------------------------
3892 // Initial sanity checks.
3893 // --------------------------------------------------------------------------
3894 if (node_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME); // if (node_name == nullptr && service_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME);
3896 // --------------------------------------------------------------------------
3897 // Handle any special cases.
3898 // --------------------------------------------------------------------------
3899 switch (hints == nullptr ? AF_UNSPEC : hints->ai_family) { // Default to AF_UNSPEC in the absence of hints
3901 if (node_name[0] != '/') break;
3902 // Next entry MUST be "case AF_UNIX" for this fall-through to work
3905 // For some unknown reason, clang++ can't compile this line that
3906 // g++ has absolutely no trouble with at all:
3907 // std::shared_ptr sa = std::make_shared<sockaddr_storage>(AF_UNIX);
3908 // So, after wasting more than a year trying to figure out what the
3909 // hundreds of lines of cryptic errors meant, I eventually gave up
3910 // on clang++ for being such a dumbfuck, and used this work-around:
3911 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3912 sa->ss_family = AF_UNIX;
3913 std::strcpy(((struct sockaddr_un *)sa.get())->sun_path, node_name); // Copy path
3917 } // -x- switch hints->family -x-
3919 // --------------------------------------------------------------------------
3920 // Acquire addrinfo[] linked-list array.
3921 // --------------------------------------------------------------------------
3922 addrinfo* __addr_result; // This is temporary
3923//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;
3924 randolf::rex::mk_exception("", getaddrinfo(node_name,
3929 // --------------------------------------------------------------------------
3930 // Find first valid addrinfo[] array by trying to open a socket with it.
3931 // --------------------------------------------------------------------------
3932 for (addrinfo* ar = __addr_result; ar != nullptr; ar = ar->ai_next) {
3933//std::cout << " ... ai_family=" << ar->ai_family << " ai_socktype=" << ar->ai_socktype << " ai_flags=" << ar->ai_flags << std::endl; // *******************
3934// ***************************** TODO: Make sure this loop is working properly
3936//std::cout << " !!! ai_family=" << __addr_result->ai_family << std::endl; // *******************
3938 // --------------------------------------------------------------------------
3939 // Copy the address to "sa" and return it. The "addr" data for IPv4 and IPv6
3940 // addresses include the TCP/UDP port number (service) in first two bytes as
3941 // as an unsigned integer (u_int16), followed immediately by the IP address.
3943 // We're taking the first record only. We assume that hints{} is defined on
3944 // an as-needed basis.
3946 // Note: AF_LINK and AF_PACKET are not widely supported, but we've made an
3947 // effort to accomodate their usage.
3948 // --------------------------------------------------------------------------
3949 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3950 switch (__addr_result->ai_family) {
3951 case AF_INET: // IPv4 address
3952 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_in));
3954 case AF_INET6: // IPv6 address
3955 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_in6));
3957 /* // We're handling this earlier as a special case: TODO: Move special handling to here
3958 case AF_UNIX: // UNIX (path) domain socket
3959 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_un));
3962 case /*AF_LINK*/18: // Link layer interface (arp)
3963 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_dl));
3965 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3966 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_ll));
3968 default: // Everything else
3969 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_storage));
3970 } // -x- switch family -x-
3971 freeaddrinfo(__addr_result); // We don't need this anymore
3973 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
3975 /*======================================================================*//**
3976 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
3977 *///=========================================================================
3978 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
3979 /// IP address or UNIX domain socket address to convert
3980 const char* node_name,
3982 const u_int16_t service_name,
3983 /// Optional pointer to a helpful addrinfo structure
3984 const addrinfo* hints = nullptr) {
3985 std::string port = std::to_string(service_name);
3986 return mk_sockaddr_storage(node_name, port.c_str(), hints);
3987 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
3989 /*======================================================================*//**
3990 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
3991 *///=========================================================================
3992 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
3993 /// IP address or UNIX domain socket address to convert
3994 const std::string node_name,
3996 const u_int16_t service_name,
3997 /// Optional pointer to a helpful addrinfo structure
3998 const addrinfo* hints = nullptr) {
3999 std::string port = std::to_string(service_name);
4000 return mk_sockaddr_storage(node_name.c_str(), port.c_str(), hints);
4001 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
4003 /*======================================================================*//**
4004 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
4005 *///=========================================================================
4006 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
4007 /// IP address or UNIX domain socket address to convert
4008 const std::string node_name,
4009 /// Port number (or server name used by some other families)
4010 const std::string service_name,
4011 /// Optional pointer to a helpful addrinfo structure
4012 const addrinfo* hints = nullptr) {
4013 return mk_sockaddr_storage(node_name.c_str(), service_name.c_str(), hints);
4014 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
4016 /*======================================================================*//**
4018 Specify a name for this rsocket.
4020 This is an arbitrary name that is entirely optional, and should be regarded
4021 as similar to the naming of threads.
4022 @returns The same rsocket object so as to facilitate stacking
4023 *///=========================================================================
4025 /// Name to assign to this @c rsocket
4026 const std::string name) noexcept {
4027 // const std::lock_guard<std::mutex> lock(__name);
4030 }; // -x- rsocket* name -x-
4032 /*======================================================================*//**
4034 Find out what this rsocket's name is.
4036 The built-in SNI mechanism will overwrite this data to indicate the hostname
4037 that was specified by the TLS-encrypted endpoint that triggered an internal
4038 SNI callback -- use the @ref name_sni() method to find out which hostname is
4039 actually being used by TLS.
4040 @returns The name of this rsocket (or an empty @c std::string if this rsocket
4041 doesn't have a name)
4044 *///=========================================================================
4045 std::string name() noexcept {
4046 // const std::lock_guard<std::mutex> lock(__name);
4048 }; // -x- std::string name -x-
4050 /*======================================================================*//**
4052 Find out what this rsocket's actual TLS SNI hostname is.
4054 This is the exact - or closest-matching (in the case of wildcards) - hostname
4055 associated with an actual TLS certificate that was provided in the configured
4056 @ref rsocket_sni object.
4057 @returns The hostname associated with the TLS certificate selected by SNI
4061 *///=========================================================================
4062 std::string name_sni() noexcept {
4064 }; // -x- std::string name_sni -x-
4066 /*======================================================================*//**
4068 Get socket I/O statistics from internally-tracked socket I/O counters.
4070 The number of bytes transmitted and received is tracked internally, so that
4071 the information can be used later in logging. These statistics are available
4072 at all times, but for logging purposes it makes the most sense to copy this
4073 information after the rsocket is closed, at which time the final statistics
4074 will continue to be available until the rsocket's destructor takes over.
4077 This method is threadsafe.
4078 @returns rsocket_io wrapped in a std::shared_ptr object to help ease memory
4084 *///=========================================================================
4085 std::shared_ptr<rsocket_io> net_io() noexcept {
4086 std::shared_ptr stats = std::make_shared<rsocket_io>();
4087 stats->bytes_rx = __bytes_rx;
4088 stats->bytes_tx = __bytes_tx;
4089 stats->crypt_rx = __crypt_rx;
4090 stats->crypt_tx = __crypt_tx;
4091 stats->is_final = false;
4093 }; // -x- std::shared_ptr<rsocket_io> net_io -x-
4095 /*======================================================================*//**
4097 Get socket I/O statistics from internally-tracked socket I/O counters as a
4098 pre-formatted std::string object.
4100 The number of bytes transmitted and received is tracked internally, so that
4101 the information can be used later in logging. These statistics are available
4102 at all times, but for logging purposes it makes the most sense to copy this
4103 information after the rsocket is closed, at which time the final statistics
4104 will continue to be available until the rsocket's destructor takes over.
4107 The format string may contain any characters, with only instances of the
4108 following case-sensitive command sequences to be interpolated accordingly
4109 (invalid commands will be ignored and will remain in place, unmodified):
4111 <table cellpadding=8 cellspacing=0 border=1>
4112 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
4113 <tr><td>$$</td><td>One dollar sign ("$")</td><td>@e N/A</td></tr>
4114 <tr><td>$aR</td><td>Total (all) bytes received</td><td>bytes_rx @c + crypt_rx</td></tr>
4115 <tr><td>$aT</td><td>Total (all) bytes transmitted</td><td>bytes_tx @c + crypt_tx</td></tr>
4116 <tr><td>$bR</td><td>Unencrypted bytes received</td><td>bytes_rx</td></tr>
4117 <tr><td>$bT</td><td>Unencrypted bytes transmitted</td><td>bytes_tx</td></tr>
4118 <tr><td>$cR</td><td>Encrypted bytes recevied</td><td>crypt_rx</td></tr>
4119 <tr><td>$cT</td><td>Encrypted bytes transmitted</td><td>crypt_tx</td></tr>
4120 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
4123 Why do we use dollar signs instead of percent symbols, like other formatting
4124 functions like printf() does? So that the format string won't be in conflict
4125 with any percent-prefixed commands in printf() and similar funcions. This
4126 means that a printf() format string can be put through a first pass here to
4127 get the needed statistics interpolated into the needed positions.
4130 This method is threadsafe.
4131 @returns An interpolated format string as an std::string object.
4136 *///=========================================================================
4140 /// Length of format string (in bytes), or 0 to auto-detect length if format string is an ASCIIZ string
4142 /// Pointer to an @ref rsocket_io structure to read the statistics from (nullptr = use internal copy of current statistics)
4143 // 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)
4144 rsocket_io* addr = nullptr) noexcept {
4146 // --------------------------------------------------------------------------
4147 // Measure size of format string if an ASCIIZ string was indicated.
4148 // --------------------------------------------------------------------------
4149 if (len == 0) len = std::strlen(format);
4151 // --------------------------------------------------------------------------
4152 // Internal variables.
4153 // --------------------------------------------------------------------------
4154 rsocket_io* data = addr == nullptr ? net_io().get() : addr; // Copy current statistics so that
4155 std::string stats; // Formatted result
4156 ulong val; // Value (to be inserted)
4157 char c; // Current character
4158 std::string cmd; // Accumulated command (may be subsituted)
4159 bool flag_commas = false; // Thousands separators flag
4160 bool flag_right = false; // Flush-right flag
4162 // --------------------------------------------------------------------------
4163 // Process format string and build resulting formatted stats string.
4165 // This is designed to be fast, and I'm taking huge shortcuts here since the
4166 // commands are all the same size (except for the first one, which is only
4167 // two dollar sign characters). Processing in this loop should be quite fast
4168 // compared to using library functions to search for dollar sign characters
4169 // since data needs to be copied anyway -- why run a loop over the text twice
4170 // when can get away with doing it faster by doing it only once? This is an
4171 // optimized approach, although it probably could be even faster by not using
4172 // std::string for temporary command storage (there are other ways to do this
4173 // but I think this should suffice for now since it isn't expected to be used
4175 // --------------------------------------------------------------------------
4176 for (int i = 0; i < len; i++) {
4178 // --------------------------------------------------------------------------
4179 // First character (potentially).
4180 // --------------------------------------------------------------------------
4181 if ((c = format[i]) != '$') { // Not a dollar sign -- save it and move on
4185 cmd = c; // Start building up the command string
4187 // --------------------------------------------------------------------------
4188 // Second character: Part 1 of 2
4190 // TODO: Add support for "," commas, and "r" right-alignment.
4191 // --------------------------------------------------------------------------
4192 if (++i == len) continue; // End of format string, so we're done
4193 cmd.push_back(c = format[i]);
4194 if (c == '$') { // Consume the previous dollar sign so that $$ turns into $
4195 stats.push_back(c); // Add $ to stats string (we're only keeping one dollar sign)
4198 flag_commas = flag_right = false; // Reset these flags for a clean start
4200 // --------------------------------------------------------------------------
4202 // , = Include thousands separators (commas)
4203 // r = Flush right (leading spaces will be added)
4204 // --------------------------------------------------------------------------
4207 if (++i == len) continue; // End of format string, so we're done
4208 cmd.push_back(c = format[i]);
4210 goto net_io_flags_loop;
4211 } else if (c == 'r') {
4212 if (++i == len) continue; // End of format string, so we're done
4213 cmd.push_back(c = format[i]);
4215 goto net_io_flags_loop;
4216 } // -x- if [,r] -x-
4219 // --------------------------------------------------------------------------
4220 // Second character: Part 1 of 2
4221 // --------------------------------------------------------------------------
4222 if (c < 'a' || c > 'c') { // Not a, b, or c, so treat it as a regular character
4223 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4225 } // -x- if !~[abc] -x-
4227 // --------------------------------------------------------------------------
4229 // --------------------------------------------------------------------------
4230 if (++i == len) continue; // End of format string, so we're done
4231 cmd.push_back(c = format[i]);
4232 if (c != 'R' && c != 'T') { // Not R or T, so treat it as a regular character
4233 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4235 } // -x- if !~[RT] -x-
4237 // --------------------------------------------------------------------------
4238 // Command processing. If the command is valid, then it will be interpolated
4239 // with its expected result by replacing it with the resulting value.
4240 // --------------------------------------------------------------------------
4241 //std::cout << "[" << cmd << "]"; // Debug
4242 if (cmd.ends_with("aR")) val = data->bytes_rx + data->crypt_rx;
4243 else if (cmd.ends_with("aT")) val = data->bytes_tx + data->crypt_tx;
4244 else if (cmd.ends_with("bR")) val = data->bytes_rx ;
4245 else if (cmd.ends_with("bT")) val = data->bytes_tx ;
4246 else if (cmd.ends_with("cR")) val = data->crypt_rx;
4247 else if (cmd.ends_with("cT")) val = data->crypt_tx;
4248 else { stats.append(cmd); continue; } // This is wrong, so ignore it
4250 // --------------------------------------------------------------------------
4251 // Re-use cmd to generate formatted value.
4252 // --------------------------------------------------------------------------
4254 cmd.resize(21); // Maximum length without commas (plus character zero): 18446744073709551615
4255 cmd.resize(sprintf(cmd.data(), flag_right ? "%20lu" : "%lu", val)); // The ::sprintf function can't use cmd.c_str() here
4256 if (flag_commas) cmd = randolf::rtools::insert_commas(cmd.c_str()); // Insert commas (this makes the string longer)
4258 // --------------------------------------------------------------------------
4259 // Convert to std::string and add to the stats string.
4260 // --------------------------------------------------------------------------
4266 }; // -x- std::string net_io -x-
4268 /*======================================================================*//**
4270 Where the destructor should save final I/O statistics before this rsocket's
4271 resources are completely freed/deallocated.
4273 Developers should take care to check that the @ref rsocket_io::is_final flag
4274 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
4275 since there's no guarantee that the destructor will necessarily be executed
4276 in a timely manner (this flag is set last, after all other statistics are
4277 copied into the structure while in a mutex-locked state).
4278 @returns The same rsocket object so as to facilitate stacking
4284 *///=========================================================================
4285 rsocket* net_io_final(
4286 /// Pointer to @ref rsocket_io structure (nullptr = disabled)
4287 rsocket_io* addr) noexcept {
4288 __io_final_addr = addr;
4290 }; // -x- rsocket* net_io_final -x-
4292 /*======================================================================*//**
4294 Where the destructor should save current I/O statistics.
4296 Statistics are copied into the structure while in a mutex-locked state, but
4297 the copy itself is not an overall atomic snapshot of the I/O statistics even
4298 though each statistic is copied atomically. This means that the actual
4299 statistics could be slightly different if updates occur independently (e.g.,
4300 due to activities in a different thread), but this likely won't matter since
4301 the anticipated use for these statistics is to display or otherwise present
4302 general statistical information to a user at regular intervals.
4303 @returns The same rsocket object so as to facilitate stacking
4309 *///=========================================================================
4310 rsocket* net_io_update(
4311 /// Pointer to @ref rsocket_io structure (nullptr = do nothing)
4312 rsocket_io* addr) noexcept {
4314 // --------------------------------------------------------------------------
4315 // Copy statistics to final location. (We do this last in case there's an
4316 // issue with locking; there shouldn't be, but if there is then at least we
4317 // already we're not inadvertently hanging on to resources at this point that
4318 // need to be freed/closed anyway.)
4319 // --------------------------------------------------------------------------
4320 if (addr != nullptr) {
4322 addr->bytes_rx = __bytes_rx;
4323 addr->bytes_tx = __bytes_tx;
4324 addr->crypt_rx = __crypt_rx;
4325 addr->crypt_tx = __crypt_tx;
4326 addr->is_final = false;
4328 } // -x- if addr -x-
4331 }; // -x- rsocket* net_io_update -x-
4333 /*======================================================================*//**
4335 Poll the underlying socket using the poll() method for data that's ready for
4336 receiving (default), etc.
4339 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4340 select(), and accept()/accept4() when using multiple sockets.
4342 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4343 part of the user address space
4344 @throws randolf::rex::xEINTR Interrupted by a signal
4345 @throws randolf::rex::xENOMEM Insufficient memory
4346 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4347 doesn't refer to a socket
4348 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4349 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4350 is a highly improbable chance that a timeout could still occur if the
4351 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4352 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4355 @returns The resulting (short)pollfd.revents bitfield
4356 @returns 0 if @c timeout_behaviour is set to TIMEOUT_ZERO
4359 *///=========================================================================
4361 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4362 const short events = POLLIN,
4363 /// Number of milliseconds to wait
4364 const int timeout = 0,
4365 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4366 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4367 if (__debug) debug("poll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4368 + ", pollfd.events=" + std::to_string(events)
4369 + ", timeout=" + std::to_string(timeout)
4371 struct pollfd fds{__socket_fd, events, 0};
4372 if (__rc_check(::poll(&fds, 1, timeout)) == 0) {
4373 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4377 }; // -x- short poll -x-
4379 /*======================================================================*//**
4381 Get port number associated with underlying socket descriptor/handle.
4384 - an emphemeral port number assignment (dynamic) is intended
4385 - the underlying socket details are not defined (e.g., in the case of an
4386 empty rsocket instantiation)
4387 - port numbers are not supported/utilized by the current family (e.g., not
4388 AF_INET {IPv4} or AF_INET6 {IPv6})
4390 The port number can be set in most constructors, or via one of the socket()
4392 @returns Port number (typically TCP and UDP, although some other families
4393 such as SCTP and DCCP also utilize port numbers)
4394 @see socket_family()
4395 @see socket_protocol()
4398 *///=========================================================================
4399 const uint16_t port() noexcept {
4400 switch (__socket_addr.ss_family) {
4401 case AF_INET: // IPv4
4402 return ntohs(((sockaddr_in*)&__socket_addr)->sin_port);
4403 case AF_INET6: // IPv6
4404 return ntohs(((sockaddr_in6*)&__socket_addr)->sin6_port);
4405 default: // Everything else
4407 } // -x- switch __socket_addr.ss_family -x-
4408 }; // -x- uint16_t port -x-
4410 /*======================================================================*//**
4412 Poll the underlying socket using the ppoll() method for data that's ready for
4413 receiving (default), etc.
4416 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4417 select(), and accept()/accept4() when using multiple sockets.
4419 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4420 part of the user address space
4421 @throws randolf::rex::xEINTR Interrupted by a signal
4422 @throws randolf::rex::xENOMEM Insufficient memory
4423 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4424 doesn't refer to a socket
4425 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4426 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4427 is a highly improbable chance that a timeout could still occur if the
4428 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4429 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4432 @returns The resulting (short)pollfd.revents bitfield
4435 *///=========================================================================
4437 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4438 const short events = POLLIN,
4440 const struct timespec* tmo_p = nullptr,
4442 const sigset_t* sigmask = nullptr,
4443 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4444 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4445 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4446 + ", pollfd.events=" + std::to_string(events)
4447 + ", timespec{tv_sec=" + std::to_string(tmo_p->tv_sec)
4448 + ", tv_nsec=" + std::to_string(tmo_p->tv_nsec) + "}"
4451 struct pollfd fds{__socket_fd, events, 0};
4452 if (__rc_check(::ppoll(&fds, 1, tmo_p, sigmask)) == 0) {
4453 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4457 }; // -x- short ppoll -x-
4459 /*======================================================================*//**
4461 Poll the underlying socket using the ppoll() method for data that's ready for
4462 receiving (default), etc.
4465 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4466 select(), and accept()/accept4() when using multiple sockets.
4468 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4469 part of the user address space
4470 @throws randolf::rex::xEINTR Interrupted by a signal
4471 @throws randolf::rex::xENOMEM Insufficient memory
4472 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4473 doesn't refer to a socket
4474 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4475 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4476 is a highly improbable chance that a timeout could still occur if the
4477 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4478 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4481 @returns The resulting (short)pollfd.revents bitfield
4483 *///=========================================================================
4485 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4486 const short events = POLLIN,
4487 /// Timeout in seconds
4488 const int tv_sec = 0,
4489 /// Timeout in nanoseconds
4490 const long tv_nsec = 0,
4492 const sigset_t* sigmask = nullptr,
4493 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4494 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4495 struct timespec tmo_p{tv_sec, tv_nsec};
4496 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4497 + ", pollfd.events=" + std::to_string(events)
4498 + ", timespec{tv_sec=" + std::to_string(tmo_p.tv_sec)
4499 + ", tv_nsec=" + std::to_string(tmo_p.tv_nsec) + "}"
4502 struct pollfd fds{__socket_fd, events, 0};
4503 if (__rc_check(::ppoll(&fds, 1, &tmo_p, sigmask)) == 0) {
4504 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4508 }; // -x- short ppoll -x-
4510 /*======================================================================*//**
4512 Send a formatted string to the @ref rsocket endpoint.
4514 The @c format is described in the documentation for the POSIX or Standard C
4515 Library @c printf() function.
4516 @throws randolf::rex::xEBADF The underlying socket is not open
4517 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
4518 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
4519 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
4520 @throws randolf::rex::xENOMEM Insufficient memory
4521 @returns The same rsocket object so as to facilitate stacking
4523 @see is_eol_fix_printf
4530 *///=========================================================================
4532 /// Format string to use
4534 /// Variadic arguments
4538 ::va_start(args, format);
4539 int rc = ::vasprintf(&buf, format, args);
4540 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
4541 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
4542 if (__eol_fix_printf && !__eol.empty()) {
4543 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
4545 __send(str.c_str(), str.length());
4549 } catch (std::exception& e) { // Free buf then re-throw the exception
4550 ::free(buf); // Prevent memory leak when an exception is thrown
4553 } // -x- if __eol_fix_printf -x-
4555 }; // -x- rsocket* printf -x-
4557 /*======================================================================*//**
4559 Send a formatted string to the @ref rsocket endpoint, and append an EoL
4562 The @c format is described in the documentation for the POSIX or Standard C
4563 Library @c printf() function.
4564 @throws randolf::rex::xEBADF The underlying socket is not open
4565 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
4566 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
4567 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
4568 @throws randolf::rex::xENOMEM Insufficient memory
4569 @returns The same rsocket object so as to facilitate stacking
4572 @see is_eol_fix_printf
4579 *///=========================================================================
4580 rsocket* printfline(
4581 /// Format string to use
4583 /// Variadic arguments
4587 ::va_start(args, format);
4588 int rc = ::vasprintf(&buf, format, args);
4589 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
4590 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
4591 if (__eol_fix_printf && !__eol.empty()) {
4592 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
4595 __send(str.c_str(), str.length());
4598 __sendline(buf, rc);
4599 } catch (std::exception& e) { // Free buf then re-throw the exception
4600 ::free(buf); // Prevent memory leak when an exception is thrown
4603 } // -x- if __eol_fix_printf -x-
4605 }; // -x- rsocket* printfline -x-
4607 /*======================================================================*//**
4609 Receive data from the endpoint into a @c std::vector<char> that is allocated
4612 If nbytes is 0, then the internal @ref buffer_size() will be used as the
4613 default, which can also be changed from its compiled-in default of 1024 by
4614 using one of the buffer_size() methods.
4617 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4618 reception, but it's important to note that it does implement temporary
4619 blocking while waiting for data.
4621 @throws randolf::rex::xEBADF The underlying socket is not open
4622 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4624 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4625 part of the user address space
4626 @throws randolf::rex::xEINTR Interrupted by a signal
4627 @throws randolf::rex::xEINVAL Invalid argument passed
4628 @throws randolf::rex::xENOMEM Insufficient memory
4629 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4630 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4631 doesn't refer to a socket
4632 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4635 @returns appropriately-sized vector of characters
4636 @see recv(std::vector<char>, const int)
4637 @see recvz(const size_t, const int)
4639 *///=========================================================================
4640 std::vector<char> recv(
4641 /// Maximum number of bytes to receive
4642 const size_t nbytes = 0,
4643 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4644 const int flags = 0) {
4645 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
4646 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4648 + ", " + std::to_string(buf_size)
4649 + ", " + std::to_string(flags)
4651 std::vector<char> buf(buf_size);
4652 buf.resize(__recv(buf.data(), buf.size(), flags));
4654 }; // -x- std::vector<char> recv -x-
4656 /*======================================================================*//**
4658 Receive data from the endpoint into the @c std::vector object supplied in the
4661 The maximum number of bytes that can be received won't exceed the number of
4662 bytes that the supplied @c std::vector<char> was initialized or resized to.
4665 For @c std::vector it's important that the @c resize() method is used to
4666 pre-allocate the underlying char[] array, instead of the unfortunately-named
4667 @c reserve() method that doesn't pre-allocate, to avoid causing segmentation
4668 faults or other undefined behaviours.
4671 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4672 reception, but it's important to note that it does implement temporary
4673 blocking while waiting for data.
4675 @throws randolf::rex::xEBADF The underlying socket is not open
4676 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4678 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4679 part of the user address space
4680 @throws randolf::rex::xEINTR Interrupted by a signal
4681 @throws randolf::rex::xEINVAL Invalid argument passed
4682 @throws randolf::rex::xENOMEM Insufficient memory
4683 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4684 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4685 doesn't refer to a socket
4686 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4689 @returns The same array that was specified in the @c buf parameter
4690 @see recv(const size_t, const int)
4691 @see recvz(const size_t, const int)
4694 *///=========================================================================
4695 std::vector<char> recv(
4696 /// Target std::vector<char> to receive data into
4697 std::vector<char> buf,
4698 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4699 const int flags = 0) {
4700 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4702 + ", " + std::to_string(buf.size())
4703 + ", " + std::to_string(flags)
4705 buf.resize(__recv(buf.data(), buf.size(), flags));
4707 }; // -x- std::vector<char> recv -x-
4709 /*======================================================================*//**
4711 Receive data from the endpoint into a @c std::string object that is allocated
4714 If nbytes is 0, then the internal @ref buffer_size() will be used as the
4715 default, which can also be changed from its compiled-in default of 1024 by
4716 using one of the buffer_size() methods.
4719 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4720 reception, but it's important to note that it does implement temporary
4721 blocking while waiting for data.
4723 @throws randolf::rex::xEBADF The underlying socket is not open
4724 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4726 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4727 part of the user address space
4728 @throws randolf::rex::xEINTR Interrupted by a signal
4729 @throws randolf::rex::xEINVAL Invalid argument passed
4730 @throws randolf::rex::xENOMEM Insufficient memory
4731 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4732 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4733 doesn't refer to a socket
4734 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4737 @returns appropriately-sized vector of characters
4739 @see recv(std::vector<char>, const int)
4740 @see recvz(const size_t, const int)
4742 *///=========================================================================
4743 std::string recv_as_string(
4744 /// Maximum number of bytes to receive
4745 const size_t nbytes = 0,
4746 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4747 const int flags = 0) {
4748 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
4749 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4751 + ", " + std::to_string(buf_size)
4752 + ", " + std::to_string(flags)
4755 buf.resize(buf_size); // Pre-fill anticipated string size
4756 buf.resize(__recv(buf.data(), buf.size(), flags)); // Shorten string
4758 }; // -x- std::string recv -x-
4760 /*======================================================================*//**
4762 Receive an ASCIIZ string from the endpoint, including the NULL terminator.
4764 @throws randolf::rex::xEBADF The underlying socket is not open
4765 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4767 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4768 part of the user address space
4769 @throws randolf::rex::xEINTR Interrupted by a signal
4770 @throws randolf::rex::xEINVAL Invalid argument passed
4771 @throws randolf::rex::xENOMEM Insufficient memory
4772 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4773 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4774 doesn't refer to a socket
4775 @throws randolf::rex::xERANGE if no NULL terminator is detected (this may
4776 occur before the underlying ASCIIZ string char* array is allocated)
4777 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4780 @returns Pointer to ASCIIZ string (will need to be deleted by caller)
4781 @see send_asciiz(const char*, const int)
4783 *///=========================================================================
4785 /// Maximum number of bytes to receive
4786 const size_t nbytes = 0,
4787 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4788 const int flags = 0) {
4789 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
4790 if (__debug) debug("recv_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4791 + ", " + std::to_string(buf_size)
4792 + ", " + std::to_string(flags)
4795 // --------------------------------------------------------------------------
4796 // Calculate size of buffer (includes NULL terminator since we'll also be
4797 // receiving this from the endpoint).
4798 // --------------------------------------------------------------------------
4801 // --------------------------------------------------------------------------
4802 // Reduce buffer size to what is actually read (remember: we don't actually
4803 // know where the EoL sequence is yet, or if there even is one).
4804 // --------------------------------------------------------------------------
4805 int max = __recv(&buf, buf_size, MSG_PEEK | flags); // Look-ahead at socket stream (buffer was inflated to include EoL sequence)
4807 for (int i = 0; i < max; i++) {
4811 } // -x- if v[i] -x-
4813 if (len < 0) throw randolf::rex::xERANGE("ERANGE: NULL terminator not found");
4815 // --------------------------------------------------------------------------
4816 // I'd love to use std::shared_ptr<char*> for this next part, but it leads
4817 // to intermittent segmentation faults and/or "malloc(): corrupted top size
4818 // occurs" errors outside of this method. So, my conclusion is that char*
4819 // (and char[], as I've tried with this too) are not properly supported by:
4820 // std::shared_ptr<char*> v = std::make_shared<char*>(new char[len + 1]);
4822 // Using std::string() is really not what we want because there is confusion
4823 // concerning the NULL terminator -- with char* strings, it's crystal clear
4824 // that there must be a NULL terminator (since a length isn't tracked). Now,
4825 // if you want an std::string, just initialize as follows:
4827 // char* temp_string = r.recv_asciiz();
4828 // std::string mystr = std::string(temp_string);
4829 // delete temp_string;
4830 // --------------------------------------------------------------------------
4831 char* v = new char[len + 1];
4832 int rc = __recv(v, len + 1, flags); // Consume with NULL terminator from socket stream
4833// TODO: Free "v" in a catch-and-rethrow block (check other calls to __recv and __send too)
4836 }; // -x- char* recv_asciiz -x-
4838 /*======================================================================*//**
4840 Receive one byte (unsigned 8-bit byte) of data from the endpoint.
4842 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4843 reception, but it's important to note that it does implement temporary
4844 blocking while waiting for data.
4846 @throws randolf::rex::xEBADF The underlying socket is not open
4847 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4849 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4850 part of the user address space
4851 @throws randolf::rex::xEINTR Interrupted by a signal
4852 @throws randolf::rex::xEINVAL Invalid argument passed
4853 @throws randolf::rex::xENOMEM Insufficient memory
4854 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4855 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4856 doesn't refer to a socket
4857 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4860 @returns one unsigned character
4861 @see recv(std::vector<char>, const int)
4862 @see recvz(const size_t, const int)
4867 *///=========================================================================
4869 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4870 const int flags = 0) {
4872 if (__debug) debug("recv_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4874 + ", " + std::to_string(sizeof(buf))
4875 + ", " + std::to_string(flags)
4877 int rc = __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4879 }; // -x- byte recv_byte -x-
4881 /*======================================================================*//**
4883 Receive one character (signed 8-bit byte) of data from the endpoint.
4885 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4886 reception, but it's important to note that it does implement temporary
4887 blocking while waiting for data.
4889 @throws randolf::rex::xEBADF The underlying socket is not open
4890 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4892 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4893 part of the user address space
4894 @throws randolf::rex::xEINTR Interrupted by a signal
4895 @throws randolf::rex::xEINVAL Invalid argument passed
4896 @throws randolf::rex::xENOMEM Insufficient memory
4897 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4898 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4899 doesn't refer to a socket
4900 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4903 @returns one signed character
4904 @see recv(std::vector<char>, const int)
4905 @see recvz(const size_t, const int)
4910 *///=========================================================================
4912 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4913 const int flags = 0) {
4915 if (__debug) debug("recv_char(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4917 + ", " + std::to_string(sizeof(buf))
4918 + ", " + std::to_string(flags)
4920 int rc = __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4921 // How to detect disconnected stream when telnet user presses CTRL-C?
4922 //std::cout << "rc=" << std::to_string(rc) << std::endl; // Debug (to be removed)
4923// TODO: Investigate checking platform's char size and moving signing bit accordingly
4925 }; // -x- char recv_char -x-
4927 /*======================================================================*//**
4929 Receive a line of data from the endpoint, into an @ref randolf::rline object
4930 with the EoL character(s) isolated. While this is meant for ASCII and UTF-8
4931 text, it will also work with binary data that doesn't include EoL character
4932 sequences as non-line-ending data (when receiving binary data,
4933 the @ref recv() and @ref recvz() methods tend to be better-suited).
4935 This is essentially a wrapper around what recvline() does, but returns both
4936 the line of text and the EoL sequence together in an @ref randolf::rline
4939 For additional details on the other parameters, please see the @ref recvline
4940 method's documentation for the remaining details.
4942 If you're using a customzied EoL sequence, then it's important to note that
4943 it probably won't be recognized by the @ref randolf::rline class, but do also
4944 check that documentation to confirm this.
4946 @throws randolf::rex::xEAGAIN if timeout occurs before EoL
4947 @throws randolf::rex::xEBADF The underlying socket is not open
4948 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4950 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4951 part of the user address space
4952 @throws randolf::rex::xEINTR Interrupted by a signal
4953 @throws randolf::rex::xEINVAL Invalid argument passed
4954 @throws randolf::rex::xENOMEM Insufficient memory
4955 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4956 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4957 doesn't refer to a socket
4958 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
4959 no EoL character sequence is detected after recvline's buffer became
4960 full (whatever data is waiting may still be received using any of the
4961 recv_ methods {with or without the @c MSG_PEEK flag set}, including
4962 @c recvline() with a larger buffer {see the @c nbytes parameter})
4963 @throws randolf::rex::xERANGE if the timeout parameter is below 0
4964 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4967 @returns One line of text as a randolf::rline object.
4970 @see eol_consumed_seq()
4973 @see sendline(const std::string, const int)
4975 @see timeout_recvline
4976 @see timeout_recvline(long)
4978 *///=========================================================================
4979 randolf::rline recv_rline(
4980 /// Maximum number of bytes to receive (including EoL character sequence)
4981 const size_t nbytes = 0,
4982 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4983 const int flags = 0,
4984 /// Line timeout (in seconds)@n
4985 /// 0 = no timeout (default), unless it was configured by way of the
4986 /// @ref timeout_recvline(long) method
4988 /// Automatically consume bytes read during a failed read (this flag is
4989 /// ignored when the @c MSG_PEEK flag is set) just before throwing the
4990 /// randolf::rex::xEOVERFLOW exception
4991 bool discard_on_overflow = true) {
4992 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
4993 // + (__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
4994 // Internally, the length of the @ref eol() sequence is added to the buffer size
4995 // so that the maximum line length without EoL characters matches the maximum
4996 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
4997 if (__debug) debug("recv_rline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4998 + ", " + std::to_string(buf_size)
4999 + ", " + std::to_string(flags)
5001 std::string line = __recvline(buf_size, flags, timeout, discard_on_overflow);
5002 return randolf::rline(line.append(eol_consumed_seq()));
5003 }; // -x- randolf::rline recv_rline -x-
5005 /*======================================================================*//**
5007 Receive a data structure from the endpoint.
5009 MSB/LSB considerations are important for any integers within your structure,
5010 so be sure to sanitize them with htons(), ntohs(), and related methods prior
5011 to sending, and then after receiving. This way, your data will be protected
5012 against corruption resulting from byte order differences when communicating
5013 between hardware architectures that differ in MSB and LSB byte ordering.
5015 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5016 reception, but it's important to note that it does implement temporary
5017 blocking while waiting for data.
5019 @throws randolf::rex::xEBADF The underlying socket is not open
5020 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5022 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5023 part of the user address space
5024 @throws randolf::rex::xEINTR Interrupted by a signal
5025 @throws randolf::rex::xEINVAL Invalid argument passed
5026 @throws randolf::rex::xENOMEM Insufficient memory
5027 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5028 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5029 doesn't refer to a socket
5030 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5033 @returns Data structure
5036 *///=========================================================================
5037 template <typename T> T recv_struct(
5038 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5039 const int flags = 0) {
5041 if (__debug) debug("recv_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5043 + ", " + std::to_string(sizeof(buf))
5044 + ", " + std::to_string(flags)
5046 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5048 }; // -x- T recv_struct -x-
5050 /*======================================================================*//**
5052 Receive one 16-bit unsigned integer of data in LSB (little endian) order from
5055 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5056 reception, which implements temporary blocking while waiting for data.
5058 @throws randolf::rex::xEBADF The underlying socket is not open
5059 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5061 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5062 part of the user address space
5063 @throws randolf::rex::xEINTR Interrupted by a signal
5064 @throws randolf::rex::xEINVAL Invalid argument passed
5065 @throws randolf::rex::xENOMEM Insufficient memory
5066 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5067 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5068 doesn't refer to a socket
5069 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5072 @returns uint16_t (converted to local endianness)
5074 *///=========================================================================
5075 uint16_t recv_uint16_lsb(
5076 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5077 const int flags = 0) {
5079 if (__debug) debug("recv_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5081 + ", " + std::to_string(sizeof(buf))
5082 + ", " + std::to_string(flags)
5084 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5085 return !__endian_is_msb ? buf : ntohs(buf);
5086 }; // -x- uint16_t recv_uint16_lsb -x-
5088 /*======================================================================*//**
5090 Receive one 16-bit unsigned integer of data in MSB (big endian) order from
5093 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5094 reception, which implements temporary blocking while waiting for data.
5096 @throws randolf::rex::xEBADF The underlying socket is not open
5097 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5099 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5100 part of the user address space
5101 @throws randolf::rex::xEINTR Interrupted by a signal
5102 @throws randolf::rex::xEINVAL Invalid argument passed
5103 @throws randolf::rex::xENOMEM Insufficient memory
5104 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5105 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5106 doesn't refer to a socket
5107 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5110 @returns uint16_t (converted to local endianness)
5112 *///=========================================================================
5113 uint16_t recv_uint16_msb(
5114 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5115 const int flags = 0) {
5117 if (__debug) debug("recv_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5119 + ", " + std::to_string(sizeof(buf))
5120 + ", " + std::to_string(flags)
5122 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5123 return __endian_is_msb ? buf : htons(buf);
5124 }; // -x- uint16_t recv_uint16_msb -x-
5126 /*======================================================================*//**
5128 Receive one 32-bit unsigned integer of data in LSB (little endian) order from
5131 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5132 reception, which implements temporary blocking while waiting for data.
5134 @throws randolf::rex::xEBADF The underlying socket is not open
5135 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5137 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5138 part of the user address space
5139 @throws randolf::rex::xEINTR Interrupted by a signal
5140 @throws randolf::rex::xEINVAL Invalid argument passed
5141 @throws randolf::rex::xENOMEM Insufficient memory
5142 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5143 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5144 doesn't refer to a socket
5145 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5148 @returns uint32_t (converted to local endianness)
5150 *///=========================================================================
5151 uint32_t recv_uint32_lsb(
5152 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5153 const int flags = 0) {
5155 if (__debug) debug("recv_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5157 + ", " + std::to_string(sizeof(buf))
5158 + ", " + std::to_string(flags)
5160 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5161 return !__endian_is_msb ? buf : htonl(buf);
5162 }; // -x- uint32_t recv_uint32_lsb -x-
5164 /*======================================================================*//**
5166 Receive one 32-bit unsigned integer of data in MSB (big endian) order from
5169 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5170 reception, which implements temporary blocking while waiting for data.
5172 @throws randolf::rex::xEBADF The underlying socket is not open
5173 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5175 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5176 part of the user address space
5177 @throws randolf::rex::xEINTR Interrupted by a signal
5178 @throws randolf::rex::xEINVAL Invalid argument passed
5179 @throws randolf::rex::xENOMEM Insufficient memory
5180 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5181 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5182 doesn't refer to a socket
5183 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5186 @returns uint32_t (converted to local endianness)
5188 *///=========================================================================
5189 uint32_t recv_uint32_msb(
5190 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5191 const int flags = 0) {
5193 if (__debug) debug("recv_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5195 + ", " + std::to_string(sizeof(buf))
5196 + ", " + std::to_string(flags)
5198 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5199 return __endian_is_msb ? buf : ntohl(buf);
5200 }; // -x- uint32_t recv_uint32_msb -x-
5202 /*======================================================================*//**
5204 Receive one 64-bit unsigned integer of data in LSB (little endian) order from
5207 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5208 reception, which implements temporary blocking while waiting for data.
5210 @throws randolf::rex::xEBADF The underlying socket is not open
5211 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5213 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5214 part of the user address space
5215 @throws randolf::rex::xEINTR Interrupted by a signal
5216 @throws randolf::rex::xEINVAL Invalid argument passed
5217 @throws randolf::rex::xENOMEM Insufficient memory
5218 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5219 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5220 doesn't refer to a socket
5221 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5224 @returns uint64_t (converted to local endianness)
5226 *///=========================================================================
5227 uint64_t recv_uint64_lsb(
5228 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5229 const int flags = 0) {
5231 if (__debug) debug("recv_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5233 + ", " + std::to_string(sizeof(buf))
5234 + ", " + std::to_string(flags)
5236 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5237 return !__endian_is_msb ? buf : ((((uint64_t)ntohl((buf) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((buf) >> 32)));
5238 }; // -x- uint64_t recv_uint64_lsb -x-
5240 /*======================================================================*//**
5242 Receive one 64-bit unsigned integer of data in MSB (big endian) order from
5245 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5246 reception, which implements temporary blocking while waiting for data.
5248 @throws randolf::rex::xEBADF The underlying socket is not open
5249 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5251 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5252 part of the user address space
5253 @throws randolf::rex::xEINTR Interrupted by a signal
5254 @throws randolf::rex::xEINVAL Invalid argument passed
5255 @throws randolf::rex::xENOMEM Insufficient memory
5256 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5257 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5258 doesn't refer to a socket
5259 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5262 @returns uint64_t (converted to local endianness)
5264 *///=========================================================================
5265 uint64_t recv_uint64_msb(
5266 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5267 const int flags = 0) {
5269 if (__debug) debug("recv_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5271 + ", " + std::to_string(sizeof(buf))
5272 + ", " + std::to_string(flags)
5274 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5275 return __endian_is_msb ? buf : ((((uint64_t)htonl((buf) & 0xffffffffUL)) << 32) | htonl((uint32_t)((buf) >> 32)));
5276 }; // -x- uint64_t recv_uint64_msb -x-
5278 /*======================================================================*//**
5280 Receive data from a specific endpoint.
5283 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5284 reception, but it's important to note that it does implement temporary
5285 blocking while waiting for data.
5288 This method is not compatible with TLS.
5290 @throws randolf::rex::xEBADF The underlying socket is not open
5291 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5293 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5294 part of the user address space
5295 @throws randolf::rex::xEINTR Interrupted by a signal
5296 @throws randolf::rex::xEINVAL Invalid argument passed
5297 @throws randolf::rex::xENOMEM Insufficient memory
5298 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5299 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5300 doesn't refer to a socket
5301 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5304 @returns Appropriately-sized vector of characters
5306 *///=========================================================================
5307 std::vector<char> recvfrom(
5308 /// Maximum number of bytes to receive
5309 const size_t nbytes,
5310 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5312 /// Target endpoint address structure
5313 struct sockaddr *from,
5314 /// Size of target endpoint structure
5315 socklen_t fromlen = sizeof(sockaddr)) {
5316 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5317 if (__debug) debug("recvfrom(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5319 + ", " + std::to_string(buf_size)
5320 + ", " + std::to_string(flags)
5322 + ", " + std::to_string(fromlen)
5324 std::vector<char> v(buf_size);
5325 v.resize(__track_bytes_rx(__rc_check(::recvfrom(__socket_fd, v.data(), v.size(), flags, from, &fromlen))));
5327 }; // -x- std::vector<char> recvfrom -x-
5330 /*======================================================================*//**
5332 This is an internal function that:
5333 1. receives data from the endpoint:
5334 - via underlying socket when TLS is not enabled
5335 - via the OpenSSL socket API when TLS is enabled
5336 2. checks for a socket I/O error (and throws an exception)
5337 3. tracks number of bytes received (if there were no errors)
5339 @throws randolf::rex::xEAGAIN The underlying socket timed out
5340 @throws randolf::rex::xEBADF The underlying socket is not open
5341 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5343 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5344 part of the user address space
5345 @throws randolf::rex::xEINTR Interrupted by a signal
5346 @throws randolf::rex::xEINVAL Invalid argument passed
5347 @throws randolf::rex::xENOMEM Insufficient memory
5348 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5349 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5350 doesn't refer to a socket
5351 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5354 @returns Number of bytes that were successfully received
5356 *///=========================================================================
5360 /// Length of message data
5362 /// Flags (ignored with encrypted streams, except for the MSG_PEEK flag)
5363 const int flags = 0) {
5365// TODO: For MSG_PEEK with OpenSSL, try this suggestion: You can get the underlying file descriptor with BIO_get_fd and then call recv() with the MSG_PEEK flag
5367 // --------------------------------------------------------------------------
5368 // When internal buffering is not set up (which is the default operation), we
5369 // simply passing data directly.
5370 // --------------------------------------------------------------------------
5371 if (__buffer == nullptr) {
5372 return __tls ? (flags & MSG_PEEK ? __rc_check_tls(SSL_peek(__tls_fd, data, len))
5373 : __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, data, len))))
5374 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, data, len, flags)));
5375 } // -x- if !__buffer -x-
5377 // --------------------------------------------------------------------------
5378 // Consume data from socket to make up for OpenSSL's inability to read beyond
5379 // one packet of data in its SSL_peek() and SSL_read() methods, and also some
5380 // inconsistencies with certain raw (unencrypted) socket implementations that
5381 // occasionally refuse to read beyond one packet of data (at least on a first
5382 // attempt to read with or without the MSG_PEEK flag).
5383 // --------------------------------------------------------------------------
5386std::cout << "-(0)- __buffer=" << (long)__buffer << std::endl
5387 << " __buffer_head=" << (long)__buffer_head << std::endl
5388 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5389 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5390 << " __buffer_size=" << __buffer_size << std::endl
5391 << " len=" << len << std::endl;
5393 // --------------------------------------------------------------------------
5394 // Fill internal buffer. It may be more than len is set to, but this is okay
5395 // because we're committed to using internal buffering now anyway (it's far
5396 // more likely that a protocol that requires the use of recvline() once is
5397 // going to be using it repeatedly).
5398 // --------------------------------------------------------------------------
5401 // --------------------------------------------------------------------------
5402 // If the amount of data contained in the internal buffer is sufficient to
5403 // satisfy the amount of data that "len" represents, then skip the attempts
5404 // to read from the socket and just drain from the buffer what's needed.
5405 // --------------------------------------------------------------------------
5406 if ((__buffer_head >= __buffer_tail // Does the buffer wrap around?
5407 ? __buffer_head - __buffer_tail // No, so calcluation is straight-forward
5408 : (__buffer_boundary - __buffer_tail) // Yes, so we need the sum of both parts
5409 + (__buffer_head - __buffer)) >= len) goto __recv_drain;
5412 // --------------------------------------------------------------------------
5413 // Tail is behind head, so do one extra read beforehand to fill it up, which
5414 // affects the first part of a wrap-around since this is a circular buffer.
5415 // --------------------------------------------------------------------------
5416 if (__buffer_head >= __buffer_tail) { // std::cout << "__buffer_head >= __buffer_tail" << std::endl;
5417 n = __tls ? __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, __buffer_head, std::min(len, (size_t)(__buffer_boundary - __buffer_head))))) // Not using SSL_peek() at all here because we need to consume this data as we stuff it into the internal buffer
5418 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, __buffer_head, std::min(len, (size_t)(__buffer_boundary - __buffer_head)), flags & (~MSG_PEEK)))); // Mask the MSG_PEEK flag because we need to consume this data as we stuff it into the internal buffer
5419//std::cout << "n(0)=" << n << std::endl;
5420 if ((__buffer_head += n) > __buffer_boundary) __buffer_head = __buffer; // Advance head (and wrap around if we're at the boundary)
5421// TODO: Test wrap-around, make sure we don't need >= instead of >
5422 } // -x- if __buffer_head -x-
5425std::cout << "-(1)- __buffer=" << (long)__buffer << std::endl
5426 << " __buffer_head=" << (long)__buffer_head << std::endl
5427 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5428 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5429 << " __buffer_size=" << __buffer_size << std::endl
5430 << " len=" << len << std::endl;
5432//std::cout << "Buffer: [" << __buffer_tail[0] << "]" << std::endl;
5434 // --------------------------------------------------------------------------
5435 // Fill in the remaining (unused) portion of the internal buffer.
5436 // --------------------------------------------------------------------------
5437 if (__buffer_head < __buffer_tail && ioctl(__socket_fd, FIONREAD, &n) == 0) {
5438 n = __tls ? __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, __buffer_head, __buffer_head - __buffer_tail))) // Not using SSL_peek() at all here because we need to consume this data as we stuff it into the internal buffer
5439 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, __buffer_head, __buffer_head - __buffer_tail, flags & (~MSG_PEEK)))); // Mask the MSG_PEEK flag because we need to consume this data as we stuff it into the internal buffer
5440//std::cout << "n(1)=" << n << std::endl;
5441 __buffer_head += n; // Advance the head
5442 } // -x- if __buffer_head -x-
5444 // --------------------------------------------------------------------------
5445 // Check if more bytes are available for reading, then run the loop again so
5446 // that we're pulling in as many bytes as possible. This makes up for a
5447 // problem with socket I/O functions not always returning all the data that's
5448 // ready to be consumed.
5449 // --------------------------------------------------------------------------
5450// if (__buffer_head != __buffer_tail && ioctl(__socket_fd, FIONREAD, &n) == 0 && n > 0) goto __recv_loop;
5451//std::cout << "Buffer size = " << __buffer_size << std::endl; // Debug
5452 // --------------------------------------------------------------------------
5453 // Drain the internal buffer. If we can drain it completely, we'll either
5454 // reset head and tail (default), or free() the internal buffer (based on the
5455 // internal policy). TODO: Create policy control options
5457 // MSG_PEEK mode is limited to the internal buffer size. Not using this flag
5458 // is not limited, ... TODO: Figure out how to deal with this
5459 // --------------------------------------------------------------------------
5461 char* drain = __buffer_tail;
5462//std::cout << "Drain: [";
5463 for (n = 0; n < len; n++) {
5464//if (*drain < 32) std::cout << "^" << (char)(drain[0] + 64); else std::cout << drain[0];
5465 ((char*)data)[n] = *drain++; // Copy byte, and advance the drain
5466//std::cout << "{" << (long)drain << "}";
5467 if (drain > __buffer_boundary) drain = __buffer; // Wrap around // TODO: Test this
5468 if (drain == __buffer_head) break; // We've reached the limit; there's no more data // TODO: Move this to earlier to avoid buffer overrun
5471//std::cout << "] n=" << ++n << std::endl;
5473 // --------------------------------------------------------------------------
5474 // Consume data in __buffer if this isn't a MSG_PEEK operation.
5475 // --------------------------------------------------------------------------
5477std::cout << "-(2)- __buffer=" << (long)__buffer << std::endl
5478 << " __buffer_head=" << (long)__buffer_head << std::endl
5479 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5480 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5481 << " MSG_PEEK=" << (flags && MSG_PEEK ? "true" : "false") << std::endl
5482 << " len=" << len << std::endl
5483 << " drain=" << (long)drain << std::endl;
5485// if ((flags & MSG_PEEK) == 0) std::cout << "[Consuming buffer]" << std::endl;
5486 if ((flags & MSG_PEEK) == 0) __buffer_tail = drain; // Consume character(s) unless MSG_PEEK flag is set
5488std::cout << "-(3)- __buffer=" << (long)__buffer << std::endl
5489 << " __buffer_head=" << (long)__buffer_head << std::endl
5490 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5491 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5492 << " MSG_PEEK=" << (flags && MSG_PEEK ? "true" : "false") << std::endl
5493 << " len=" << len << std::endl
5494 << " drain=" << (long)drain << std::endl;
5498 }; // -x- int __recv -x-
5500 /*======================================================================*//**
5502 This is an internal function that:
5503 1. receives a line of data from the endpoint:
5504 - via underlying socket when TLS is not enabled
5505 - via the OpenSSL socket API when TLS is enabled
5506 2. checks for a socket I/O error (and throws an exception)
5507 3. tracks number of bytes received (if there were no errors)
5508 4. isolates the EoL sequence from the line of data, and records it for
5509 later reference (for those cases where it's needed)
5511 @throws randolf::rex::xEAGAIN if timeout occurs before EoL
5512 @throws randolf::rex::xEBADF The underlying socket is not open
5513 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5515 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5516 part of the user address space
5517 @throws randolf::rex::xEINTR Interrupted by a signal
5518 @throws randolf::rex::xEINVAL Invalid argument passed
5519 @throws randolf::rex::xENOMEM Insufficient memory
5520 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5521 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5522 doesn't refer to a socket
5523 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
5524 no EoL character sequence is detected after recvline's buffer became
5525 full (whatever data is waiting may still be received using any of the
5526 recv_ methods {with or without the @c MSG_PEEK flag set}, including
5527 @c recvline() with a larger buffer {see the @c nbytes parameter})
5528 @throws randolf::rex::xERANGE if the timeout parameter is below 0
5529 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5533 *///=========================================================================
5534 std::string __recvline(
5535 /// Maximum number of bytes to receive (including EoL character sequence)
5536 const size_t buf_size,
5537 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5539 /// Line timeout (in seconds)@n
5540 /// 0 = no timeout (default), unless it was configured by way of the
5541 /// @ref timeout_recvline(long) method
5543 /// Automatically consume bytes read during a failed read (this flag is
5544 /// ignored when the @c MSG_PEEK flag is set) just before throwing the
5545 /// randolf::rex::xEOVERFLOW exception
5546 bool discard_on_overflow) {
5548 // --------------------------------------------------------------------------
5549 // Syntax checks as part of preparation of timeout variable for faster
5550 // comparisons within line-reading loop.
5551 // --------------------------------------------------------------------------
5552 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
5553 if (timeout == 0) timeout = __recvline_timeout;
5554 timeout = timeout == 0 ? LONG_MAX : timeout + time(0);
5556 // --------------------------------------------------------------------------
5557 // Allocate line buffer.
5558 // --------------------------------------------------------------------------
5559 std::string v; v.resize(buf_size); // Add EoL size to buffer size; we'll probably be shrinking later anyway
5561 // --------------------------------------------------------------------------
5562 // Create internal buffer for __recv (if it hasn't already been created).
5564 // Buffering becomes necessary with TLS connection because SSL_peek fails to
5565 // return data that spans multiple packets. What triggers this behaviour is
5566 // the command "stty -icanon && openssl s_client host_name tcp_port" followed
5567 // by manual interactive typing some text then pressing the "[Enter]" key.
5569 // The ::malloc(bsize) won't be leaked because it will be freed elsewhere,
5570 // including in this rsocket's destructor.
5571 // --------------------------------------------------------------------------
5572 if (__buffer == nullptr) {
5573 size_t bsize = std::max(__buffer_size, buf_size);
5574 __buffer = (char*)(::malloc(bsize)); //new char[bsize];
5575 __buffer_head = __buffer;
5576 __buffer_tail = __buffer;
5577 __buffer_boundary = __buffer + bsize;
5578 } // -x- if !__buffer -x-
5580 // --------------------------------------------------------------------------
5581 // Line-reading loop.
5582 // --------------------------------------------------------------------------
5583 int len_with_eol = 0;
5584 int br = 0; // Number of Bytes Received
5586 br = __recv(v.data(), v.size(), MSG_PEEK | flags); // Look-ahead at socket stream (with buffer size that was inflated earlier to include EoL sequence)
5587 } catch (const randolf::rex::xEAGAIN e) { // Socket timeout occurred, but recvline timeout may not have yet so we need to catch it here to implement the recvline timeout
5588std::cout << "EAGAIN caught (recvline_check socket timeout occurred) WIP" << std::endl; // Debug
5589 __rc_check(nanosleep(&__recvline_idle, nullptr)); // Prevent tight CPU utilization loop; EINVAL = bad parameters (e.g., tv_nsec > 999999999 is not uncommon)
5591 if (time(0) > timeout) throw randolf::rex::xEAGAIN("recvline timed out");
5592 int len = eol_index(v, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
5594if (br >= buf_size) std::cout << "overflow... br=" << br << " buf_size=" << buf_size << std::endl;
5596 if (br >= buf_size) {
5597 if (discard_on_overflow && (flags & MSG_PEEK) != 0) __recv(v.data(), br, flags); // Consume undesired data before throwing exception
5598 throw randolf::rex::xEOVERFLOW("recvline overflow (line too long or missing EoL sequence)");
5600 } // -x- if !len -x-
5602 // --------------------------------------------------------------------------
5603 // Save the EoL sequence that was detected following a successful recvline()
5604 // (this must be completed prior to "final clean-up" {which is next}, because
5605 // we need to copy these characters before the truncation occurs).
5606 // --------------------------------------------------------------------------
5607 __eol_consumed_seq = v.substr(len, len_with_eol - len);
5609 // --------------------------------------------------------------------------
5611 // --------------------------------------------------------------------------
5612std::cout << "pre-drain" << std::endl;
5613 if ((flags & MSG_PEEK) == 0) __recv(v.data(), len_with_eol, flags); // Consume data if not in MSG_PEEK mode / __recv will throw exception if something goes wrong so there's no need for an additional __rc_check call from here
5614std::cout << "post-drain" << std::endl;
5615 v.resize(len); // Truncate string to exclude unused portion
5618 }; // -x- std::string __recvline -x-
5621 /*======================================================================*//**
5623 Receive a line of data from the endpoint, with the EoL character(s) removed.
5624 While this is meant for ASCII and UTF-8 text, it will also work with binary
5625 data that doesn't include EoL character sequences as non-line-ending data
5626 (when receiving binary data, the @ref recv() and @ref recvz() methods tend to
5629 If @c nbytes is 0, then the internal buffer_size will be used as the default,
5630 which can also be changed from its compiled-in default of 1024 by using one
5631 of the buffer_size() methods. The total number of bytes received, including
5632 the EoL sequence, will not exceed the total number of byte specified with the
5633 @c nbytes parameter.
5635 For @c nbytes the EoL character sequence size should be included, especially
5636 for shorter lines -- if the EoL character sequence is detected automatically
5637 on-the-fly (which is the default), then provisioning 2 characters is needed
5638 since an EoL sequence could be 1 or 2 characters long (line lengths shorter
5639 than 3 characters could be particularly problematic when detecting on-the-fly
5640 EoL character sequences). In such a case, a better approach is to allow for
5641 2 additional characters and then test the length of the returned string to
5642 ensure it doesn't exceed whatever the required maximum is (and throw the same
5643 @ref randolf::rex::xEOVERFLOW exception if the length is exceeded).
5645 For more information about which characters an EoL sequence is comprised of,
5646 and how our specialized support for multiple EoL sequences works, see the
5647 documentation for the various @ref eol methods.
5650 When setting the recvline timeout (either with the @c timeout parameter with
5651 this method, or by using the @ref timeout_recvline(long) method), you may
5652 also need to set the socket timeout beforehand using the @ref timeout()
5653 method, because @c recvline doesn't do this...
5656 @ref timeout_recvline sets the overall total timeout for an entire line to
5659 @ref timeout sets the timeout between individual characters received (such
5660 as keystrokes from a live end-user)
5663 If your socket timeout is longer than then recvline timeout, this will have
5664 the effect of rendering the recvling timeout as ineffective.
5666 The @c timeout parameter can be used to override the total overall timeout
5667 for receiving a line, which is useful for specific situations where a
5668 specific timeout is desired for particular prompts, during certain data entry
5672 When a line is not found (@c randolf::rex::xEAGAIN), or a line is too long
5673 because there's too much data (longer than a line) without an EoL sequence
5674 (@c randolf::rex::xOVERFLOW), then that data will not be discarded or
5675 consumed, and will remain ready for receiving (@ref recv) or for discarding
5679 If you're searching for a readline() method for socket I/O, then this is most
5680 likely what you're wanting/needing.
5682 @throws randolf::rex::xEAGAIN if timeout occurs before EoL
5683 @throws randolf::rex::xEBADF The underlying socket is not open
5684 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5686 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5687 part of the user address space
5688 @throws randolf::rex::xEINTR Interrupted by a signal
5689 @throws randolf::rex::xEINVAL Invalid argument passed
5690 @throws randolf::rex::xENOMEM Insufficient memory
5691 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5692 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5693 doesn't refer to a socket
5694 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
5695 no EoL character sequence is detected after recvline's buffer became
5696 full (whatever data is waiting may still be received using any of the
5697 recv_ methods {with or without the @c MSG_PEEK flag set}, including
5698 @c recvline() with a larger buffer {see the @c nbytes parameter})
5699 @throws randolf::rex::xERANGE if the timeout parameter is below 0
5700 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5703 @returns One line of text as a std::string object.
5706 @see eol_consumed_seq()
5708 @see sendline(const std::string, const int)
5710 @see timeout_recvline
5711 @see timeout_recvline(long)
5713 *///=========================================================================
5714 std::string recvline(
5715 /// Maximum number of bytes to receive (including EoL character sequence)@n
5716 /// 0 = use internal @ref buffer_size()
5717 const size_t nbytes = 0,
5718 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5719 const int flags = 0,
5720 /// Line timeout (in seconds)@n
5721 /// 0 = no timeout (default), unless it was configured by way of the
5722 /// @ref timeout_recvline(long) method
5724 /// Automatically consume bytes read during a failed read (this flag is
5725 /// ignored when the @c MSG_PEEK flag is set) just before throwing the
5726 /// randolf::rex::xEOVERFLOW exception
5727 bool discard_on_overflow = true) {
5728 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
5729 // + (__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
5730 // Internally, the length of the @ref eol() sequence is added to the buffer size
5731 // so that the maximum line length without EoL characters matches the maximum
5732 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
5733 if (__debug) debug("recvline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5734 + ", " + std::to_string(buf_size)
5735 + ", " + std::to_string(flags)
5737 return __recvline(buf_size, flags, timeout, discard_on_overflow);
5738 }; // -x- std::string recvline -x-
5740 /*======================================================================*//**
5742 Receive data in the form of a "msghdr" structure.
5744 This method is not compatible with TLS.
5746 @throws randolf::rex::xEBADF The underlying socket is not open
5747 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5749 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5750 part of the user address space
5751 @throws randolf::rex::xEINTR Interrupted by a signal
5752 @throws randolf::rex::xEINVAL Invalid argument passed
5753 @throws randolf::rex::xENOMEM Insufficient memory
5754 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5755 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5756 doesn't refer to a socket
5757 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5760 @returns pointer to "msghdr" structure
5762 *///=========================================================================
5764 /// Pointer to "msghdr" structure (or nullptr for automatic allocation)
5766 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5767 const int flags = 0) {
5768 if (__debug) debug("recvmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5770 + ", " + std::to_string(flags)
5772 if (msg == nullptr) msg = new msghdr{0};
5773 __track_bytes_rx(__rc_check(::recvmsg(__socket_fd, msg, flags)));
5775 }; // -x- msghdr* recvmsg -x-
5777 /*======================================================================*//**
5779 Receive data in the form of an "mmsghdr" structure.
5781 This method is not compatible with TLS.
5783 @throws randolf::rex::xEBADF The underlying socket is not open
5784 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5786 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5787 part of the user address space
5788 @throws randolf::rex::xEINTR Interrupted by a signal
5789 @throws randolf::rex::xEINVAL Invalid argument passed
5790 @throws randolf::rex::xENOMEM Insufficient memory
5791 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5792 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5793 doesn't refer to a socket
5794 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5797 @returns pointer to "mmsghdr" structure
5799 *///=========================================================================
5801 /// Pointer to "mmsghdr" structure (or nullptr for automatic allocation)
5802 struct mmsghdr* mmsg,
5803 /// Size of target endpoint structure
5804 const unsigned int vlen = sizeof(mmsghdr),
5805 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
5806 const int flags = 0,
5808 struct timespec* timeout = {0}) {
5809 if (__debug) debug("recvmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5811 + ", " + std::to_string(vlen)
5812 + ", " + std::to_string(flags)
5813 + ", timeout{tv_sec=" + std::to_string(timeout->tv_sec)
5814 + ", tv_nsec=" + std::to_string(timeout->tv_nsec) + "}"
5816 __track_bytes_rx(__rc_check(::recvmmsg(__socket_fd, mmsg, vlen, flags, timeout)));
5818 }; // -x- mmsghdr* recvmmsg -x-
5820 /*======================================================================*//**
5822 Receive data from the endpoint, and add a 0 (null) onto the end. This is
5823 useful when using the resulting std::vector<char> as an ASCIIZ string.
5825 If nbytes is 0, then the internal buffer_size will be used as the default,
5826 which can also be changed from its compiled-in default of 1024 by using one
5827 of the buffer_size() methods.
5830 The resulting std::vector size is always inflated by 1. This means that
5831 relying on a comparison against 0 will result in an infinite loop:
5833 for (std::vector<char> v = r.recvz(); v.size > 0; v = recvz()) { ... }
5835 So, you'll need to compare against 1 instead of 0 to compensate fot the
5836 inflated size due to the addition of null character 0:
5838 for (std::vector<char> v = r.recvz(); v.size > 1; v = recvz()) { ... }
5842 The resulting size of std::vector<char> will be <tt>nbytes + 1</tt> which
5843 ensures that any string processing functions or presentation libraries - such
5844 as @c printf() - expecting an ASCIIZ string buffer will stop at null (0), and
5845 will reliably output no more than the maximum size specified.
5847 The way to think of this is that @c nbytes specifies the maximum number of
5848 bytes (a.k.a., characters) to recieve over the underlying socket, and the
5849 final 0 (null) being added is not included in the maximum specified by the
5850 @c nbytes parameter.
5853 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5854 reception, but it's important to note that it does implement temporary
5855 blocking while waiting for data.
5857 @throws randolf::rex::xEBADF The underlying socket is not open
5858 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5860 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5861 part of the user address space
5862 @throws randolf::rex::xEINTR Interrupted by a signal
5863 @throws randolf::rex::xEINVAL Invalid argument passed
5864 @throws randolf::rex::xENOMEM Insufficient memory
5865 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5866 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5867 doesn't refer to a socket
5868 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5871 @returns appropriately-sized vector of characters + 1 (includes additional
5872 null terminator character 0)
5873 @see recv(const size_t, const int)
5874 @see recv(std::vector<char>, const int)
5876 *///=========================================================================
5877 std::vector<char> recvz(
5878 /// Maximum number of bytes to receive
5879 const size_t nbytes = 0,
5880 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5881 const int flags = 0) {
5882 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5883 if (__debug) debug("recvz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5885 + ", " + std::to_string(buf_size)
5886 + ", " + std::to_string(flags)
5888 std::vector<char> v(buf_size);
5889 v.resize(__recv(v.data(), v.size(), flags) + 1); // Add 1 to include room for final null character 0
5890 v[v.size() - 1] = (char)0; // Set final element to 0 (null)
5892 }; // -x- std::vector<char> recvz -x-
5895 /*======================================================================*//**
5897 This is an internal function that:
5898 1. sends data to the endpoint:
5899 - via underlying socket when TLS is not enabled
5900 - via the OpenSSL socket API when TLS is enabled
5901 2. checks for a socket I/O error (and throws an exception)
5902 3. tracks number of bytes transmitted (if there were no errors)
5905 This method is threadsafe because the underlying POSIX send() and OpenSSL's
5906 SSL_write() functions are threadsafe.
5908 @throws randolf::rex::xEBADF The underlying socket is not open
5909 @throws randolf::rex::xECONNRESET Connect reset by peer
5910 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5911 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5912 part of the user address space
5913 @throws randolf::rex::xEINTR Interrupted by a signal
5914 @throws randolf::rex::xEINVAL Invalid argument passed
5915 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5916 occur, but the POSIX sockets documentation lists it as one of the
5917 errors that can be returned, perhaps because some incorrectly
5918 implemented TCP/IP stacks return this error?)
5919 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5920 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5921 and 65,527 bytes for IPv6)
5922 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5923 network congestion (or, less commonly, insufficient memory)
5924 @throws randolf::rex::xENOMEM Insufficient memory
5925 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5926 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5927 doesn't refer to a socket
5928 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5929 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5930 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5932 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5934 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5935 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5936 but it really isn't)
5938 @returns Number of bytes that were successfully transmitted
5940 *///=========================================================================
5944 /// Length of message data
5946 /// Flags (ignored with encrypted streams)
5947 const int flags = 0) {
5948 if (__tls) return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data, len)));
5949 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data, len, flags)));
5950 }; // -x- int __send -x-
5952 /*======================================================================*//**
5954 This is an internal function that:
5955 1. sends data to the endpoint:
5956 - via underlying socket when TLS is not enabled
5957 - via the OpenSSL socket API when TLS is enabled
5958 2. checks for a socket I/O error (and throws an exception)
5959 3. tracks number of bytes transmitted (if there were no errors)
5962 This method is threadsafe because the underlying POSIX send() and OpenSSL's
5963 SSL_write() functions are threadsafe.
5965 @throws randolf::rex::xEBADF The underlying socket is not open
5966 @throws randolf::rex::xECONNRESET Connect reset by peer
5967 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5968 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5969 part of the user address space
5970 @throws randolf::rex::xEINTR Interrupted by a signal
5971 @throws randolf::rex::xEINVAL Invalid argument passed
5972 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5973 occur, but the POSIX sockets documentation lists it as one of the
5974 errors that can be returned, perhaps because some incorrectly
5975 implemented TCP/IP stacks return this error?)
5976 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5977 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5978 and 65,527 bytes for IPv6)
5979 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5980 network congestion (or, less commonly, insufficient memory)
5981 @throws randolf::rex::xENOMEM Insufficient memory
5982 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5983 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5984 doesn't refer to a socket
5985 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5986 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5987 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5989 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5991 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5992 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5993 but it really isn't)
5995 @returns Number of bytes that were successfully transmitted
5997 *///=========================================================================
6001 /// Number of bytes to send
6003 /// Flags (ignored with encrypted streams)
6004 const int flags = 0) {
6005 if (__tls) { // Encrypted
6006 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data , len )))
6007 + __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.c_str(), __eol_out.length())));
6008 } else { // Not encrypted
6009 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data , len , MSG_MORE | flags))) // MSG_MORE prevents extra packets from being sent
6010 + __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.c_str(), __eol_out.length(), flags)));
6011 } // -x- if __tls -x-
6012 }; // -x- int __sendline -x-
6014 /*======================================================================*//**
6016 This is an internal function that:
6017 1. sends data to the endpoint:
6018 - via underlying socket when TLS is not enabled
6019 - via the OpenSSL socket API when TLS is enabled
6020 2. checks for a socket I/O error (and throws an exception)
6021 3. tracks number of bytes transmitted (if there were no errors)
6024 This method is threadsafe because the underlying POSIX send() and OpenSSL's
6025 SSL_write() functions are threadsafe.
6027 @throws randolf::rex::xEBADF The underlying socket is not open
6028 @throws randolf::rex::xECONNRESET Connect reset by peer
6029 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6030 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6031 part of the user address space
6032 @throws randolf::rex::xEINTR Interrupted by a signal
6033 @throws randolf::rex::xEINVAL Invalid argument passed
6034 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6035 occur, but the POSIX sockets documentation lists it as one of the
6036 errors that can be returned, perhaps because some incorrectly
6037 implemented TCP/IP stacks return this error?)
6038 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6039 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6040 and 65,527 bytes for IPv6)
6041 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6042 network congestion (or, less commonly, insufficient memory)
6043 @throws randolf::rex::xENOMEM Insufficient memory
6044 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6045 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6046 doesn't refer to a socket
6047 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6048 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6049 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6051 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6053 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6054 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6055 but it really isn't)
6057 @returns Number of bytes that were successfully transmitted
6059 *///=========================================================================
6061 /// Flags (ignored with encrypted streams)
6062 const int flags = 0) {
6063 if (__tls) { // Encrypted
6064 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.c_str(), __eol_out.length())));
6065 } else { // Not encrypted
6066 return __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.c_str(), __eol_out.length(), flags)));
6067 } // -x- if __tls -x-
6068 }; // -x- int __send_eol -x-
6069 // TODO: Create a recv_eol() method that some people will probably consider to be evil (ha ha!)
6070 // recv_eol() will need to be able to auto-detect the CRLF sequence on-the-fly (if necessary) just like recvline() does
6073 /*======================================================================*//**
6075 Send data in the form of a std::string to the endpoint.
6077 This method is threadsafe.
6079 @throws randolf::rex::xEBADF The underlying socket is not open
6080 @throws randolf::rex::xECONNRESET Connect reset by peer
6081 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6082 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6083 part of the user address space
6084 @throws randolf::rex::xEINTR Interrupted by a signal
6085 @throws randolf::rex::xEINVAL Invalid argument passed
6086 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6087 occur, but the POSIX sockets documentation lists it as one of the
6088 errors that can be returned, perhaps because some incorrectly
6089 implemented TCP/IP stacks return this error?)
6090 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6091 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6092 and 65,527 bytes for IPv6)
6093 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6094 network congestion (or, less commonly, insufficient memory)
6095 @throws randolf::rex::xENOMEM Insufficient memory
6096 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6097 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6098 doesn't refer to a socket
6099 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6100 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6101 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6103 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6105 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6106 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6107 but it really isn't)
6109 @returns The same rsocket object so as to facilitate stacking
6111 *///=========================================================================
6114 const std::string msg,
6115 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6116 const int flags = 0) {
6117 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6119 + ", " + std::to_string(msg.length())
6120 + ", " + std::to_string(flags)
6122 __send(msg.c_str(), msg.length(), flags);
6124 }; // -x- rsocket* send -x-
6126 /*======================================================================*//**
6128 Send data in the form of a std::vector<char> to the endpoint.
6130 This method is threadsafe.
6132 @throws randolf::rex::xEBADF The underlying socket is not open
6133 @throws randolf::rex::xECONNRESET Connect reset by peer
6134 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6135 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6136 part of the user address space
6137 @throws randolf::rex::xEINTR Interrupted by a signal
6138 @throws randolf::rex::xEINVAL Invalid argument passed
6139 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6140 occur, but the POSIX sockets documentation lists it as one of the
6141 errors that can be returned, perhaps because some incorrectly
6142 implemented TCP/IP stacks return this error?)
6143 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6144 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6145 and 65,527 bytes for IPv6)
6146 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6147 network congestion (or, less commonly, insufficient memory)
6148 @throws randolf::rex::xENOMEM Insufficient memory
6149 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6150 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6151 doesn't refer to a socket
6152 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6153 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6154 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6156 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6158 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6159 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6160 but it really isn't)
6162 @returns The same rsocket object so as to facilitate stacking
6164 *///=========================================================================
6167 const std::vector<char> msg,
6168 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6169 const int flags = 0) {
6170 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6172 + ", " + std::to_string(msg.size())
6173 + ", " + std::to_string(flags)
6175 __send(msg.data(), msg.size(), flags);
6177 }; // -x- rsocket* send -x-
6179 /*======================================================================*//**
6181 Send data in the form of a C-string to the endpoint.
6183 This method is threadsafe.
6185 @throws randolf::rex::xEBADF The underlying socket is not open
6186 @throws randolf::rex::xECONNRESET Connect reset by peer
6187 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6188 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6189 part of the user address space
6190 @throws randolf::rex::xEINTR Interrupted by a signal
6191 @throws randolf::rex::xEINVAL Invalid argument passed
6192 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6193 occur, but the POSIX sockets documentation lists it as one of the
6194 errors that can be returned, perhaps because some incorrectly
6195 implemented TCP/IP stacks return this error?)
6196 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6197 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6198 and 65,527 bytes for IPv6)
6199 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6200 network congestion (or, less commonly, insufficient memory)
6201 @throws randolf::rex::xENOMEM Insufficient memory
6202 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6203 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6204 doesn't refer to a socket
6205 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6206 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6207 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6209 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6211 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6212 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6213 but it really isn't)
6215 @returns The same rsocket object so as to facilitate stacking
6218 *///=========================================================================
6220 /// Pointer to data to send
6222 /// Number of bytes to send, or 0 to auto-detect length if message is an ASCIIZ string
6224 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6225 const int flags = 0) {
6226 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6228 + ", " + std::to_string(len)
6229 + ", " + std::to_string(flags)
6232 // --------------------------------------------------------------------------
6233 // Measure size of format string if an ASCIIZ string was indicated.
6234 // --------------------------------------------------------------------------
6235 if (len == 0) len = std::strlen(msg);
6237 // --------------------------------------------------------------------------
6239 // --------------------------------------------------------------------------
6240 __send(msg, len, flags);
6242 }; // -x- rsocket* send -x-
6244 /*======================================================================*//**
6246 Send data in the form of an ASCIIZ string to the endpoint, including the
6247 terminating NULL character.
6249 This method is threadsafe.
6251 @throws randolf::rex::xEBADF The underlying socket is not open
6252 @throws randolf::rex::xECONNRESET Connect reset by peer
6253 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6254 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6255 part of the user address space
6256 @throws randolf::rex::xEINTR Interrupted by a signal
6257 @throws randolf::rex::xEINVAL Invalid argument passed
6258 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6259 occur, but the POSIX sockets documentation lists it as one of the
6260 errors that can be returned, perhaps because some incorrectly
6261 implemented TCP/IP stacks return this error?)
6262 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6263 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6264 and 65,527 bytes for IPv6)
6265 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6266 network congestion (or, less commonly, insufficient memory)
6267 @throws randolf::rex::xENOMEM Insufficient memory
6268 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6269 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6270 doesn't refer to a socket
6271 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6272 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6273 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6275 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6277 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6278 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6279 but it really isn't)
6281 @returns The same rsocket object so as to facilitate stacking
6282 @see recv_asciiz(const size_t, const int)
6283 @see sendz(const char*, const int) which doesn't transmit the terminating
6286 *///=========================================================================
6287 rsocket* send_asciiz(
6288 /// Pointer to data to send
6290 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6291 const int flags = 0) {
6292 if (__debug) debug("send_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6294 + ", " + std::to_string(std::strlen(msg) + 1)
6295 + ", " + std::to_string(flags)
6297 __send(msg, std::strlen(msg) + 1, flags);
6299 }; // -x- rsocket* send_asciiz -x-
6301 /*======================================================================*//**
6303 Send one 8-bit byte (one unsigned character) of data to the endpoint.
6305 This method is threadsafe.
6307 @throws randolf::rex::xEBADF The underlying socket is not open
6308 @throws randolf::rex::xECONNRESET Connect reset by peer
6309 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6310 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6311 part of the user address space
6312 @throws randolf::rex::xEINTR Interrupted by a signal
6313 @throws randolf::rex::xEINVAL Invalid argument passed
6314 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6315 occur, but the POSIX sockets documentation lists it as one of the
6316 errors that can be returned, perhaps because some incorrectly
6317 implemented TCP/IP stacks return this error?)
6318 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6319 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6320 and 65,527 bytes for IPv6)
6321 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6322 network congestion (or, less commonly, insufficient memory)
6323 @throws randolf::rex::xENOMEM Insufficient memory
6324 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6325 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6326 doesn't refer to a socket
6327 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6328 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6329 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6331 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6333 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6334 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6335 but it really isn't)
6337 @returns The same rsocket object so as to facilitate stacking
6342 *///=========================================================================
6346 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6347 const int flags = 0) {
6348 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6350 + ", " + std::to_string(sizeof(value))
6351 + ", " + std::to_string(flags)
6353 __send(&value, sizeof(value), flags);
6355 }; // -x- rsocket* send_byte -x-
6357 /*======================================================================*//**
6359 Send one signed character (one 8-bit byte) of data to the endpoint.
6360 @copydoc send_byte(char, int)
6361 @returns The same rsocket object so as to facilitate stacking
6366 *///=========================================================================
6370 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6371 const int flags = 0) {
6372 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6374 + ", " + std::to_string(sizeof(value))
6375 + ", " + std::to_string(flags)
6377 __send(&value, sizeof(value), flags);
6379 }; // -x- rsocket* send_char -x-
6381 /*======================================================================*//**
6383 Send the EoL sequence to the endpoint.
6385 This method is threadsafe.
6387 @throws randolf::rex::xEBADF The underlying socket is not open
6388 @throws randolf::rex::xECONNRESET Connect reset by peer
6389 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6390 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6391 part of the user address space
6392 @throws randolf::rex::xEINTR Interrupted by a signal
6393 @throws randolf::rex::xEINVAL Invalid argument passed
6394 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6395 occur, but the POSIX sockets documentation lists it as one of the
6396 errors that can be returned, perhaps because some incorrectly
6397 implemented TCP/IP stacks return this error?)
6398 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6399 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6400 and 65,527 bytes for IPv6)
6401 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6402 network congestion (or, less commonly, insufficient memory)
6403 @throws randolf::rex::xENOMEM Insufficient memory
6404 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6405 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6406 doesn't refer to a socket
6407 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6408 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6409 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6411 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6413 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6414 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6415 but it really isn't)
6417 @returns The same rsocket object so as to facilitate stacking
6419 *///=========================================================================
6421 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6422 const int flags = 0) {
6423 if (__debug) debug("send_eol(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6424 + ", " + std::to_string(flags)
6428 }; // -x- rsocket* send_eol -x-
6430 /*======================================================================*//**
6432 Send a data structure to the endpoint.
6434 MSB/LSB considerations are important for any integers within your structure,
6435 so be sure to sanitize them with htons(), ntohs(), and related methods prior
6436 to sending, and then after receiving. This way, your data will be protected
6437 against corruption resulting from byte order differences when communicating
6438 between hardware architectures that differ in MSB and LSB byte ordering.
6440 This method is threadsafe.
6442 @throws randolf::rex::xEBADF The underlying socket is not open
6443 @throws randolf::rex::xECONNRESET Connect reset by peer
6444 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6445 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6446 part of the user address space
6447 @throws randolf::rex::xEINTR Interrupted by a signal
6448 @throws randolf::rex::xEINVAL Invalid argument passed
6449 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6450 occur, but the POSIX sockets documentation lists it as one of the
6451 errors that can be returned, perhaps because some incorrectly
6452 implemented TCP/IP stacks return this error?)
6453 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6454 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6455 and 65,527 bytes for IPv6)
6456 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6457 network congestion (or, less commonly, insufficient memory)
6458 @throws randolf::rex::xENOMEM Insufficient memory
6459 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6460 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6461 doesn't refer to a socket
6462 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6463 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6464 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6466 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6468 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6469 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6470 but it really isn't)
6472 @returns The same rsocket object so as to facilitate stacking
6475 *///=========================================================================
6476 template <typename T> rsocket* send_struct(
6479 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6480 const int flags = 0) {
6481 if (__debug) debug("send_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6483 + ", " + std::to_string(sizeof(value))
6484 + ", " + std::to_string(flags)
6486 __send(&value, sizeof(value), flags);
6488 }; // -x- rsocket* send_struct -x-
6490 /*======================================================================*//**
6492 Send one 16-bit unsigned integer of data in LSB (little endian) order to the
6495 This method is threadsafe.
6497 @throws randolf::rex::xEBADF The underlying socket is not open
6498 @throws randolf::rex::xECONNRESET Connect reset by peer
6499 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6500 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6501 part of the user address space
6502 @throws randolf::rex::xEINTR Interrupted by a signal
6503 @throws randolf::rex::xEINVAL Invalid argument passed
6504 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6505 occur, but the POSIX sockets documentation lists it as one of the
6506 errors that can be returned, perhaps because some incorrectly
6507 implemented TCP/IP stacks return this error?)
6508 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6509 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6510 and 65,527 bytes for IPv6)
6511 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6512 network congestion (or, less commonly, insufficient memory)
6513 @throws randolf::rex::xENOMEM Insufficient memory
6514 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6515 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6516 doesn't refer to a socket
6517 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6518 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6519 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6521 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6523 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6524 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6525 but it really isn't)
6527 @returns The same rsocket object so as to facilitate stacking
6529 *///=========================================================================
6530 rsocket* send_uint16_lsb(
6532 const uint16_t value,
6533 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6534 const int flags = 0) {
6535 uint16_t buf = !__endian_is_msb ? value : ntohs(value);
6536 if (__debug) debug("send_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6537 + ", " + std::to_string(value)
6538 + ", " + std::to_string(sizeof(buf))
6539 + ", " + std::to_string(flags)
6541 __send(&buf, sizeof(buf), flags);
6543 }; // -x- rsocket* send_uint16_lsb -x-
6545 /*======================================================================*//**
6547 Send one 16-bit integer of data in MSB (big endian) order to the endpoint.
6549 This method is threadsafe.
6551 @throws randolf::rex::xEBADF The underlying socket is not open
6552 @throws randolf::rex::xECONNRESET Connect reset by peer
6553 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6554 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6555 part of the user address space
6556 @throws randolf::rex::xEINTR Interrupted by a signal
6557 @throws randolf::rex::xEINVAL Invalid argument passed
6558 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6559 occur, but the POSIX sockets documentation lists it as one of the
6560 errors that can be returned, perhaps because some incorrectly
6561 implemented TCP/IP stacks return this error?)
6562 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6563 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6564 and 65,527 bytes for IPv6)
6565 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6566 network congestion (or, less commonly, insufficient memory)
6567 @throws randolf::rex::xENOMEM Insufficient memory
6568 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6569 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6570 doesn't refer to a socket
6571 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6572 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6573 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6575 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6577 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6578 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6579 but it really isn't)
6581 @returns The same rsocket object so as to facilitate stacking
6583 *///=========================================================================
6584 rsocket* send_uint16_msb(
6586 const uint16_t value,
6587 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6588 const int flags = 0) {
6589 int16_t buf = __endian_is_msb ? value : htons(value);
6590 if (__debug) debug("send_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6591 + ", " + std::to_string(value)
6592 + ", " + std::to_string(sizeof(buf))
6593 + ", " + std::to_string(flags)
6595 __send(&buf, sizeof(buf), flags);
6597 }; // -x- rsocket* send_int16_msb -x-
6599 /*======================================================================*//**
6601 Send one 32-bit unsigned integer of data in LSB (little endian) order to the
6604 This method is threadsafe.
6606 @throws randolf::rex::xEBADF The underlying socket is not open
6607 @throws randolf::rex::xECONNRESET Connect reset by peer
6608 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6609 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6610 part of the user address space
6611 @throws randolf::rex::xEINTR Interrupted by a signal
6612 @throws randolf::rex::xEINVAL Invalid argument passed
6613 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6614 occur, but the POSIX sockets documentation lists it as one of the
6615 errors that can be returned, perhaps because some incorrectly
6616 implemented TCP/IP stacks return this error?)
6617 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6618 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6619 and 65,527 bytes for IPv6)
6620 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6621 network congestion (or, less commonly, insufficient memory)
6622 @throws randolf::rex::xENOMEM Insufficient memory
6623 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6624 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6625 doesn't refer to a socket
6626 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6627 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6628 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6630 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6632 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6633 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6634 but it really isn't)
6636 @returns The same rsocket object so as to facilitate stacking
6638 *///=========================================================================
6639 rsocket* send_uint32_lsb(
6641 const uint32_t value,
6642 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6643 const int flags = 0) {
6644 uint32_t buf = !__endian_is_msb ? value : ntohl(value);
6645 if (__debug) debug("send_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6646 + ", " + std::to_string(value)
6647 + ", " + std::to_string(sizeof(buf))
6648 + ", " + std::to_string(flags)
6650 __send(&buf, sizeof(buf), flags);
6652 }; // -x- rsocket* send_uint32_lsb -x-
6654 /*======================================================================*//**
6656 Send one 32-bit unsigned integer of data in MSB (big endian) order to the
6659 This method is threadsafe.
6661 @throws randolf::rex::xEBADF The underlying socket is not open
6662 @throws randolf::rex::xECONNRESET Connect reset by peer
6663 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6664 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6665 part of the user address space
6666 @throws randolf::rex::xEINTR Interrupted by a signal
6667 @throws randolf::rex::xEINVAL Invalid argument passed
6668 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6669 occur, but the POSIX sockets documentation lists it as one of the
6670 errors that can be returned, perhaps because some incorrectly
6671 implemented TCP/IP stacks return this error?)
6672 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6673 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6674 and 65,527 bytes for IPv6)
6675 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6676 network congestion (or, less commonly, insufficient memory)
6677 @throws randolf::rex::xENOMEM Insufficient memory
6678 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6679 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6680 doesn't refer to a socket
6681 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6682 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6683 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6685 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6687 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6688 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6689 but it really isn't)
6691 @returns The same rsocket object so as to facilitate stacking
6693 *///=========================================================================
6694 rsocket* send_uint32_msb(
6696 const uint32_t value,
6697 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6698 const int flags = 0) {
6699 uint32_t buf = __endian_is_msb ? value : htonl(value);
6700 if (__debug) debug("send_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6701 + ", " + std::to_string(value)
6702 + ", " + std::to_string(sizeof(buf))
6703 + ", " + std::to_string(flags)
6705 __send(&buf, sizeof(buf), flags);
6707 }; // -x- rsocket* send_uint32_msb -x-
6709 /*======================================================================*//**
6711 Send one 64-bit unsigned integer of data in LSB (little endian) order to the
6714 This method is threadsafe.
6716 @throws randolf::rex::xEBADF The underlying socket is not open
6717 @throws randolf::rex::xECONNRESET Connect reset by peer
6718 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6719 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6720 part of the user address space
6721 @throws randolf::rex::xEINTR Interrupted by a signal
6722 @throws randolf::rex::xEINVAL Invalid argument passed
6723 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6724 occur, but the POSIX sockets documentation lists it as one of the
6725 errors that can be returned, perhaps because some incorrectly
6726 implemented TCP/IP stacks return this error?)
6727 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6728 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6729 and 65,527 bytes for IPv6)
6730 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6731 network congestion (or, less commonly, insufficient memory)
6732 @throws randolf::rex::xENOMEM Insufficient memory
6733 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6734 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6735 doesn't refer to a socket
6736 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6737 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6738 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6740 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6742 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6743 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6744 but it really isn't)
6746 @returns The same rsocket object so as to facilitate stacking
6748 *///=========================================================================
6749 rsocket* send_uint64_lsb(
6751 const uint64_t value,
6752 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6753 const int flags = 0) {
6754 uint64_t buf = !__endian_is_msb ? value : ((((uint64_t)ntohl((value) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((value) >> 32)));
6755 if (__debug) debug("send_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6756 + ", " + std::to_string(value)
6757 + ", " + std::to_string(sizeof(buf))
6758 + ", " + std::to_string(flags)
6760 __send(&buf, sizeof(buf), flags);
6762 }; // -x- rsocket* send_uint64_lsb -x-
6764 /*======================================================================*//**
6766 Send one 64-bit unsigned integer of data in MSB (big endian) order to the
6769 This method is threadsafe.
6771 @throws randolf::rex::xEBADF The underlying socket is not open
6772 @throws randolf::rex::xECONNRESET Connect reset by peer
6773 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
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::xEISCONN Socket is already connected (this shouldn't
6779 occur, but the POSIX sockets documentation lists it as one of the
6780 errors that can be returned, perhaps because some incorrectly
6781 implemented TCP/IP stacks return this error?)
6782 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6783 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6784 and 65,527 bytes for IPv6)
6785 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6786 network congestion (or, less commonly, insufficient memory)
6787 @throws randolf::rex::xENOMEM Insufficient memory
6788 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6789 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6790 doesn't refer to a socket
6791 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6792 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6793 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6795 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6797 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6798 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6799 but it really isn't)
6801 @returns The same rsocket object so as to facilitate stacking
6803 *///=========================================================================
6804 rsocket* send_uint64_msb(
6806 const uint64_t value,
6807 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6808 const int flags = 0) {
6809 uint64_t buf = __endian_is_msb ? value : ((((uint64_t)htonl((value) & 0xffffffffUL)) << 32) | htonl((uint32_t)((value) >> 32)));
6810 if (__debug) debug("send_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6811 + ", " + std::to_string(value)
6812 + ", " + std::to_string(sizeof(buf))
6813 + ", " + std::to_string(flags)
6815 __send(&buf, sizeof(buf), flags);
6817 }; // -x- rsocket* send_uint64_msb -x-
6819 /*======================================================================*//**
6821 Send data in the form of a std::string to the endpoint, with an EoL sequence
6824 This method is threadsafe.
6826 @throws randolf::rex::xEBADF The underlying socket is not open
6827 @throws randolf::rex::xECONNRESET Connect reset by peer
6828 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6829 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6830 part of the user address space
6831 @throws randolf::rex::xEINTR Interrupted by a signal
6832 @throws randolf::rex::xEINVAL Invalid argument passed
6833 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6834 occur, but the POSIX sockets documentation lists it as one of the
6835 errors that can be returned, perhaps because some incorrectly
6836 implemented TCP/IP stacks return this error?)
6837 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6838 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6839 and 65,527 bytes for IPv6)
6840 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6841 network congestion (or, less commonly, insufficient memory)
6842 @throws randolf::rex::xENOMEM Insufficient memory
6843 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6844 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6845 doesn't refer to a socket
6846 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6847 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6848 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6850 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6852 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6853 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6854 but it really isn't)
6856 @returns The same rsocket object so as to facilitate stacking
6859 @see recvline(const size_t, const int)
6862 *///=========================================================================
6865 const std::string msg = std::string(),
6866 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6867 const int flags = 0) {
6868 if (__debug) debug("sendline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6870 + ", " + std::to_string(msg.length())
6871 + "+" + std::to_string(__eol_out.length())
6872 + ", " + std::to_string(flags)
6874 __sendline(msg.c_str(), msg.length(), flags);
6876 }; // -x- rsocket* sendline -x-
6878 /*======================================================================*//**
6880 Send data in the form of a "msghdr" structure to a specific endpoint.
6882 This method is not compatible with TLS.
6884 This method is threadsafe.
6886 @throws randolf::rex::xEBADF The underlying socket is not open
6887 @throws randolf::rex::xECONNRESET Connect reset by peer
6888 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6889 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6890 part of the user address space
6891 @throws randolf::rex::xEINTR Interrupted by a signal
6892 @throws randolf::rex::xEINVAL Invalid argument passed
6893 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6894 occur, but the POSIX sockets documentation lists it as one of the
6895 errors that can be returned, perhaps because some incorrectly
6896 implemented TCP/IP stacks return this error?)
6897 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6898 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6899 and 65,527 bytes for IPv6)
6900 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6901 network congestion (or, less commonly, insufficient memory)
6902 @throws randolf::rex::xENOMEM Insufficient memory
6903 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6904 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6905 doesn't refer to a socket
6906 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6907 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6908 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6910 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6912 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6913 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6914 but it really isn't)
6916 @returns The same rsocket object so as to facilitate stacking
6918 *///=========================================================================
6920 /// Pointer to data to send
6921 const struct msghdr* msg,
6922 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6923 const int flags = 0) {
6924 if (__debug) debug("sendmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6926 + ", " + std::to_string(flags)
6928 __track_bytes_tx(__rc_check(::sendmsg(__socket_fd, msg, flags)));
6930 }; // -x- rsocket* sendmsg -x-
6932 /*======================================================================*//**
6934 Send data in the form of a "mmsghdr" structure to a specific endpoint.
6936 This method is not compatible with TLS.
6938 This method is threadsafe.
6940 @throws randolf::rex::xEBADF The underlying socket is not open
6941 @throws randolf::rex::xECONNRESET Connect reset by peer
6942 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6943 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6944 part of the user address space
6945 @throws randolf::rex::xEINTR Interrupted by a signal
6946 @throws randolf::rex::xEINVAL Invalid argument passed
6947 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6948 occur, but the POSIX sockets documentation lists it as one of the
6949 errors that can be returned, perhaps because some incorrectly
6950 implemented TCP/IP stacks return this error?)
6951 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6952 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6953 and 65,527 bytes for IPv6)
6954 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6955 network congestion (or, less commonly, insufficient memory)
6956 @throws randolf::rex::xENOMEM Insufficient memory
6957 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6958 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6959 doesn't refer to a socket
6960 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6961 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6962 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6964 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6966 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6967 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6968 but it really isn't)
6970 @returns The same rsocket object so as to facilitate stacking
6972 *///=========================================================================
6974 /// Pointer to data to send
6975 struct mmsghdr* mmsg,
6976 /// Size of target endpoint structure
6977 const unsigned int vlen = sizeof(mmsghdr),
6978 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6979 const int flags = 0) {
6980 if (__debug) debug("sendmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6982 + ", " + std::to_string(vlen)
6983 + ", " + std::to_string(flags)
6985 __track_bytes_tx(__rc_check(::sendmmsg(__socket_fd, mmsg, vlen, flags)));
6987 }; // -x- rsocket* sendmsg -x-
6989 /*======================================================================*//**
6991 Send data in the form of a std::string to a specific endpoint.
6993 This method is not compatible with TLS.
6995 This method is threadsafe.
6997 @throws randolf::rex::xEBADF The underlying socket is not open
6998 @throws randolf::rex::xECONNRESET Connect reset by peer
6999 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7000 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7001 part of the user address space
7002 @throws randolf::rex::xEINTR Interrupted by a signal
7003 @throws randolf::rex::xEINVAL Invalid argument passed
7004 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7005 occur, but the POSIX sockets documentation lists it as one of the
7006 errors that can be returned, perhaps because some incorrectly
7007 implemented TCP/IP stacks return this error?)
7008 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7009 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7010 and 65,527 bytes for IPv6)
7011 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7012 network congestion (or, less commonly, insufficient memory)
7013 @throws randolf::rex::xENOMEM Insufficient memory
7014 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7015 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7016 doesn't refer to a socket
7017 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7018 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7019 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7021 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7023 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7024 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7025 but it really isn't)
7027 @returns The same rsocket object so as to facilitate stacking
7029 *///=========================================================================
7032 const std::string msg,
7033 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
7035 /// Target endpoint address structure
7036 const struct sockaddr *to,
7037 /// Size of target endpoint structure
7038 socklen_t tolen = sizeof(sockaddr)) {
7039 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7041 + ", " + std::to_string(flags)
7043 + ", " + std::to_string(tolen)
7045 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg.c_str(), msg.length(), flags, to, tolen)));
7047 }; // -x- rsocket* sendto -x- // TODO: Create easier-to-use variants
7049 /*======================================================================*//**
7051 Send data in the form of a C-string to a specific endpoint.
7053 This method is not compatible with TLS.
7055 This method is threadsafe.
7057 @throws randolf::rex::xEBADF The underlying socket is not open
7058 @throws randolf::rex::xECONNRESET Connect reset by peer
7059 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7060 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7061 part of the user address space
7062 @throws randolf::rex::xEINTR Interrupted by a signal
7063 @throws randolf::rex::xEINVAL Invalid argument passed
7064 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7065 occur, but the POSIX sockets documentation lists it as one of the
7066 errors that can be returned, perhaps because some incorrectly
7067 implemented TCP/IP stacks return this error?)
7068 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7069 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7070 and 65,527 bytes for IPv6)
7071 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7072 network congestion (or, less commonly, insufficient memory)
7073 @throws randolf::rex::xENOMEM Insufficient memory
7074 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7075 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7076 doesn't refer to a socket
7077 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7078 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7079 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7081 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7083 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7084 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7085 but it really isn't)
7087 @returns The same rsocket object so as to facilitate stacking
7089 *///=========================================================================
7091 /// Pointer to data to send
7093 /// Number of bytes to send
7095 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
7097 /// Target endpoint address structure
7098 const struct sockaddr *to,
7099 /// Size of target endpoint structure
7100 socklen_t tolen = sizeof(sockaddr)) {
7101 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7103 + ", " + std::to_string(std::strlen(msg))
7104 + ", " + std::to_string(flags)
7106 + ", " + std::to_string(tolen)
7108 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, len, flags, to, tolen)));
7110 }; // -x- rsocket* sendto -x-
7112 /*======================================================================*//**
7114 Send data in the form of an ASCIIZ string to the endpoint. The terminating
7115 NULL character won't be transmitted.
7117 This method is threadsafe.
7119 @throws randolf::rex::xEBADF The underlying socket is not open
7120 @throws randolf::rex::xECONNRESET Connect reset by peer
7121 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7122 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7123 part of the user address space
7124 @throws randolf::rex::xEINTR Interrupted by a signal
7125 @throws randolf::rex::xEINVAL Invalid argument passed
7126 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7127 occur, but the POSIX sockets documentation lists it as one of the
7128 errors that can be returned, perhaps because some incorrectly
7129 implemented TCP/IP stacks return this error?)
7130 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7131 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7132 and 65,527 bytes for IPv6)
7133 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7134 network congestion (or, less commonly, insufficient memory)
7135 @throws randolf::rex::xENOMEM Insufficient memory
7136 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7137 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7138 doesn't refer to a socket
7139 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7140 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7141 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7143 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7145 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7146 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7147 but it really isn't)
7149 @returns The same rsocket object so as to facilitate stacking
7150 @see recvz(const size_t, const int)
7151 @see send_asciiz(const char*, const int) which also transmits the terminating
7154 *///=========================================================================
7156 /// Pointer to data to send
7158 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
7159 const int flags = 0) {
7160 if (__debug) debug("sendz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7162 + ", " + std::to_string(std::strlen(msg))
7163 + ", " + std::to_string(flags)
7165 __send(msg, std::strlen(msg), flags);
7167 }; // -x- rsocket* sendz -x-
7169 /*======================================================================*//**
7171 Send data in the form of an ASCIIZ string to a specific endpoint. The
7172 terminating NULL character won't be transmitted.
7174 This method is not compatible with TLS.
7176 This method is threadsafe.
7178 @throws randolf::rex::xEBADF The underlying socket is not open
7179 @throws randolf::rex::xECONNRESET Connect reset by peer
7180 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7181 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7182 part of the user address space
7183 @throws randolf::rex::xEINTR Interrupted by a signal
7184 @throws randolf::rex::xEINVAL Invalid argument passed
7185 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7186 occur, but the POSIX sockets documentation lists it as one of the
7187 errors that can be returned, perhaps because some incorrectly
7188 implemented TCP/IP stacks return this error?)
7189 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7190 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7191 and 65,527 bytes for IPv6)
7192 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7193 network congestion (or, less commonly, insufficient memory)
7194 @throws randolf::rex::xENOMEM Insufficient memory
7195 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7196 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7197 doesn't refer to a socket
7198 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7199 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7200 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7202 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7204 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7205 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7206 but it really isn't)
7208 @returns The same rsocket object so as to facilitate stacking
7209 *///=========================================================================
7211 /// Pointer to data to send
7213 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
7215 /// Target endpoint address structure
7216 const struct sockaddr *to,
7217 /// Size of target endpoint structure
7218 socklen_t tolen = sizeof(sockaddr)) {
7219 if (__debug) debug("sendzto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7221 + ", " + std::to_string(std::strlen(msg))
7222 + ", " + std::to_string(flags)
7224 + ", " + std::to_string(tolen)
7226 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, std::strlen(msg), flags, to, tolen)));
7228 }; // -x- rsocket* sendzto -x-
7230 /*======================================================================*//**
7232 Set socket option to the specific integer.
7235 These setsockopt() methods take an integer or character value directly, or a
7236 pointer to a structure, and then rsocket handles the remaining tedious
7237 technical details behind-the-scenes for you when calling the underlying
7238 socket's setsockopt() function.
7240 @throws randolf::rex::xEBADF The underlying socket is not open
7241 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7242 part of the user address space
7243 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7244 valid for this socket's family (a.k.a., communication domain)
7245 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7247 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7248 doesn't refer to a socket
7250 @returns The same rsocket object so as to facilitate stacking
7253 *///=========================================================================
7254 rsocket* setsockopt(
7255 /// The level at which the option resides; typically @c SOL_SOCKET
7257 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7259 /// The value that this socket option will be set to
7261 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
7262 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7264 }; // -x- rsocket* setsockopt -x-
7266 /*======================================================================*//**
7268 Set socket option to the specific unsigned integer.
7269 @copydetails setsockopt(const int, const int, const int)
7272 For any values that require a u_int, you'll need to explicitly cast this type
7273 when specifying the value directly; for example: (u_int)32768
7275 @throws randolf::rex::xEBADF The underlying socket is not open
7276 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7277 part of the user address space
7278 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7279 valid for this socket's family (a.k.a., communication domain)
7280 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7282 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7283 doesn't refer to a socket
7285 @returns The same rsocket object so as to facilitate stacking
7287 *///=========================================================================
7288 rsocket* setsockopt(
7289 /// The level at which the option resides; typically @c SOL_SOCKET
7291 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7293 /// The value that this socket option will be set to
7294 const u_int value) {
7295 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
7296 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7298 }; // -x- rsocket* setsockopt -x-
7300 /*======================================================================*//**
7302 Set socket option to the specific unsigned character.
7303 @copydetails setsockopt(const int, const int, const int)
7305 *///=========================================================================
7306 rsocket* setsockopt(
7307 /// The level at which the option resides; typically @c SOL_SOCKET
7309 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7311 /// The value that this socket option will be set to
7312 const u_char value) {
7313 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
7314 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7316 }; // -x- rsocket* setsockopt -x-
7318 /*======================================================================*//**
7320 Set socket option to the specific structure.
7321 @copydetails setsockopt(const int, const int, const int)
7323 *///=========================================================================
7324 rsocket* setsockopt(
7325 /// The level at which the option resides; typically @c SOL_SOCKET
7327 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7329 /// The structure that this socket option will be set to
7330 const linger& value) {
7331 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7332 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7334 }; // -x- rsocket* setsockopt -x-
7336 /*======================================================================*//**
7338 Set socket option to the specific structure.
7339 @copydetails setsockopt(const int, const int, const linger&)
7341 *///=========================================================================
7342 rsocket* setsockopt(
7343 /// The level at which the option resides; typically @c SOL_SOCKET
7345 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7347 /// The structure that this socket option will be set to
7348 const timeval& value) {
7349 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7350 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7352 }; // -x- rsocket* setsockopt -x-
7354 /*======================================================================*//**
7356 Set socket option to the specific structure.
7357 @copydetails setsockopt(const int, const int, const linger&)
7359 *///=========================================================================
7360 rsocket* setsockopt(
7361 /// The level at which the option resides; typically @c SOL_SOCKET
7363 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7365 /// The structure that this socket option will be set to
7366 const in_addr& value) {
7367 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7368 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7370 }; // -x- rsocket* setsockopt -x-
7372 /*======================================================================*//**
7374 Set socket option to the specific structure.
7375 @copydetails setsockopt(const int, const int, const linger&)
7377 *///=========================================================================
7378 rsocket* setsockopt(
7379 /// The level at which the option resides; typically @c SOL_SOCKET
7381 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7383 /// The structure that this socket option will be set to
7384 const ip_mreq& value) {
7385 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7386 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7388 }; // -x- rsocket* setsockopt -x-
7390 /*======================================================================*//**
7392 Set socket option to the specific structure.
7393 @copydetails setsockopt(const int, const int, const linger&)
7395 *///=========================================================================
7396 rsocket* setsockopt(
7397 /// The level at which the option resides; typically @c SOL_SOCKET
7399 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7401 /// The structure that this socket option will be set to
7402 const ip_mreq_source& value) {
7403 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7404 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7406 }; // -x- rsocket* setsockopt -x-
7408 /*======================================================================*//**
7410 Set socket option to the specific structure.
7411 @copydetails setsockopt(const int, const int, const linger&)
7413 *///=========================================================================
7414 rsocket* setsockopt(
7415 /// The level at which the option resides; typically @c SOL_SOCKET
7417 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7419 /// The structure that this socket option will be set to
7420 const icmp6_filter& value) {
7421 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7422 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7424 }; // -x- rsocket* setsockopt -x-
7426 /*======================================================================*//**
7428 Set socket option to the specific structure.
7429 @copydetails setsockopt(const int, const int, const linger&)
7431 *///=========================================================================
7432 rsocket* setsockopt(
7433 /// The level at which the option resides; typically @c SOL_SOCKET
7435 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7437 /// The structure that this socket option will be set to
7438 const sockaddr_in6& value) {
7439 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7440 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7442 }; // -x- rsocket* setsockopt -x-
7444 /*======================================================================*//**
7446 Set socket option to the specific structure.
7447 @copydetails setsockopt(const int, const int, const linger&)
7449 *///=========================================================================
7450 rsocket* setsockopt(
7451 /// The level at which the option resides; typically @c SOL_SOCKET
7453 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7455 /// The structure that this socket option will be set to
7456 const ip6_mtuinfo& value) {
7457 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7458 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7460 }; // -x- rsocket* setsockopt -x-
7462 /*======================================================================*//**
7464 Set socket option to the specific structure.
7465 @copydetails setsockopt(const int, const int, const linger&)
7467 *///=========================================================================
7468 rsocket* setsockopt(
7469 /// The level at which the option resides; typically @c SOL_SOCKET
7471 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7473 /// The structure that this socket option will be set to
7474 const ipv6_mreq& value) {
7475 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7476 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7478 }; // -x- rsocket* setsockopt -x-
7480 /*======================================================================*//**
7482 Set socket option to the specific structure.
7483 @copydetails setsockopt(const int, const int, const linger&)
7485 *///=========================================================================
7486 rsocket* setsockopt(
7487 /// The level at which the option resides; typically @c SOL_SOCKET
7489 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7491 /// The structure that this socket option will be set to
7492 const group_req& value) {
7493 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7494 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7496 }; // -x- rsocket* setsockopt -x-
7498 /*======================================================================*//**
7500 Set socket option to the specific structure.
7501 @copydetails setsockopt(const int, const int, const linger&)
7503 *///=========================================================================
7504 rsocket* setsockopt(
7505 /// The level at which the option resides; typically @c SOL_SOCKET
7507 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7509 /// The structure that this socket option will be set to
7510 const group_source_req& value) {
7511 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7512 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7514 }; // -x- rsocket* setsockopt -x-
7516 /*======================================================================*//**
7518 Shut down the underlying socket, partially or fully.
7520 <div style=padding-left:32px;>
7523 <td valign=top>SHUT_RD:</td>
7524 <td>Further receives will be disallowed.</td>
7527 <td valign=top>SHUT_WR:</td>
7528 <td>Further sends will be disallowed (this may cause actions specific
7529 to the protocol family of the socket to occur).</td>
7532 <td valign=top>SHUT_RDWR:</td>
7533 <td>Further sends and receives will be disallowed (default).</td>
7538 @throws randolf::rex::xEBADF The underlying socket is not open
7539 @throws randolf::rex::xEINVAL Invalid argument passed
7540 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7541 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7542 doesn't refer to a socket
7544 @returns The same rsocket object so as to facilitate stacking
7547 *///=========================================================================
7551 /// SHUT_RDWR (default)
7552 const int how = SHUT_RDWR) {
7553 if (__debug) debug("shutdown(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7554 + ", " + std::to_string(how)
7556 if (__tls) SSL_shutdown(__tls_fd); // We have to shut down TLS connections first, if they're active
7557 __rc_check(::shutdown(__socket_fd, how));
7558 //__socket_connected = false; // TODO: Figure out when to change this to false
7560 }; // -x- rsocket* shutdown -x-
7562 /*======================================================================*//**
7564 Complete the configuration of an rsocket that was previously initialized
7565 without any parameters (a.k.a., an "empty rsocket").
7566 @copydetails rsocket()
7567 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
7568 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
7569 @throws randolf::rex::xEALREADY If this socket() method was already used, or
7570 it was used after rsocket() initialized with at least one parameter
7571 @throws randolf::rex::xEINVAL Protocal family invalid or not available
7572 @throws randolf::rex::xEINVAL Invalid flags in type
7573 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
7574 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
7575 @throws randolf::rex::xENOBUFS Insufficient memory
7576 @throws randolf::rex::xENOMEM Insufficient memory
7577 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
7578 supported within the specified family (a.k.a., communication domain)
7579 @returns The same rsocket object so as to facilitate stacking
7581 @see socket_family()
7583 @see socket_protocol()
7587 *///=========================================================================
7589 /// Communication domain; usually one of:@n
7590 /// AF_INET (IPv4)@n
7591 /// AF_INET6 (IPv6)@n
7592 /// AF_UNIX (UNIX domain sockets)
7594 /// Communication semantics; usually one of:@n
7595 /// SOCK_STREAM (common for TCP)@n
7596 /// SOCK_DGRAM (common for UDP)
7597 const int type = SOCK_STREAM,
7598 /// Network protocol; usually one of:@n
7602 /// PF_UNSPEC (auto-detect)
7603 const int protocol = PF_UNSPEC) {
7604 __socket(family, type, protocol);
7606 }; // -x- rsocket* socket -x-
7608 /*======================================================================*//**
7610 Get underlying socket family/domain constant (SO_DOMAIN).
7611 @returns socket family/domain constant
7615 @see socket_protocol()
7618 *///=========================================================================
7619 const int socket_family() noexcept { return __socket_addr.ss_family; }; // -x- int socket_family -x-
7621 /*======================================================================*//**
7623 Get underlying socket descriptor/handle.
7624 @returns socket descriptor/handle
7625 @returns 0 = socket not yet allocated
7628 @see socket_family()
7629 @see socket_protocol()
7632 *///=========================================================================
7633 const int socket_fd() noexcept { return __socket_fd; }; // -x- int socket_fd -x-
7635 /*======================================================================*//**
7637 Set underlying socket descriptor/handle (to one that is presumed to be open).
7639 This method is only available while an underlying socket has not been created
7640 or previously assigned, such as after an empty @ref rsocket instantiation.
7641 @throws randolf::rex::xEALREADY If this socket_fd() method was already used,
7642 or it was used after socket() initialized it, or if rsocket() had
7643 initialized with at least one parameter that resulted in the creation
7644 of an underlying socket
7645 @returns The same rsocket object so as to facilitate stacking
7647 @see socket_family()
7648 @see socket_protocol()
7651 *///=========================================================================
7653 /// New socket descriptor/handle
7654 const int new_socket_fd) {
7655 if (__debug) debug("socket_fd(socket{0x" + randolf::rtools::to_hex(new_socket_fd) + "}"
7657 if (__socket_fd != 0) throw randolf::rex::xEALREADY("EALREADY");
7658 __socket_fd = new_socket_fd;
7659 __socket_addr.ss_family = getsockopt_int(SOL_SOCKET, SO_DOMAIN);
7660 __socket_type = getsockopt_int(SOL_SOCKET, SO_TYPE);
7661 __socket_protocol = getsockopt_int(SOL_SOCKET, SO_PROTOCOL);
7662 __socket_open = true;
7664 }; // -x- rsocket* socket_fd -x-
7666 /*======================================================================*//**
7668 Get underlying socket protocol constant (SO_PROTOCOL).
7669 @returns socket protocol constant
7672 @see socket_family()
7676 *///=========================================================================
7677 const int socket_protocol() noexcept { return __socket_protocol; }; // -x- int socket_protocol -x-
7679 /*======================================================================*//**
7681 Get underlying socket type constant (SO_TYPE).
7682 @returns socket type constant
7685 @see socket_family()
7687 @see socket_protocol()
7689 *///=========================================================================
7690 const int socket_type() noexcept { return __socket_type; }; // -x- int socket_type -x-
7692 /*======================================================================*//**
7694 Find out whether the underlying socket is at the out-of-band (OOB) mark.
7696 @throws randolf::rex::xEBADF The underlying socket is not open
7697 @throws randolf::rex::xEINVAL The underlying socket file descriptor is not a
7698 type to which @ref sockatmark() can be applied
7700 @returns TRUE = at OOB mark
7701 @returns FALSE = not at OOB mark
7704 *///=========================================================================
7705 const bool sockatmark() {
7706 return __rc_check(::sockatmark(__socket_fd)) ? true : false;
7707 }; // -x- bool sockatmark -x-
7709 /*======================================================================*//**
7711 Find out what the read timeout is set to on the current socket.
7713 Since getting the read timeout is such a common operation, this specialized
7714 method was created to ease software development efforts; internally we're
7715 just calling getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO).
7717 @throws randolf::rex::xEBADF The underlying socket is not open
7718 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7719 part of the user address space
7720 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7721 valid for this socket's family (a.k.a., communication domain)
7722 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7724 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7725 doesn't refer to a socket
7727 @returns @c timeval socket option structure wrapped in std::shared_ptr
7729 *///=========================================================================
7730 std::shared_ptr<timeval> timeout() {
7731 return getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO);
7732 }; // -x- std::shared_ptr<timeval> timeout -x-
7734 /*======================================================================*//**
7736 Set the recv timeout on the current socket.
7738 Since setting the read timeout is such a common operation, this specialized
7739 method was created to ease software development efforts; internally we're
7740 just calling setsockopt(SOL_SOCKET, SO_RCVTIMEO, timeval).
7742 Although a timeout of 100,000 microseconds (1/10 of one second) may suffice
7743 in healthy and efficient networks, a more conservative setting of 1 second
7744 tends to minimally yield more reliable results. Many end-user applications
7745 use defaults of 3 to 5 seconds to cover a wider variety of unpredictable
7746 network connections (such as over shared wireless connections that are slow),
7747 and this setting should ultimately be configurable by users/administrators.
7750 The default timeout for new sockets is normally 0 (no timeout).
7752 @throws randolf::rex::xEBADF The underlying socket is not open
7753 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7754 part of the user address space
7755 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7756 valid for this socket's family (a.k.a., communication domain)
7757 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7759 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7760 doesn't refer to a socket
7762 @returns The same rsocket object so as to facilitate stacking
7764 *///=========================================================================
7766 /// timeval structure
7767 const struct timeval tv) {
7768 return setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
7769 }; // -x- rsocket* timeout -x-
7771 /*======================================================================*//**
7772 @copydoc timeout(struct timeval)
7774 *///=========================================================================
7776 /// Timeout in seconds
7777 const int tv_sec = 0,
7778 /// Timeout in microseconds
7779 const long tv_usec = 0) {
7780 struct timeval tv{tv_sec, tv_usec};
7781 return setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
7782 }; // -x- rsocket* timeout -x-
7784 /*======================================================================*//**
7786 Find out what the read timeout is set to when using the @ref recvline()
7789 @returns @c long value (0 = no timeout)
7792 @see timeout_recvline(long)
7794 *///=========================================================================
7795 long timeout_recvline() {
7796 return __recvline_timeout;
7797 }; // -x- long timeout_recvline -x-
7799 /*======================================================================*//**
7801 Set the read timeout for the @ref recvline() method (the @ref recvline()
7802 method's @c timeout parameter can override this setting).
7805 The default timeout for this recvline_timeout setting is 0 (no timeout).
7807 @throws randolf::rex::xERANGE if the timeout parameter is below 0
7809 @returns The same rsocket object so as to facilitate stacking
7812 @see timeout_recvline
7814 *///=========================================================================
7815 rsocket* timeout_recvline(
7816 /// timeval structure
7817 const long timeout) {
7818 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
7819 __recvline_timeout = timeout;
7821 }; // -x- rsocket* timeout_recvline -x-
7823 /*======================================================================*//**
7825 Enable or disable encrypted communications (from the OpenSSL library).
7828 TLS cannot be enabled before a non-encrypted rsocket is open (an rsocket is
7829 typically opened with the @ref socket() method, the @ref connect() method, or
7830 one of the @ref accept() methods). If calling @c tls(true) on an rsocket
7831 that isn't open, an exception will be thrown.
7833 If needed, a new TLS context will be instantiated and TLS will be initialized
7834 (if this hasn't already been done). TLS instantiation can be done first by
7835 calling the @ref tls_ctx() method (regardless of whether encryption is being
7836 enabled or disabled). If the default @ref TLS_FLAGS aren't sufficient for
7837 the needs of your application, then the @ref tls_ctx() method facilitates
7838 this regardless of wehther rsocket is open.
7841 The reason a TLS context is instantiated and TLS is initialized even when the
7842 status is being set to FALSE is to facilitate TLS ingress from an unencrypted
7843 connection later in the session (see the @ref TLS_NO_INGRESS flag for more
7847 If a client attempts to upgrade a TLS connection to TLS (e.g., by using a
7848 command such as @c STARTTLS, which is commonly transmitted in unencrypted
7849 form, like @c telnet-ssl does -- using `telnet-ssl -z ssl` prevents this
7850 condition), the following error that's difficult to track down may be
7851 triggered when calling any of the @c recv methods (I hope that including this
7852 information here in this documentation will be helpful):
7854 SSL_ERROR_UNKNOWN: error:0A00010B:SSL routines::wrong version number
7856 This is most likely not a programming error, but rather a problem with how
7857 users may attempt to mis-use a connection based on a misunderstanding of the
7858 communications requirements (e.g., connecting unencrypted and attempting to
7859 upgrade to TLS over a connection that's expecting TLS encrypted data from the
7860 very beginning, without involving any ingress).
7862 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7863 OpenSSL library doesn't document which errors may be returned)
7865 @returns The same rsocket object so as to facilitate stacking
7869 *///=========================================================================
7871 /// TRUE = Enable encrypted communications@n FALSE = Disable encrypted communications
7872 const bool status = true,
7873 /// Configuration parameters
7874 const int flags = TLS_FLAGS::TLS_DEFAULT) { // | TLS_FLAGS::TLS_CLIENT
7875 if (__debug) debug("tls(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7876 + ", " + (status ? "true" : "false")
7879 // --------------------------------------------------------------------------
7880 // Create default context (with "flags" passthrough), unless it was already
7881 // created (usually by one of the tls_ctx() methods).
7882 // --------------------------------------------------------------------------
7883 if (__tls_ctx == nullptr) tls_ctx((SSL_CTX*)nullptr, flags);
7885 // --------------------------------------------------------------------------
7886 // Make sure tls_fd (ssl_fd) is allocated. If not, then it needs to be
7887 // allocated and configured.
7888 // --------------------------------------------------------------------------
7889 if (status == true && __tls_fd == nullptr) {
7890 __tls_fd = SSL_new(__tls_ctx);
7891 if (__debug) debug("SSL_set_fd(<tls_fd>, socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7894 // --------------------------------------------------------------------------
7895 // Associate OpenSSL file descriptor with underlying socket file descriptor.
7896 // --------------------------------------------------------------------------
7897 __rc_check_tls(SSL_set_fd(__tls_fd, __socket_fd));
7899 // --------------------------------------------------------------------------
7900 // Enable read-ahead so that SSL_peek will work properly.
7901 // --------------------------------------------------------------------------
7902 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
7903 SSL_set_read_ahead(__tls_fd, 1); // 0=disable / 1=enable
7905// SSL_CTX_set_max_pipelines(__tls_ctx, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
7906// SSL_set_max_pipelines(__tls_fd, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
7908// SSL_CTX_set_mode(__tls_ctx, SSL_MODE_AUTO_RETRY);
7909// SSL_set_mode(__tls_fd, SSL_MODE_AUTO_RETRY);
7911 // --------------------------------------------------------------------------
7912 // We're probably not going to use BIO because we don't need it, and it adds
7913 // unnecessary overhead for what we're doing. Also, BIO doesn't provide an
7914 // alternative to the SSL_peek function, and also doesn't resolve the need
7915 // for the MSG_WAITALL flag that the ::recv() function supports.
7917 // TODO: Remove this completely, unless is can solve the problem of reading
7918 // all incoming data (needed for readline, primarily)
7919 // --------------------------------------------------------------------------
7920 __tls_rbio = SSL_get_rbio(__tls_fd);
7921 __tls_wbio = SSL_get_wbio(__tls_fd);
7923 } // -x- if !__tls_fd -x-
7926 }; // -x- rsocket* tls -x-
7928 /*======================================================================*//**
7930 Return the current TLS context (multiple TLS contexts are supported, although
7931 typically needed to support SNI with inbound connections).
7932 @returns Pointer to OpenSSL's context (normally labelled @c SSL_CTX* in the
7933 documentation for OpenSSL), or nullptr if this context was never
7934 assigned to (or created by) this rsocket
7937 *///=========================================================================
7938 SSL_CTX* tls_ctx() noexcept {
7940 }; // -x- SSL_CTX* tls_ctx -x-
7942 /*======================================================================*//**
7944 Copy the source rsocket's TLS context map and add it to this rsocket's
7945 collection; or, if the source doesn't have any TLS contexts and this rsocket
7946 doesn't have any TLS contexts in its collection, then initialize TLS and
7947 instantiate a new TLS context. In either scenario, the source rsocket will
7948 be treated as a template as all TLS flags duplicated to enable encrypted
7949 socket I/O for use in this rsocket().
7952 At least one TLS context is needed to enable encrypted socket I/O for use in
7956 Encrypted socket I/O is only possible after a TLS context has been
7957 initialized (this is not a global setting as it has per-rsocket specificity).
7960 The only @ref TLS_FLAGS flag that doesn't get transferred is @ref TLS_SERVER
7961 when no flags are specified. Specifying any flag(s) will cause this method
7962 to ignore the source rsocket's TLS flags so as to defer to this override.
7964 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7965 OpenSSL library doesn't document which errors may be returned)
7967 @returns The same rsocket object so as to facilitate stacking
7970 *///=========================================================================
7972 /// OpenSSL's TLS context to use (if not provided, a new context will be
7973 /// created automatically using OpenSSL's defaults)
7975 /// Configuration parameters
7976 const int flags = TLS_FLAGS::TLS_DEFAULT) {
7977 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7979 + ", " + std::to_string(flags)
7982 // --------------------------------------------------------------------------
7983 // TLS-related variables (OpenSSL).
7985 // Note: The __tls_fd variable cannot be allocated until after __socket_fd
7986 // has been, hence the "post" note in the documentation.
7987 // --------------------------------------------------------------------------
7988 __tls = (bool)rtemplate->__tls; // Preserve TLS mode setting
7989 if (rtemplate->__tls_ctx != nullptr) { // If TLS context is defined, then do these important things:
7990 __tls_ctx = rtemplate->__tls_ctx; // 1. copy the pointer to SSL_CTX
7991 SSL_CTX_up_ref(__tls_ctx); // 2. increment SSL_CTX's internal reference count
7992 } // -x- if __tlx_ctx -x-
7994 // --------------------------------------------------------------------------
7995 // Copy or override TLS flags.
7996 // --------------------------------------------------------------------------
7997 if (flags == TLS_FLAGS::TLS_DEFAULT) {
7998 __tls_exclusive = rtemplate->__tls_exclusive; // TLS policy
7999 __tls_egress = rtemplate->__tls_egress; // TLS policy
8000 __tls_ingress = rtemplate->__tls_ingress; // TLS policy
8001 } else { // Save flags
8002 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
8003 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
8004 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
8005 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
8006 } // -x- if flags -x-
8009 }; // -x- rsocket* tls_ctx -x-
8011 /*======================================================================*//**
8013 Initialize TLS and instantiate a TLS context, and add it to this rsocket's
8014 current collection of TLS contexts, and set it as the currently active TLS
8015 context (so that a certificate chain and private key may be added to it).
8017 At least one TLS context is needed to enable encrypted socket I/O for use in
8020 Encrypted socket I/O is only possible after a TLS context has been
8021 initialized (this is not a global setting as it has per-rsocket specificity).
8023 This is the default TLS context for this @c rsocket, which will also be used
8024 for non-SNI handshakes.
8026 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8027 OpenSSL library doesn't document which errors may be returned)
8029 @returns The same rsocket object so as to facilitate stacking
8032 *///=========================================================================
8034 /// OpenSSL's TLS context to use (if not provided, a new context will be
8035 /// created using OpenSSL's defaults)
8037 /// Configuration parameters
8038 const int flags = TLS_FLAGS::TLS_DEFAULT) {
8039 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8041 + ", " + std::to_string(flags)
8044 // --------------------------------------------------------------------------
8045 // Ignore repeated calls to this method.
8046 // --------------------------------------------------------------------------
8047 if (__tls_ctx != nullptr) return this;
8049 // --------------------------------------------------------------------------
8050 // Fire up OpenSSL's algorithms and pre-load its error strings.
8052 // These two functions have been deprecated since OpenSSL v1.1.0, so we don't
8053 // need to call them. If someone needs them, then they can always call them
8054 // in their own code anyway.
8055 // --------------------------------------------------------------------------
8056 //OpenSSL_add_all_algorithms(); // Load and register cryptography algorithms
8057 //SSL_load_error_strings(); // Load all error messages into memory
8059 // --------------------------------------------------------------------------
8060 // Save (ctx != nullptr) or create (ctx == nullptr) OpenSSL context.
8061 // --------------------------------------------------------------------------
8062 __tls_ctx = ctx == nullptr ? SSL_CTX_new(flags & TLS_FLAGS::TLS_SERVER ? TLS_server_method() : TLS_client_method()) // Create anew (default)
8063 : ctx; // Use OpenSSL context that was provided
8064 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);
8066 // --------------------------------------------------------------------------
8067 // Enable read-ahead so that SSL_peek will work properly.
8068 // --------------------------------------------------------------------------
8069 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
8071 // --------------------------------------------------------------------------
8073 // --------------------------------------------------------------------------
8074 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
8075 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
8076 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
8077 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
8080 }; // -x- rsocket* tls_ctx -x-
8082 /*======================================================================*//**
8084 Check the private key it to ensure it's consistent with the corresponding TLS
8087 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8088 OpenSSL library doesn't document which errors may be returned)
8090 @returns The same rsocket object so as to facilitate stacking
8091 @see tls_ctx_use_privatekey_file
8092 @see tls_ctx_use_privatekey_pem
8094 *///=========================================================================
8095 rsocket* tls_ctx_check_privatekey() {
8096 if (__debug) debug("tls_ctx_check_privatekey();");
8097 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);
8099 }; // -x- rsocket* tls_ctx_check_privatekey -x-
8101 /*======================================================================*//**
8103 Load a TLS certificate chain and private key in PEM format from text files
8104 and use them in the TLS context.
8106 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8107 OpenSSL library doesn't document which errors may be returned)
8109 @returns The same rsocket object so as to facilitate stacking
8110 @see tls_ctx_use_certificate_chain_and_privatekey_pems
8111 @see tls_ctx_use_certificate_chain_file
8112 @see tls_ctx_use_certificate_chain_pem
8113 @see tls_ctx_use_privatekey_file
8114 @see tls_ctx_use_privatekey_pem
8115 @see tls_ctx_check_privatekey
8117 *///=========================================================================
8118 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
8119 /// Pointer to ASCIIZ path and filename to certificate chain file (@c nullptr
8120 /// will simply be ignored)
8121 const char* chain_file,
8122 /// Pointer to ASCIIZ path and filename to private key file (@c nullptr will
8123 /// simply be ignored)
8124 const char* key_file) {
8125 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
8126 + std::string(chain_file)
8127 + ", " + std::string( key_file)
8129 if (chain_file != nullptr) tls_ctx_use_certificate_chain_file(chain_file);
8130 if ( key_file != nullptr) tls_ctx_use_privatekey_file( key_file);
8132 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
8134 /*======================================================================*//**
8136 Load a TLS certificate chain and private key in PEM format from text files
8137 and use them in the TLS context.
8139 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8140 OpenSSL library doesn't document which errors may be returned)
8142 @returns The same rsocket object so as to facilitate stacking
8143 @see tls_ctx_use_certificate_chain_and_privatekey_pems
8144 @see tls_ctx_use_certificate_chain_file
8145 @see tls_ctx_use_certificate_chain_pem
8146 @see tls_ctx_use_privatekey_file
8147 @see tls_ctx_use_privatekey_pem
8148 @see tls_ctx_check_privatekey
8150 *///=========================================================================
8151 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
8152 /// Pointer to ASCIIZ path and filename to certificate chain file (an empty
8153 /// string will simply be ignored)
8154 const std::string chain_file,
8155 /// Pointer to ASCIIZ path and filename to private key file (an empty string
8156 /// will simply be ignored)
8157 const std::string key_file) {
8158 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
8162 if (!chain_file.empty()) tls_ctx_use_certificate_chain_file(chain_file);
8163 if ( !key_file.empty()) tls_ctx_use_privatekey_file( key_file);
8165 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
8167 /*======================================================================*//**
8169 Load a TLS certificate chain and a TLS private key in PEM format from memory
8170 and use them in the TLS context.
8172 Although this functionality doesn't exist in OpenSSL (at the time of writing
8173 this method), it's provided here in a manner that has exactly the same effect
8174 as the @ref tls_ctx_use_certificate_chain_and_privatekey_files() methods, but
8175 without needing the PEM-formatted certificate chain stored in files
8179 The @c cert_pem_data and key_pem_data parameters are pointers to the memory
8180 locations that holds the PEM formatted certificate chain data and private key
8181 data, respectively. If the corresponding lengths of each of these data aren't
8182 specified or are set to zero (default), then they will be treated as multiline
8185 Behind the scenes, we're just writing the cert_pem_data and key_pem_data
8186 memory to temporary files with severely-limited permissions (), then
8187 optionally overwriting those temporary files with random data prior to
8188 deleting them (this is the default, since better security practices should be
8189 the default, but on a secured system it may not be necessary and so this
8190 option can also be disabled to save CPU cycles and reduce overall disk-write
8193 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8194 OpenSSL library doesn't document which errors may be returned)
8196 @returns The same rsocket object so as to facilitate stacking
8197 @see tls_ctx_use_certificate_chain_and_privatekey_files
8198 @see tls_ctx_use_certificate_chain_file
8199 @see tls_ctx_use_certificate_chain_pem
8200 @see tls_ctx_use_privatekey_file
8201 @see tls_ctx_use_privatekey_pem
8202 @see tls_ctx_check_privatekey
8204 *///=========================================================================
8205 rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems(
8206 /// Pointer to certificate chain data in PEM format
8207 const char* cert_pem_data,
8208 /// Pointer to private key data in PEM format
8209 const char* key_pem_data,
8210 /// Length of cert_pem_data (in bytes), or 0 to auto-detect length if cert_pem_data is an ASCIIZ string
8211 size_t cert_len = 0,
8212 /// Length of key_pem_data (in bytes), or 0 to auto-detect length if key_pem_data is an ASCIIZ string
8214 /// Whether to overwrite the temporary files with random data before deleting them
8215 const bool random_fill = true) {
8216 tls_ctx_use_certificate_chain_pem(cert_pem_data, cert_len, random_fill);
8217 tls_ctx_use_privatekey_pem( key_pem_data, key_len, random_fill);
8219 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems -x-
8221 /*======================================================================*//**
8223 Load a TLS certificate chain in PEM format from a text file and use it in the
8226 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8227 OpenSSL library doesn't document which errors may be returned)
8229 @returns The same rsocket object so as to facilitate stacking
8230 @see tls_ctx_use_certificate_chain_file
8231 @see tls_ctx_use_certificate_chain_pem
8232 @see tls_ctx_check_privatekey
8234 *///=========================================================================
8235 rsocket* tls_ctx_use_certificate_chain_file(
8236 /// Pointer to ASCIIZ path and filename to certificate chain file
8238 if (__debug) debug("tls_ctx_use_certificate_chain_file("
8241 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);
8243 }; // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
8245 /*======================================================================*//**
8247 Load a TLS certificate chain in PEM format from a text file and use it in the
8250 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8251 OpenSSL library doesn't document which errors may be returned)
8253 @returns The same rsocket object so as to facilitate stacking
8254 @see tls_ctx_use_certificate_chain_file
8255 @see tls_ctx_use_certificate_chain_pem
8256 @see tls_ctx_check_privatekey
8258 *///=========================================================================
8259 rsocket* tls_ctx_use_certificate_chain_file(
8260 /// Path and filename to certificate chain file
8261 const std::string file) {
8262 if (__debug) debug("tls_ctx_use_certificate_chain_file("
8265 if (SSL_CTX_use_certificate_chain_file(__tls_ctx, file.c_str()) != 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);
8267 }; // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
8269 /*======================================================================*//**
8271 Load a TLS certificate chain in PEM format from memory and use it in the TLS
8274 Although this functionality doesn't exist in OpenSSL (at the time of writing
8275 this method), it's provided here in a manner that has exactly the same effect
8276 as the @ref tls_ctx_use_certificate_chain_file() methods, but without needing
8277 the PEM-formatted certificate chain stored in a file beforehand.
8280 The @c pem_data parameter is a pointer to the memory location that holds
8281 the PEM formatted certificate chain data. If the length of this data isn't
8282 specified or is set to zero (default), then it will be treated as a multiline
8285 Behind the scenes, we're just writing the pem_data memory to a temporary
8286 file with severely-limited permissions (), then optionally overwriting that
8287 temporary file with random data prior to deleting it (this is the default,
8288 since better security practices should be the default, but on a secured
8289 system it may not be necessary and so this option can also be disabled to
8290 save CPU cycles and reduce overall disk-write I/O operations).
8292 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8293 OpenSSL library doesn't document which errors may be returned)
8295 @returns The same rsocket object so as to facilitate stacking
8296 @see tls_ctx_use_certificate_chain_file
8297 @see tls_ctx_check_privatekey
8299 *///=========================================================================
8300 rsocket* tls_ctx_use_certificate_chain_pem(
8301 /// Pointer to certificate chain data in PEM format
8302 const char* pem_data,
8303 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
8305 /// Whether to overwrite the temporary file with random data before deleting it
8306 const bool random_fill = true) {
8307 if (__debug) debug("tls_ctx_use_certificate_chain_pem(<buf>, " + std::to_string(len)
8308 + ", " + (random_fill ? "true" : "false")
8311 // --------------------------------------------------------------------------
8312 // Measure size of certificate chain if an ASCIIZ string was indicated.
8313 // --------------------------------------------------------------------------
8314 if (len == 0) len = std::strlen(pem_data);
8316 // --------------------------------------------------------------------------
8317 // Generate filename for temporary use.
8318 // --------------------------------------------------------------------------
8319 std::string file = std::filesystem::temp_directory_path();
8320 file.append("/rsocket.")
8321 .append(std::to_string(::getpid()))
8323 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
8325 // --------------------------------------------------------------------------
8326 // Open temporary file.
8327 // --------------------------------------------------------------------------
8328 FILE* fp = fopen(file.c_str(), "w+");
8329 if (fp == nullptr) randolf::rex::mk_exception("Cannot open temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8331 ioctl(fileno(fp), FS_IOC_GETFLAGS, &attr);
8332 attr |= FS_NOATIME_FL // Don't update access time attribute
8333 | FS_NODUMP_FL // Don't include in filesystem backup dumps
8334 | FS_SECRM_FL; // Mark file for secure deletion (where supported)
8335 ioctl(fileno(fp), FS_IOC_SETFLAGS, &attr);
8337 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
8338 if (fputs(pem_data, fp) == EOF) {
8340 randolf::rex::mk_exception("Cannot write to temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8341 } // -x- if !fputs -x-
8344 // --------------------------------------------------------------------------
8345 // Attempt to load certificate chain file, but save the error code for later
8346 // because we need to clean up the temporary file before possibly throwing an
8348 // --------------------------------------------------------------------------
8349 int rc = SSL_CTX_use_certificate_chain_file(__tls_ctx, file.c_str());
8351 // --------------------------------------------------------------------------
8352 // Overwrite the contenst of the temporary file before deleting it so as to
8353 // sabotage a simple attempt to undelete the file and access the certificate.
8355 // We're also re-using the "len" local variable because it's not needed once
8356 // we get the loop started, and it's more optimal to not allocate yet another
8357 // local variable while "len" goes to waste. :D
8358 // --------------------------------------------------------------------------
8359 if (random_fill) { // This option is configurable
8360 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
8361 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
8362 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
8363 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
8364 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
8366 } // -x- if randfill -x-
8367 fchmod(fileno(fp), 0); // Remove all permissions
8368 fclose(fp); // Close file handle
8369 unlink(file.c_str()); // Delete temporary file
8371 // --------------------------------------------------------------------------
8372 // Error check ... was delayed here until after temporary file cleanup.
8373 // --------------------------------------------------------------------------
8374 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);
8377 }; // -x- rsocket* tls_ctx_use_certificate_chain_pem -x-
8379 /*======================================================================*//**
8381 Load a TLS private key in PEM format from a text file and use it in the TLS
8384 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8385 OpenSSL library doesn't document which errors may be returned)
8387 @returns The same rsocket object so as to facilitate stacking
8388 @see tls_ctx_use_privatekey_file
8389 @see tls_ctx_use_privatekey_pem
8390 @see tls_ctx_check_privatekey
8392 *///=========================================================================
8393 rsocket* tls_ctx_use_privatekey_file(
8394 /// Pointer to ASCIIZ path-and-filename of private key file
8396 if (__debug) debug("tls_ctx_use_privatekey_file("
8399 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);
8402 }; // -x- rsocket* tls_ctx_use_privatekey_file -x-
8404 /*======================================================================*//**
8406 Load a TLS private key in PEM format from a text file and use it in the TLS
8409 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8410 OpenSSL library doesn't document which errors may be returned)
8412 @returns The same rsocket object so as to facilitate stacking
8413 @see tls_ctx_use_privatekey_file
8414 @see tls_ctx_use_privatekey_pem
8415 @see tls_ctx_check_privatekey
8417 *///=========================================================================
8418 rsocket* tls_ctx_use_privatekey_file(
8419 /// Path and filename to private key file
8420 const std::string file) {
8421 if (__debug) debug("tls_ctx_use_privatekey_file("
8424 if (SSL_CTX_use_PrivateKey_file(__tls_ctx, file.c_str(), 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);
8427 }; // -x- rsocket* tls_ctx_use_privatekey_file -x-
8429 /*======================================================================*//**
8431 Load a TLS private key in PEM format from memory and use it in the TLS
8434 Although this functionality doesn't exist in OpenSSL (at the time of writing
8435 this method), it's provided here in a manner that has exactly the same effect
8436 as the @ref tls_ctx_use_privatekey_file() methods, but without needing the
8437 PEM-formatted private key stored in a file beforehand.
8440 The @c pem_data parameter is a pointer to the memory location that holds the
8441 PEM formatted private key data. If the length of this data isn't specified
8442 or is set to zero (default), then it will be treated as a multiline ASCIIZ
8445 Behind the scenes, we're just writing the pem_data memory to a temporary
8446 file (with severely-limited permissions), then optionally overwriting that
8447 temporary file with random data prior to deleting it (this is the default,
8448 since better security practices should be the default, but on a secured
8449 system it may not be necessary and so this option can also be disabled to
8450 save CPU cycles and reduce overall disk-write I/O operations).
8452 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8453 OpenSSL library doesn't document which errors may be returned)
8455 @returns The same rsocket object so as to facilitate stacking
8456 @see tls_ctx_use_privatekey_file
8457 @see tls_ctx_check_privatekey
8459 *///=========================================================================
8460 rsocket* tls_ctx_use_privatekey_pem(
8461 /// Pointer to private key data in PEM format
8462 const char* pem_data,
8463 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
8465 /// Whether to overwrite the temporary file with random data before deleting it
8466 const bool random_fill = true) {
8467 if (__debug) debug("tls_ctx_use_privatekey_pem(<buf>, " + std::to_string(len)
8468 + ", " + (random_fill ? "true" : "false")
8471 // --------------------------------------------------------------------------
8472 // Measure size of private key if an ASCIIZ string was indicated.
8473 // --------------------------------------------------------------------------
8474 if (len == 0) len = std::strlen(pem_data);
8476 // --------------------------------------------------------------------------
8477 // Generate filename for temporary use.
8478 // --------------------------------------------------------------------------
8479 std::string file = std::filesystem::temp_directory_path();
8480 file.append("/rsocket.")
8481 .append(std::to_string(::getpid()))
8483 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
8485 // --------------------------------------------------------------------------
8486 // Open temporary file.
8487 // --------------------------------------------------------------------------
8488 FILE* fp = fopen(file.c_str(), "w+");
8489 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);
8490 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
8491 if (fputs(pem_data, fp) == EOF) {
8493 randolf::rex::mk_exception("Cannot cannot write to temporary file (to use private key) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8494 } // -x- if !fputs -x-
8497 // --------------------------------------------------------------------------
8498 // Attempt to load private key file, but save the error code for later
8499 // because we need to clean up the temporary file before possibly throwing an
8501 // --------------------------------------------------------------------------
8502 int rc = SSL_CTX_use_PrivateKey_file(__tls_ctx, file.c_str(), SSL_FILETYPE_PEM);
8504 // --------------------------------------------------------------------------
8505 // Overwrite the contenst of the temporary file before deleting it so as to
8506 // sabotage a simple attempt to undelete the file and access the certificate.
8508 // We're also re-using the "len" local variable because it's not needed once
8509 // we get the loop started, and it's more optimal to not allocate yet another
8510 // local variable while "len" goes to waste. :D
8511 // --------------------------------------------------------------------------
8512 if (random_fill) { // This option is configurable
8513 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
8514 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
8515 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
8516 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
8517 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
8519 } // -x- if randfill -x-
8520 fchmod(fileno(fp), 0); // Remove all permissions
8521 fclose(fp); // Close file handle
8522 unlink(file.c_str()); // Delete temporary file
8524 // --------------------------------------------------------------------------
8525 // Error check ... was delayed here until after temporary file cleanup.
8526 // --------------------------------------------------------------------------
8527 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);
8530 }; // -x- rsocket* tls_ctx_use_privatekey_pem -x-
8532 /*======================================================================*//**
8534 Initiate the TLS handshake with the endpoint (which is presumed to be a
8536 This method makes it easier to support application-level commands such as @c
8537 STARTTLS (which are implemented in protocols like SMTP, POP3, IMAP4, MEOW,
8538 FTP, NNTP, LDAP, XMPP, etc.).
8540 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8541 OpenSSL library doesn't document which errors may be returned)
8543 @returns The same rsocket object so as to facilitate stacking
8545 @see connect(std::string, int)
8548 *///=========================================================================
8549 rsocket* tls_do_handshake() {
8550 if (__debug) debug("tls_handshake(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8552 __rc_check_tls(SSL_do_handshake(__tls_fd));
8554 }; // -x- rsocket* tls_do_handshake -x-
8556 /*======================================================================*//**
8558 Get OpenSSL's TLS structure.
8559 @returns TLS structure
8560 @returns nullptr = TLS structure not yet allocated
8562 *///=========================================================================
8563 const SSL* tls_fd() noexcept {
8565 }; // -x- int tls_fd -x-
8567 /*======================================================================*//**
8569 Return the current @ref rsocket_sni object that this @c rsocket will use when
8570 accepting incoming encrypted connections.
8571 @returns Pointer to @c rsocket_sni object
8572 @returns nullptr = SNI is not assigned
8574 @see tls_sni(rsocket_sni*)
8576 *///=========================================================================
8577 rsocket_sni* tls_sni() noexcept {
8579 }; // -x- rsocket_sni* tls_sni -x-
8581 /*======================================================================*//**
8583 Set the current @ref rsocket_sni object that this @c rsocket will use when
8584 accepting incoming encrypted connections.
8586 Use the @ref name() method to find out which server name was supplied by the
8587 endpoint that triggered the SNI callback, regardless of whether it matches
8588 any of the TLS certificates used with this rsocket object or the rsocket_sni
8589 object that's associated with this rsocket object. If an SNI callback wasn't
8590 triggered, or if the endpoint didn't provide a server name, then it will
8591 remain unaffected (and the default {empty string} will remain unchanged).
8592 @returns The same rsocket object so as to facilitate stacking
8595 @see is_tls_sni_match
8597 *///=========================================================================
8599 /// Pointer to the @ref rsocket_sni object to use, or specify @c nullptr to
8600 /// remove SNI support from this rsocket object
8601 rsocket_sni* sni) noexcept {
8603 // --------------------------------------------------------------------------
8604 // Remove SNI support.
8605 // --------------------------------------------------------------------------
8606 if (sni == nullptr) {
8607 if (__tls_sni != nullptr) SSL_CTX_set_client_hello_cb(__tls_ctx, nullptr, this);
8609 // --------------------------------------------------------------------------
8610 // Add or set SNI support.
8611 // --------------------------------------------------------------------------
8613 SSL_CTX_set_client_hello_cb(__tls_ctx, tls_sni_callback, this); // Configure SNI callbacks for TLS.
8614 //SSL_CTX_set_tlsext_servername_callback(__tls_ctx, tls_sni_callback); // Don't use this anymore; it's outdated
8615 //SSL_CTX_set_tlsext_servername_arg(__tls_ctx, this); // Don't use this anymore; it's outdated
8617 } // -x- if !sni -x-
8619 // --------------------------------------------------------------------------
8620 // Update internal pointer to the SNI map.
8621 // --------------------------------------------------------------------------
8625 }; // -x- rsocket_sni* tls_sni -x-
8628 /*======================================================================*//**
8630 Get OpenSSL's TLS structure.
8631 @returns TLS structure
8632 @returns nullptr = TLS structure not yet allocated
8634 *///=========================================================================
8635 int static tls_sni_callback(
8636 /// OpenSSL's socket descriptor/handle
8638 /// Where to store the @c alert value
8640 /// Context-specific argument
8641 void* arg) noexcept {
8643 // --------------------------------------------------------------------------
8644 // Internal variables.
8645 // --------------------------------------------------------------------------
8646 rsocket* r = (rsocket*)arg; // We may be updating the TLS context (this is normally the new client's rsocket object)
8647 const unsigned char* out = nullptr;
8648 size_t out_size = 0;
8650 // --------------------------------------------------------------------------
8651 // Obtain a pointer to newly-allocated ClientHello fields data. If *out is
8652 // nullptr, it means that no TLSEXT_TYPE_server_name was received from the
8653 // client, and so the default TLS context will suffice. If out_size is less
8654 // than or equal to 2, then it also won't have what we need.
8655 // --------------------------------------------------------------------------
8656 if (!SSL_client_hello_get0_ext(tls_fd, TLSEXT_TYPE_server_name, &out, &out_size)
8657 || out == nullptr || out_size <= 2) return SSL_CLIENT_HELLO_SUCCESS; // 1
8659 // --------------------------------------------------------------------------
8660 // Scan for TLSEXT_NAMETYPE_host_name, but if we can't find it then we'll
8661 // just leave the default TLS context as is.
8662 // --------------------------------------------------------------------------
8663 unsigned char* p = (unsigned char*)out;
8664 size_t len = (*(p++) << 8);
8666 if (len + 2 != out_size) goto finish;
8668 // --------------------------------------------------------------------------
8669 // We're taking a shortcut by examining only the first element in the list,
8670 // but in the future we need to make this more robust in case other types of
8671 // elements precede what we're looking for.
8673 // Unfortunately, there's no documentation that properly-explains the format
8674 // of the list, so some deeper research into OpenSSL's source code will be
8675 // needed (a cursory look so far has not yielded the necessary insight).
8677 // TODO: Turn this into a loop that supports future clients that provide
8678 // multiple SNI server names in their requests. (Although this isn't
8679 // occuring at present with common end-user tools such as web browsers
8680 // and eMail software, it may happen in the future as client/server
8681 // software becomes more savvy.)
8682 // --------------------------------------------------------------------------
8683 if (out_size == 0 || *p++ != TLSEXT_NAMETYPE_host_name) goto finish;
8685 // --------------------------------------------------------------------------
8686 // Avoid buffer overrun caused by corrupt or prematurely-truncated data.
8687 // --------------------------------------------------------------------------
8688 if (--out_size <= 2) goto finish;
8690 // --------------------------------------------------------------------------
8691 // Extract and use the hostname (SNI server name) that was supplied by the
8692 // endpoint so that the correct TLS certificate can be selected and assigned.
8693 // --------------------------------------------------------------------------
8694 len = (*(p++) << 8);
8696 if (!(len + 2 > out_size)) { // Only process SNI server name string if it's at least 2 bytes long
8697 // Debug: std::cout << "Server name: " << (const char*)p << std::endl; // Debug
8699 // --------------------------------------------------------------------------
8700 // Obtain the correct TLS context (wildcards supported) that is associated
8701 // with the hostname (SNI server name) that was supplied by the endpoint.
8702 // --------------------------------------------------------------------------
8703 const char* sni_name = (const char*)p;
8704 SSL_CTX* new_ctx = r->__tls_sni->get_ctx(sni_name, true, r->tls_ctx());
8706 // --------------------------------------------------------------------------
8707 // Change TLS context so that encryption with the endpoint uses the correct
8708 // TLS certificate (otherwise the endpoint will indicate security risk errors
8709 // to end users, log files, etc., and may {should} reject the connection).
8710 // --------------------------------------------------------------------------
8711//std::cout << "SNI setup... " << sni_name << std::endl;
8712 SSL_set_SSL_CTX(tls_fd, new_ctx);
8713//std::cout << "---1--- " << r->__tls_new_endpoint << std::endl;
8714 r->__tls_new_endpoint->tls_ctx(new_ctx);
8715//std::cout << "---2---" << std::endl;
8716 r->__tls_new_endpoint->__name_sni.assign(sni_name);
8717//std::cout << "SNI name() = " << r->__tls_new_endpoint->name() << std::endl;
8718 r->__tls_new_endpoint->__tls_sni_match = true;
8720 } // -x- if len+2 -x-
8723 // --------------------------------------------------------------------------
8724 // Free the ClientHello fields data, then return SSL_CLIENT_HELLO_SUCCESS.
8725 // Even if we encountered a problem with the ClientHello fields data, we
8726 // still return SSL_CLIENT_HELLO_SUCCESS so that the TLS context will be
8727 // accepted as valid.
8729 // If we return SSL_CLIENT_HELLO_ERROR the connection will fail unnecessarily
8730 // for valid TLS certificates. If the ClientHello fields data is malformed
8731 // to a degree that doesn't satisfy OpenSSL, then OpenSSL will reject it, so
8732 // there's really no point in duplicating what OpenSSL already does properly,
8733 // which OpenSSL will pass through the standard error channels with normal
8734 // error details-and-diagnostics anyway.
8735 // --------------------------------------------------------------------------
8736 //OPENSSL_free((char*)out); // OpenSSL documentation says to do this, but it fails and crashes the program
8738 return SSL_CLIENT_HELLO_SUCCESS; // 1
8739 }; // -x- int tls_sni_callback -x-
8742 /*======================================================================*//**
8744 Convert a 48-bit integer to a machine address in the form of @c
8745 xx:xx:xx:xx:xx:xx where every instance of @c xx is a hexadecimal
8746 representation of each respective 8-bit byte portion.
8748 This method is needed because we don't want to bring in the heavy fmt::format
8749 class as a dependency.
8750 @returns Mac address as 17-character in the typical format expected by system
8753 *///=========================================================================
8754 static std::string to_mac(
8755 /// Pointer to 48-bit integer
8756 const void* addr) noexcept {
8758 h.resize(18); // 48-bit mac address needs 6 hexadecimal pairs of nybbles delimited by colons plus a NULL terminator
8759 h.resize(snprintf(h.data(),
8761 "%02x:%02x:%02x:%02x:%02x:%02x",
8767 ((u_char*)addr)[5])); // Convert, and truncate NULL terminator
8769 }; // -x- std::string to_mac -x-
8771 /*======================================================================*//**
8773 Convert a 48-bit integer to a node address in the form of @c xxxx:xxxx:xxxx
8774 where every instance of @c xxxx is a hexadecimal representation of each
8775 respective 16-bit word portion.
8777 This method is needed because we don't want to bring in the heavy fmt::format
8778 class as a dependency.
8779 @returns Node address as 14-character in the typical format expected by
8780 network administrators
8782 *///=========================================================================
8783 static std::string to_node(
8784 /// Pointer to 48-bit integer
8785 const void* addr) noexcept {
8787 h.resize(15); // 48-bit node address needs 3 hexadecimal sets of words delimited by colons plus a NULL terminator
8788 h.resize(snprintf(h.data(),
8791 ((u_int16_t*)addr)[0],
8792 ((u_int16_t*)addr)[1],
8793 ((u_int16_t*)addr)[2])); // Convert, and truncate NULL terminator
8795 }; // -x- std::string to_node -x-
8797 /*======================================================================*//**
8799 Send a formatted string to the @ref rsocket endpoint.
8801 The @c format is described in the documentation for the POSIX or Standard C
8802 Library @c printf() function.
8803 @throws randolf::rex::xEBADF The underlying socket is not open
8804 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
8805 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
8806 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
8807 @throws randolf::rex::xENOMEM Insufficient memory
8808 @returns The same rsocket object so as to facilitate stacking
8810 @see is_eol_fix_printf
8817 *///=========================================================================
8819 /// Format string to use
8821 /// Variadic arguments in @c va_list format
8824 int rc = ::vasprintf(&buf, format, args);
8825 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
8826 if (__eol_fix_printf && !__eol.empty()) {
8827 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
8829 __send(str.c_str(), str.length());
8833 } catch (std::exception& e) { // Free buf then re-throw the exception
8834 ::free(buf); // Prevent memory leak when an exception is thrown
8837 } // -x- if __eol_fix_printf -x-
8839 }; // -x- rsocket* vprintf -x-
8841 /*======================================================================*//**
8843 Send a formatted string to the @ref rsocket endpoint, and append an EoL
8846 The @c format is described in the documentation for the POSIX or Standard C
8847 Library @c printf() function.
8848 @throws randolf::rex::xEBADF The underlying socket is not open
8849 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
8850 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
8851 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
8852 @throws randolf::rex::xENOMEM Insufficient memory
8853 @returns The same rsocket object so as to facilitate stacking
8856 @see is_eol_fix_printf
8863 *///=========================================================================
8864 rsocket* vprintfline(
8865 /// Format string to use
8867 /// Variadic arguments in @c va_list format
8870 int rc = ::vasprintf(&buf, format, args);
8871 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
8872 if (__eol_fix_printf && !__eol.empty()) {
8873 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
8876 __send(str.c_str(), str.length());
8879 __sendline(buf, rc);
8880 } catch (std::exception& e) { // Free buf then re-throw the exception
8881 ::free(buf); // Prevent memory leak when an exception is thrown
8884 } // -x- if __eol_fix_printf -x-
8886 }; // -x- rsocket* vprintfline -x-
8889 /*======================================================================*//**
8890 Track unencrypted bytes received. When the number of bytes is negative or
8891 zero, it isn't recorded.
8892 This is an internal function.
8893 @returns same value provided in @ref n
8895 *///=========================================================================
8896 int __track_bytes_rx(
8897 /// Number of bytes transferred
8899 if (n > 0) __bytes_rx.fetch_add(n, std::memory_order_relaxed);
8901 }; // -x- __track_bytes_rx -x-
8903 /*======================================================================*//**
8904 Track unencrypted bytes transmitted. When the number of bytes is negative or
8905 zero, it isn't recorded.
8906 This is an internal function.
8907 @returns same value provided in @ref n
8909 *///=========================================================================
8910 int __track_bytes_tx(
8911 /// Number of bytes transferred
8913 if (n > 0) __bytes_tx.fetch_add(n, std::memory_order_relaxed);
8915 }; // -x- __track_bytes_tx -x-
8917 /*======================================================================*//**
8918 Track encrypted bytes received. When the number of bytes is negative or
8919 zero, it isn't recorded.
8920 This is an internal function.
8921 @returns same value provided in @ref n
8923 *///=========================================================================
8924 int __track_crypt_rx(
8925 /// Number of bytes transferred
8927 if (n > 0) __crypt_rx.fetch_add(n, std::memory_order_relaxed);
8929 }; // -x- __track_crypt_rx -x-
8931 /*======================================================================*//**
8932 Track encrypted bytes transmitted. When the number of bytes is negative or
8933 zero, it isn't recorded.
8934 This is an internal function.
8935 @returns same value provided in @ref n
8937 *///=========================================================================
8938 int __track_crypt_tx(
8939 /// Number of bytes transferred
8941 if (n > 0) __crypt_tx.fetch_add(n, std::memory_order_relaxed);
8943 }; // -x- __track_crypt_tx -x-
8945 }; // -x- class rsocket -x-
8947}; // -x- namespace randolf -x-
8949// Save this for a future sendlines() methods.
8950// const void* msg_ptr = msg.c_str(); // Prevent repeated calls to c_str() method
8951// const int len = msg.length(); // Prevent repeated calls to length() method