4#include <randolf/rsocket_io>
5#include <randolf/rsocket_sni>
6#include <randolf/rtools>
7#include <randolf/sockaddr_dl.h>
10#include <bit> // std::endian
11#include <cstdarg> // std::va_list
12#include <cstring> // std::strlen
13#include <ctime> // std::strftime
14#include <exception> // std::exception
15#include <filesystem> // std::filesystem::temp_directory_path
16#include <initializer_list>
17#include <iostream> // std::put_date
18#include <map> // std::map
19#include <memory> // std::shared_ptr
20#include <mutex> // std::mutex
23//#include <sstream> // std::ostringstream
24#include <unordered_map> // std::unordered_map
30#include <string.h> // strerror()
35#include <linux/fs.h> // Flags for ioctl()
37#include <net/if.h> // ifreq structure used by bind()
39#include <netinet/icmp6.h>
40#include <netinet/in.h>
42#include <netpacket/packet.h> // struct sockaddr_ll
44#include <openssl/err.h>
45#include <openssl/ossl_typ.h>
46#include <openssl/ssl.h>
49#include <sys/socket.h>
50#include <sys/stat.h> // fchmod()
51#include <sys/time.h> // TIMEVAL_TO_TIMESPEC macro
55static_assert((sizeof(__time_t) * CHAR_BIT) >= 64, "64-bit __time_t is required");
59 /*======================================================================*//**
61 This @ref rsocket class provides an easy-to-use and thoroughy-implemented
62 object-oriented socket I/O interface for C++, intended to make socket I/O
63 programming (with or without TLS encryption) easier and more enjoyable.
65 Here's a short list of benefits that are helpful in developing high quality
66 code that's consistent-and-reliable, and improves overall productivity:
68 - eliminating the need to repeatedly write blocks of code that check for
69 errors, by throwing exceptions instead (see @ref randolf::rex::rex class
70 for details and the long list of exceptions that are supported)
71 - eliminating the need to track socket descriptors
72 - eliminating the need to handle encrypted I/O separately (most functions)
73 - eliminating the need to manage memory for many common structures used to
74 interface with socket options, etc.
75 - eliminating the need to record socket I/O statistics with every call to
76 underlying socket I/O functions (see @ref randolf::rsocket_io for
78 - text-line reading/writing with an adapative approach (invented by
79 Randolf Richardson in the 1980s for a custom BBS software project) to
80 automatically detect EoL (End-of-Line) character sequences (unless the
81 developer provides a specific sequence via an @ref eol method) that can
82 determine whether an endpoint is sending Linux/UNIX (standard), MacOS,
83 DOS CR/LF, or even broken LF/CR (reverse of DOS) EoL sequences
84 - transparent support for encryption with many additional features,
85 including STARTTLS, ingress/egress policy enforcement, and SNI
86 - eliminating the complexity of handling events with poll(), select(), and
87 related functions (see the @ref randolf::rsocket_mux class for details)
88 - providing a variety of other useful features that make it easier to
89 communicate with socket endpoints, such as receiving/sending an entire
90 structure via a single call to the new-and-specialized @ref recv_struct
91 or @ref send_struct methods, respectively
93 An rsocket is either the endpoint that our underlying socket will connect to,
94 or it's the server daemon that our underying socket will listen() to and
95 accept() [inbound] connections from.
99 Using the C interface, the programming must check for errors by testing the
100 response codes (most of which are consistent, with a few subtle outliers),
101 which leads to a lot of additional error-checking code with the potential for
102 unintended errors (a.k.a., bugs). This style is necessary in C, but with C++
103 the way to handle errors is with exceptions, so I created this rsocket class
104 to handle all these tedious details behind-the-scenes and, for any socket
105 errors, to generate exceptions so that source code can be greatly simplified
106 (and, as a result, also easier to read and review).
108 Pre-allocating buffers is also handled internally, which is particularly
109 useful when making repeated calls to recv() and related methods. These
110 methods return std::shared_ptr<@c structure > (a C++ smart pointer that aids
111 in the prevention of resource leaks) or std::vector<char> (resized to the
112 actual number of bytes received) as appropriate which eliminates the need to
113 track @c size_t separately.
116 Lower-case letter "r" is regularly used in partial example code to represent
117 an instantiated rsocket object.
119 An ASCIIZ string is a C-string (char* array) that includes a terminating null
120 (0) character at the end.
122 The following custom qualifiers are incorporated into headings by Doxygen
123 alongside method titles throughout the documentation:
124 - @c POSIX denotes a method that is based on POSIX functions by the same
125 name and don't deviate significantly from the POSIX function arguments
126 (intended to be helpful to developers transitioning to/from rsocket or
127 working on source code that utilizes @ref rsocket and POSIX functions)
128 - @c TLS denotes that a method works properly with TLS-encrypted sockets
129 (most of the POSIX functions have been made to work properly with TLS,
130 but for the few rare cases of functions that can't be made to work with
131 TLS an effort has also been made to mention this using Doxygen's
132 "warning" sections in addition to omitting the TLS qualifier)
134 @par Getting started with a few simple examples
136 This is an example of connecting to an HTTP server, using the "GET" command
137 to request the home page (using HTTP/1.0), then receiving-and-displaying the
138 resulting web page's contents via STDOUT (or sending an error message to
139 STDERR). Finally, we exit with an EXIT_SUCCESS (or EXIT_FAILURE) code.
142 #include <iostream> // std::cout, std::cerr, std::endl, etc.
143 #include <randolf/rex>
144 #include <randolf/rsocket>
146 int main(int argc, char *argv[]) {
148 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
149 r.connect("www.example.com", 80);
150 r.sendline("GET / HTTP/1.0");
151 r.sendline("Host: www.example.com");
152 r.sendline("Connection: close");
154 while (r.is_open()) {
155 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
156 } // -x- while data -x-
158 } catch (const randolf::rex::xALL e) {
159 std::cerr << "Socket exception: " << e.what() << std::endl;
161 } catch (const std::exception e) {
162 std::cerr << "Other exception: " << e.what() << std::endl;
166 } // -x- int main -x-
169 Parameter stacking is supported (with methods that return @c rsocket*); in
170 this example, notice that semicolons (";") and "r." references are omittted
171 (when compared with the above):
174 #include <iostream> // std::cout, std::cerr, std::endl, etc.
175 #include <randolf/rex>
176 #include <randolf/rsocket>
178 int main(int argc, char *argv[]) {
180 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
181 r.connect("www.example.com", 80)
182 ->sendline("GET / HTTP/1.0")
183 ->sendline("Host: www.example.com")
184 ->sendline("Connection: close")
186 while (r.is_open()) {
187 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
188 } // -x- while data -x-
190 } catch (const randolf::rex::xALL e) {
191 std::cerr << "Socket exception: " << e.what() << std::endl;
193 } catch (const std::exception e) {
194 std::cerr << "Other exception: " << e.what() << std::endl;
198 } // -x- int main -x-
203 This is meant to be a comprehensive socket class for C++, which is intended
204 to make socket I/O coding easier for developers with some key features:
206 - easy conversion from C-style POSIX sockets due to API consistency
207 - transparent TLS support (OpenSSL dependency)
208 - keys and certificate chains can also be loaded from memory
209 - SNI support (see the @ref rsocket_sni class for more information)
210 - underlying socket handle is accessible (via the @ref socket_fd() method)
211 - socket options are easier to get and to set
212 - sensible support for future or unknown socket options
213 - errors are presented as ~100 separate exception classes: @ref rex::rex
214 - one parent exception class makes it easier to catch all socket errors
215 - a few exceptions groups are also provided to catch groups of errors
216 - new buffers are returned as std::shared_ptr's to eliminate memory leaks
217 - constructors with sensible defaults help to simplify coding
218 - documentation includes code samples (with @c \#include lines as needed)
219 - debug output with helpful output (and option to set output file handle)
220 - low-overhead is considered (this is why there's a bit more overloading)
221 - thread-safety is noted where it is absolutely available (or where some
222 caution is warranted)
223 - can send ASCIIZ (C-strings) without needing to specify string length
224 - can send @c std::string (which tracks its own string length)
225 - each socket can optionally have a @ref name
227 Additional features that are not part of the typical POSIX standard, but
228 deserve special mention because they are needed so often:
230 - easy access to internal I/O counters (see @ref rsocket_io for details)
231 - your rsocket_io structure can be updated automatically by rsocket's
232 destructor after underlying socket is closed (see @c net_io_final()
234 - printf() // Formatted strings (with automatic EoL sequence substitution)
235 - recvline(), sendline() // ASCII text line I/O operations
236 - class-wide configurable newline sequence (defaults to @e autodetect)
237 - recv_asciiz(), send_asciiz() // ASCIIZ string I/O operations
238 - recv_byte() , send_byte() // 8-bit byte operations (LSB/MSB is N/A)
239 - recv_struct(), send_struct() // Multi-byte operations
240 - recv_uint16(), send_uint16() // 16-bit operations with LSB/MSB variants
241 - recv_uint32(), send_uint32() // 32-bit operations with LSB/MSB variants
242 - recv_uint64(), send_uint64() // 64-bit operations with LSB/MSB variants
244 Some advanced features are planned that exceed what the basic socket I/O
245 functions provide, but are also needed:
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)
253 - recv_uint128()/send_uint128() // 128-bit operations w/ LSB/MSB variants
254 - recv_uint(n)/send_uint(n) where "n" specifies the number of bits (which
255 must be a multiple of 8), with LSB/MSB variants
256 - auto-detection of inbound TLS connection (this is turned off by default)
257 - This is not the same as STARTTLS (an application-level command, for
258 which the @ref tls_do_handshake() method will likely be used)
259 - simple timing tracking options using timing_start() and timing_stop()
260 methods, the results of which can be retrieved with timing_get() or a
261 similarly-named group of methods
263 Other features that are not a high priority:
265 - internal support for portability to MS-Windows (this is a big headache
266 that I know will be time-consuming because Windows Sockets is not the
267 same as the standard POSIX sockets APIs that are used by Linux, UNIX,
268 MacOS, and pretty much all other Operating Systems. This means that
269 this really isn't a high priority for me without sufficient demand and
270 sufficient funding so I can commit my time without missing mortgage
271 payments, student loan payments {for my kids}, living expenses, etc.)
275 I use the term "ASCIIZ string" to indicate an array of characters that's
276 terminated by a 0 (a.k.a., null). Although this is very much the same as a
277 C-string, the difference is that in many API functions a C-string must often
278 be accompanied by its length value. When referring to an ASCIIZ string, I'm
279 intentionally indicating that the length of the string is not needed because
280 the string is null-terminated. (This term was also commonly used in assembly
281 language programming in the 1970s, 1980s, and 1990s, and as far as I know is
282 still used by machine language programmers today.)
284 UTF-8 works without any problems as it is backward-compatible to 8-bit ASCII,
285 and because @c std::string uses 8-bit bytes to store strings internally. Do
286 keep in mind that the manipulation of UTF-8 substrings will require working
287 with UTF-8 codepoints that may consume one or more bytes, which is beyond the
288 scope of the impartial (to UTF-8 and ASCII) functionality that this rsocket
291 UTF-8 newline codepoints NEL (c2 85), LS (e2 80 a8), and PS (e2 80 a9) garner
292 no special handling at this time - unlike CR/LF (0d/0a) - and are merely
293 treated as non-newline codepoints. There is a possibility of adding support
294 for this in the future, but additional research and planning is required to
295 make sure this works properly. What is most likely is that some UTF-8 flags
296 will be added to support each of these (which will probably be disabled by
297 default) that will be integrated into the readline() methods. This also
298 depends on how widely used these particular codepoints are used, and pending
299 further research to determine whether these really are supposed to be used
300 functionally as newlines...
302 Examples of two UTF-8 codepoints that are not functional, but which a few
303 people have mistakenly assumed are functional:
304 - <sup>C</sup><sub>R</sub> = superscript "C" with subscript "R" (e2 90 9d)
305 - <sup>N</sup><sub>L</sub> = superscript "N" with subscript "L" (e2 90 a4)
307 The above are designed as special characters to represent the Carriage-Return
308 and New-Line respectively on ASCII character reference charts, which were
309 used primarily by IBM in the 1970s and 1980s for instruction manuals and
310 other documentation, and on some keyboard overlays.
314 I created this class to make it easier to write internet server daemons. I
315 started out using C-style socket functions (because C++ doesn't come with a
316 socket class), but I found that I didn't enjoy mixing something as important
317 and detailed as socket I/O in a procedural way into the object-oriented
318 paradigm that C++ provides.
320 After looking for existing solutions (none of which served as comprehensive
321 replacements for socket I/O), I embarked on creating the rsocket class, and
322 then I began to understand why this probably hadn't been done -- it's a
323 massive untertaking, primarily because there are a lot of functions that are
324 needed to handle socket I/O. Further, [at the time of this writing] the @c
325 sockaddr_storage structure wasn't as widely used as it should be, and so
326 information about it tended to be scarce, incomplete, or incorrect (further
327 research was required to understand this properly, which was worthwhile).
329 Moving error codes into exceptions is also a major effort because they are so
330 diverse and plentiful, and there are so many errors that can occur at various
331 stages for many different reasons. There are also a few outlier functions
332 that require slightly different approaches to error handling due to subtly
333 different rules for handling their errors, and so the exception-generation
334 wasn't as straight-forward as one might optimistically expect, but this is
335 one of the many benefits of the object-oriented programming pardigm because a
336 handling edge cases internally results in a consistent error-handling
337 interface using exceptions that also simplifies the source code. (Need to
338 handle a specific set of conditions? Catch the relevant exceptions for those
339 cases in an inner set of exceptions, and just catch all the others in a more
340 general way without the added complexity of repeatedly checking for errors
341 every step along the way.)
343 So, I dedicated time to make this work, and with the intention of making it
344 an open source project once I got it into a state that's ready for the
345 general public. This required putting my other C++ projects on hold, which
346 was fine because they didn't have strict deadlines and using this socket
347 class in them will speed up development in the long-term anyway, so it's
348 clearly worth the effort to me.
350 My background in programming began when I was a young child, teaching myself
351 BASIC and then machine language (when I found BASIC to be too limited) before
352 moving on to other languages like Perl and Java many years later. Eventually
353 I circled around to C (which I chose to learn the hard way by writing some
354 PostgreSQL extensions) and then C++ a few years after that. I have a lot of
355 experience with socket communications, including fully-featured DNS resolver
356 library code in machine language for Novell's NetWare that used C calling
357 conventions and supported varargs, which worked well for the few developers
358 who needed or wanted it.
361 - 2022-Nov-09 v1.00 Initial version
362 - 2022-Nov-26 v1.00 Moved exception handling to a separate class
363 - 2022-Nov-28 v1.00 Completed readline/send functionality
364 - 2022-Dec-03 v1.00 Added endianness transparency
365 - 2022-Dec-04 v1.00 Added printf() support
366 - 2022-Dec-24 v1.00 Added socket MUXing
367 - 2023-Feb-22 v1.00 Added TLS/SSL support
368 - 2023-Mar-10 v1.00 Added TLS-SNI support
369 - 2023-Apr-19 v1.00 Added TLS certificate loading from memory
370 - 2023-May-24 v1.00 Added support for clang++ compilation
371 - 2023-Jun-09 v1.00 Improvements to dynamic internal read buffering
372 - 2023-Oct-31 v1.00 Improvements to various classes
373 - 2024-Feb-21 v1.00 Added is_buffered() method
374 - 2024-Mar-31 v1.00 Completed @ref recvline (implemented a work-around for
375 a subtle SSL_peek failure to extract additional data
376 when a user at an end-point is communicating with
377 "icanon" mode enabled)
378 - 2024-May-24 v1.00 Changed source code to accomodate @c clang++ compiler
379 - 2024-Jun-04 v1.00 Added @c POSIX and @c TLS qualifiers to all applicable
380 methods (Doxygen incorporates into the documentation)
382 @author Randolf Richardson
384 *///=========================================================================
387 // --------------------------------------------------------------------------
388 // The rsocket_group class needs access to some of our internal variables.
389 // --------------------------------------------------------------------------
390 friend class rsocket_group;
392 // --------------------------------------------------------------------------
394 // --------------------------------------------------------------------------
396 int __socket_type = 0;
397 int __socket_protocol = 0;
398 struct sockaddr_storage __socket_addr = {}; // Initialize to all elements to their default values
399 socklen_t __socket_addr_size = sizeof(__socket_addr); // We need to point to this (and it might be modified)
400 int __socket_backlog = SOMAXCONN; // Default backlog is 4096 on Linux, and 128 on older systems
402 // --------------------------------------------------------------------------
403 // Socket flags (internal, but with get-methods to provide read-only access).
404 // --------------------------------------------------------------------------
405 std::atomic_bool __socket_open = false; // Socket is open by way of socket() function
406 std::atomic_bool __socket_connected = false; // Socket is connected
408 // --------------------------------------------------------------------------
409 // TLS flags and variables.
410 // --------------------------------------------------------------------------
411 std::atomic_bool __tls = false; // Indicates whether socket I/O is encrypted with OpenSSL
412 SSL_CTX* __tls_ctx = nullptr; // TLS context (nullptr == no encryption initialized)
413 rsocket_sni* __tls_sni = nullptr; // SNI maps
414 bool __tls_sni_match = false; // Set to TRUE only if SNI matched in the SNI callback
415 SSL* __tls_fd = nullptr; // TLS connection structure (nullptr == no connection)
416 BIO* __tls_rbio = nullptr; // TLS BIO input stream
417 BIO* __tls_wbio = nullptr; // TLS BIO output stream
418 bool __tls_exclusive = false; // See enum TLS_FLAGS::TLS_EXCLUSIVE
419 bool __tls_egress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_EGRESS
420 bool __tls_ingress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_INGRESS
421 bool __tls_server_mode = true; // See enum TLS_FLAGS::TLS_SERVER
422 inline static std::atomic_int __fn_counter = 0; // Used in generating unique-and-threadsafe temporary filenames
423 rsocket* __tls_new_endpoint = nullptr; // Weak pointer to newly-spawned endpoint's rsocket object
425 // --------------------------------------------------------------------------
426 // Keep track of test for whether this host stores integers using big endian.
427 // --------------------------------------------------------------------------
428 inline static const bool __endian_is_msb = htons(42) == 42; // TRUE==MSB / FALSE==LSB
430 // --------------------------------------------------------------------------
431 // General variables. Atomic variables are used for variables as needed to
432 // support operations in a thread-safe manner.
433 // --------------------------------------------------------------------------
434 std::atomic_bool __debug = false;
435 std::atomic<std::FILE*> __debug_fd = stderr;
436 std::string __debug_prefix = "rsocket-debug";
437 #define RSOCKET_BUFFER_SIZE 1024 // Default buffer size
438 size_t __buffer_size = RSOCKET_BUFFER_SIZE; // Buffer size used by recv() when a buffer size isn't provided
439 char* __buffer = nullptr; // Initialized automatically when recvline() is used
440 char* __buffer_head = nullptr; // Newest data in buffer (a pointer is faster than calculating offsets)
441 char* __buffer_tail = nullptr; // Oldest data in buffer (a pointer is faster than calculating offsets)
442 char* __buffer_boundary = nullptr; // Size-wrapped boundary (a pointer is faster than calculating lengths)
443 std::string __name; // Arbitrary name of this rsocket (used by some applications)
444 std::string __name_sni; // SNI hostname (when tls_sni is configured)
446 // --------------------------------------------------------------------------
447 // EoL sequence variables.
448 // --------------------------------------------------------------------------
449 static const char __CR = (char)13; // CTRL-M / ASCII 13 (0Dh) / "\r" / Used by eol() methods
450 static const char __LF = (char)10; // CTRL-J / ASCII 10 (0Ah) / "\n" / Used by eol() methods
451 static constexpr const char* __CRLF = "\x0d\x0a"; // Used by eol() methods
452 std::string __eol; // Used by recvline() method; maintained by eol() methods
453 bool __eol_adoption = true; // Whether to adopt the dynamically detected EoL sequence when __eol is empty (the default)
454 std::string __eol_out = std::string(__CRLF); // Used by sendline() method; maintained by eol() methods; also used by sendline() method
455 bool __eol_fix_printf = true; // Whether to replace "\n" sequence in printf() methods to EoL sequence
457 // --------------------------------------------------------------------------
458 // Fine-tuning for recvline(). This is used with nanosleep().
459 // --------------------------------------------------------------------------
460 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
461 long __recvline_timeout = 0; // Number of seconds (0 = unlimited; maximum for "long" equates to approximately 292 billion years since the UNIX/Linux epoch)
463 // --------------------------------------------------------------------------
464 // Statistical variables.
465 // --------------------------------------------------------------------------
466 rsocket_io* __io_final_addr = nullptr; // Used by ~rsocket() destructor, net_io_final(), and net_io_update()
467 std::atomic_ulong __bytes_rx = 0; // Total number of bytes received
468 std::atomic_ulong __bytes_tx = 0; // Total number of bytes transmitted
469 std::atomic_ulong __crypt_rx = 0; // Total number of encrypted bytes recevied
470 std::atomic_ulong __crypt_tx = 0; // Total number of encrypted bytes transmitted
473 /*======================================================================*//**
475 Optional flags used with various methods to determine whether they will throw
476 an @ref randolf::rex::xETIMEDOUT exception or merely return a @c nullptr, an
477 empty set, or a 0 (zero) when a timeout duration elapses.
480 You'll know when this is an option because the method will support this.
481 *///=========================================================================
482 enum TIMEOUT_BEHAVIOUR: bool {
484 /*----------------------------------------------------------------------*//**
485 Indicate that an @ref randolf::rex::xETIMEDOUT exception should be thrown
486 when the timeout duration elapses.
487 *///-------------------------------------------------------------------------
488 TIMEOUT_EXCEPTION = true,
490 /*----------------------------------------------------------------------*//**
491 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
492 when the timeout duration elapses.
493 *///-------------------------------------------------------------------------
494 TIMEOUT_EMPTY = false,
496 /*----------------------------------------------------------------------*//**
497 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
498 when the timeout duration elapses.
499 *///-------------------------------------------------------------------------
500 TIMEOUT_NULL = false,
502 /*----------------------------------------------------------------------*//**
503 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
504 when the timeout duration elapses.
505 *///-------------------------------------------------------------------------
506 TIMEOUT_ZERO = false,
508 }; // -x- enum TIMEOUT_BEHAVIOUR -x-
510 /*======================================================================*//**
512 Optional flags used with rsocket's @ref tls() and @ref tls_ctx() methods to
513 specify relevant policies/semantics.
514 *///=========================================================================
515 enum TLS_FLAGS: int {
517 /*----------------------------------------------------------------------*//**
518 The TLS_DEFAULT flag isn't necessary, but it's included here for completeness
519 as it accomodates programming styles that prefer to emphasize when defaults
520 are being relied upon.
521 *///-------------------------------------------------------------------------
524 /*----------------------------------------------------------------------*//**
525 Only encrypted connections are permitted, initially, and all attempts to
526 begin with unencrypted connections will consistently fail.
528 Encrypted connections must begin with a cryptographic handshake packet, or
529 else the connection will be rejected as due to being reasonably assumed to be
532 If this flag isn't set, an application-level mechanism can be used to upgrade
533 to TLS encryption (this is common with connections that begin unencrypted,
534 and issue a proprietary command {such as the @c STARTTLS command in SMTP} to
535 upgrade to TLS later on {of which the @ref tls() method is used to affect an
536 ingress; see the @ref TLS_NO_INGRESS flag for additional information}).
538 Creating an exclusively @c unencrypted connection is accomplished by not
541 @see TLS_NO_EGRESS to prevent support for application-level downgrades to
542 unencrypted connections
543 *///-------------------------------------------------------------------------
546 /*----------------------------------------------------------------------*//**
547 Mid-stream upgrades to encrypted connections are not permitted (e.g., via
548 application-level initiations like the @c STARTTLS command as seen in SMTP),
549 which will also cause calls to the @ref tls() method to fail fast after plain
550 non-encrypted data has already been sent or received.
552 This flag is not necessary when the @ref TLS_EXCLUSIVE flag is set.
553 @see is_tls_ingress_okay()
554 @see tls_do_handshake()
556 *///-------------------------------------------------------------------------
559 /*----------------------------------------------------------------------*//**
560 Mid-stream downgrades to unencrypted connections are not permitted (e.g., via
561 application-level initiations like a hypothetical @c STOPTLS command as seen
562 in FTPS), which will also cause calls to the @ref tls() method to fail fast
563 after encrypted data has already been sent or received.
565 This flag may be combined with the @ref TLS_EXCLUSIVE flag to prevent a
566 connection from being downgraded programatically.
568 Although egress to an unencrypted connection doesn't occur automatically
569 (since egress can only be affected programatically to support commands at the
570 application level), this flag is useful to prevent third-party code from
571 downgrading an encrypted @ref rsocket to unencrypted.
573 Supporting unencrypted communications is strongly discouraged over public
574 networks (e.g., the internet) because unencrypted streams are trivially
575 susceptible to man-in-the-middle attacks that can alter the contents of the
576 data in both directions (which is a particularly dangerous prospect for
577 sending/receiving sensitive information).
578 @see is_tls_egress_okay()
580 *///-------------------------------------------------------------------------
583 /*----------------------------------------------------------------------*//**
584 This is a convenience flag that provides an option for developers to be more
585 clear in their use of the @ref tls() and @ref tls_ctx() methods to indicate
586 intent to rely on what is already the default.
587 @see tls_do_handshake()
589 *///-------------------------------------------------------------------------
592 /*----------------------------------------------------------------------*//**
593 Indicates that this rsocket will be for a server daemon, and to initialize a
594 new TLS context (when one isn't being provided) using OpenSSL's
595 @c TLS_server_method() function instead of OpenSSL's @c TLS_client_method()
596 function (the latter is the default because most code is anticipated to be
600 Setting the CLIENT/SERVER flag incorrectly will typically cause the following
601 error that's difficult to track down, which is usually triggered by calling
602 @ref accept, @ref accept4(), or @ref connect(), and so I hope that including
603 this information here in this documentation will be helpful:
605 error:140C5042:SSL routines:ssl_undefined_function:called a function you should not call
608 The absence of this flag has the same effect as specifying the @ref
611 *///-------------------------------------------------------------------------
614 }; // -x- enum TLS_FLAGS -x-
617 /*======================================================================*//**
618 Return Code check, and throws an rsocket-specific exception if an error
619 occurred, which is indicated by @c -1 (a lot of example code shows @c < @c 1
620 comparisons, but we specifically test for @c -1 because the documentation
621 clearly states @c -1. This is important because a system that can support an
622 extremely high number of socket handles might, in theory, assign handles with
623 values that get interpreted as negative integers, and so less-than-zero tests
624 would result in dropped packets or dropped sockets (any such socket code that
625 allocates such handles obviously must not ever allocate @c -1 since this
626 would definitely be misinterpreted as an error).
628 If rc is not @c -1, then it is simply returned as is.
630 If n is nonzero and rc is 0, then n will override errno. (This option is
631 provided to accomodate the few socket library functions that return values
632 that are never errors, and expect the developer to rely on other means of
633 detecting whether an error occurred. This is an example of where Object
634 Oriented Programming is helpful in making things better.)
635 @returns Original value of @c rc (if no exceptions were thrown)
636 *///=========================================================================
637 const int __rc_check(
640 /// Override @c errno (if not 0)
642 /// Exception handling flags (see @ref rex::REX_FLAGS for details)
643 const int flags = rex::rex::REX_FLAGS::REX_DEFAULT) {
644 if (rc == -1 || (rc == 0 && n != 0)) {
645 const int socket_errno = n == 0 ? errno : n; // If "n" is not zero, use it instead
646 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
647 randolf::rex::mk_exception("", socket_errno, flags); // This function doesn't return (this code ends here)
650 }; // -x- int __rc_check -x-
652 /*======================================================================*//**
653 Similar to @ref __rc_check(), but specialized for handling OpenSSL return
655 *///=========================================================================
656 const int __rc_check_tls(
657 /// Return code (from OpenSSL's API functions)
660 if (__debug) debug("__rc_check_tls(" + std::to_string(rc) + ") throwing exception"); // TODO: Remove this
661 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)
664 }; // -x- int __rc_check -x-
666 /*======================================================================*//**
667 Internal function that opens the socket. This is used by the constructors
668 and their accompanying socket() methods.
670 Throws randolf::rex::xEALREADY exception if the socket is already open to
671 prevent multiple calls to @c open, which can be particularly problematic if
672 any of the additional function calls specify different parameters -- this
673 helps developer(s) to avoid unexpected results if they're inadvertently using
674 the same rsocket object when they intend to use different rsocket objects.
675 *///=========================================================================
677 /// Communication domain; usually one of:@n
679 /// AF_INET6 (IPv6)@n
680 /// AF_UNIX (UNIX domain sockets)
682 /// Communication semantics; usually one of:@n
683 /// SOCK_STREAM (common for TCP)@n
684 /// SOCK_DGRAM (common for UDP)
686 /// Network protocol; usually one of:@n
690 const int protocol) {
691 if (__debug) debug("socket(" + std::to_string(family)
692 + ", " + std::to_string(type)
693 + ", " + std::to_string(protocol)
696 // --------------------------------------------------------------------------
698 // --------------------------------------------------------------------------
699 if (__socket_open) throw randolf::rex::xEALREADY("EALREADY: Socket is already open"); // Socket has already been opened
700 if (family == AF_UNSPEC) throw randolf::rex::xEAFNOSUPPORT("EAFNOSUPPORT: AF_UNSPEC family (SO_DOMAIN) is not supported by socket()");
702 // --------------------------------------------------------------------------
703 // Build minimum parts of __socket_addr structure.
704 // --------------------------------------------------------------------------
705 __socket_addr.ss_family = family;
706 __socket_type = type;
707 __socket_protocol = protocol;
709 // --------------------------------------------------------------------------
710 // Create new socket handle, and save it, then set internal variable to
711 // indicate that the socket is open.
712 // --------------------------------------------------------------------------
713 __socket_fd = __rc_check(::socket(__socket_addr.ss_family, __socket_type, __socket_protocol));
714 __socket_open = true;
716 }; // -x- void __socket -x-
719 /*======================================================================*//**
721 Instantiate an empty rsocket without opening said socket, and without
722 throwing any exceptions.
725 Instantiating an empty rsocket is particularly useful for header-file
726 definitions since exceptions can't be handled outside of subroutines, and
727 it's also useful for enabling debug() mode @em before setting the socket's
728 configuration with one of the socket() methods; for example:
730 randolf::rsocket r; // Empty rsocket (empty / incomplete initialization)
731 r.debug(true); // Enable debug mode
732 r.socket(...); // Required to complete rsocket initialization
737 The built-in defaults, when not provided, are as follows ("family" is also
738 known as the "communication domain"):
739 - @c family = AF_INET
740 - @c type = SOCK_STREAM
741 - @c protocol = PF_UNSPEC
743 You will need to use one of the socket(...) methods to specify socket details
744 after defining rsocket objects with empty constructors so that you can catch
745 runtime exceptions. (This also provides you with an option to enable debug
746 mode during runtime prior to attempting to open an rsocket.)
751 #include <iostream> // std::cout, std::cerr, std::endl, etc.
752 #include <randolf/rex>
753 #include <randolf/rsocket>
755 randolf::rsocket r; // <-- Empty rsocket initialization (no exceptions)
757 int main(int argc, char *argv[]) {
759 r.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Required to complete rsocket initialization
760 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
761 // ... other socket I/O operations
763 } catch (const randolf::rex::xALL e) {
764 std::cerr << "Socket exception: " << e.what() << std::endl;
766 } catch (const std::exception e) {
767 std::cerr << "Other exception: " << e.what() << std::endl;
771 } // -x- int main -x-
777 @see socket_protocol()
780 *///=========================================================================
781 rsocket() noexcept {}; // -x- constructor rsocket -x-
783 /*======================================================================*//**
785 Instantiate an rsocket based on a minimal subset of the settings in the
786 specified rsocket (using it as a template), without actually opening a
787 socket, and therefore also without throwing any exceptions.
790 This constructor does not suffice as a full clone()-like operation, and is
791 minimal because it's used internally by the @ref accept() and @ref accept4()
794 Details that are absorbed from the template/source rsocket (which eliminates
795 the need to assign, set, and configure various parameters (TLS and TLS SNI
796 parameters will be copied in a passive way by default):
797 - Socket family (SO_DOMAIN)
798 - Socket type (SO_TYPE)
799 - Socket protocol (SO_PROTOCOL)
800 - TLS details (status, context {which includes loaded certificates},
801 policies specified with @ref TLS_FLAGS, etc.)
803 - TLS context (you'll still need to call `tls_ctx(r->tls_ctx())`)
804 - TLS SNI map (you'll still need to call `tls_sni(r->tls_sni())`)
807 The TLS Context will not be initialized because it needs a real socket to
808 draw from. If using TLS, you'll need to use the @ref tls() method after
809 the underlying socket has been initiated.
811 When @c flag_create_socket is set to FALSE, no exceptions will be thrown.
817 @see socket_protocol()
820 *///=========================================================================
822 /// Source rsocket object to use as a template to absorb settings from
823 const rsocket* rtemplate,
824 /// TRUE = create a new socket handle (default)@n
825 /// FALSE = don't create a new socket because a new one will be assigned or
826 /// created later (all variants of the @ref accept() methods do this)
827 const bool flag_create_socket = true) {
829 // --------------------------------------------------------------------------
830 // General socket variables.
831 // --------------------------------------------------------------------------
832 if (flag_create_socket) {
833 __socket(rtemplate->__socket_addr.ss_family,
834 rtemplate->__socket_type,
835 rtemplate->__socket_protocol);
836 } else { // !flag_create_socket
837 __socket_addr.ss_family = rtemplate->__socket_addr.ss_family;
838 __socket_type = rtemplate->__socket_type;
839 __socket_protocol = rtemplate->__socket_protocol;
840 } // -x- if flag_create_socket -x-
841 __name = rtemplate->__name;
843 // --------------------------------------------------------------------------
844 // TLS and SNI settings, but not whether TLS is enabled.
845 // --------------------------------------------------------------------------
846 __tls_ctx = rtemplate->__tls_ctx;
847 __tls_sni = rtemplate->__tls_sni;
849 // --------------------------------------------------------------------------
851 // --------------------------------------------------------------------------
852 __eol.assign( rtemplate->__eol); // Copy source std::string's contents (not a reference)
853 __eol_fix_printf = rtemplate->__eol_fix_printf;
855 }; // -x- constructor rsocket -x-
857 /*======================================================================*//**
859 Instantiate an rsocket with IP/host address and [optional] port number.
861 This is either the endpoint that our underlying socket will be connecting to,
862 or it's the local address of the server daemon that our socket will listen()
863 to and accept() inbound connections from.
867 The built-in defaults, when not provided, are as follows ("family" is also
868 known as the "communication domain"):
869 - @c family = AF_INET
870 - @c type = SOCK_STREAM
871 - @c protocol = PF_UNSPEC
873 The socket() methods do the same work as the constructors with matching
874 arguments, and are provided as convenience methods intended to augment
875 empty rsocket constructors used in header files, but do require an address to
876 be specified (for protocols that need port numbers, such as TCP or UDP, a
877 "port" number also needs to be specified since the default port 0 will result
878 in the dynamic allocation of a port number by the system).
880 For UNIX domain sockets use family AF_UNIX, type SOCK_STREAM, and protocol
881 IPPROTO_IP when instantiating or opening an rsocket.
886 #include <iostream> // std::cout, std::cerr, std::endl, etc.
887 #include <randolf/rex>
888 #include <randolf/rsocket>
890 int main(int argc, char *argv[]) {
892 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Same as socket() method
893 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
894 r.bind("127.0.0.1", 32768);
895 // ... other socket I/O operations
897 } catch (const randolf::rex::xALL e) {
898 std::cerr << "Socket exception: " << e.what() << std::endl;
900 } catch (const std::exception e) {
901 std::cerr << "Other exception: " << e.what() << std::endl;
905 } // -x- int main -x-
908 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
909 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
910 @throws randolf::rex::xEINVAL Protocal family invalid or not available
911 @throws randolf::rex::xEINVAL Invalid flags in type
912 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
913 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
914 @throws randolf::rex::xENOBUFS Insufficient memory
915 @throws randolf::rex::xENOMEM Insufficient memory
916 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
917 supported within the specified family (a.k.a., communication domain)
921 *///=========================================================================
923 /// Communication domain; usually one of:@n
925 /// AF_INET6 (IPv6)@n
926 /// AF_UNIX (UNIX domain sockets)
928 /// Communication semantics; usually one of:@n
929 /// SOCK_STREAM (common for TCP)@n
930 /// SOCK_DGRAM (common for UDP)
931 const int type = SOCK_STREAM,
932 /// Network protocol; usually one of:@n
936 /// PF_UNSPEC (auto-detect)
937 const int protocol = PF_UNSPEC) {
938 __socket(family, type, protocol);
939 }; // -x- constructor rsocket -x-
941 /*======================================================================*//**
943 Destructor, which closes any underlying sockets, frees any TLS structures
944 that were allocated by OpenSSL, and performs any other necessary clean-up
945 before finally copying the I/O statistics to a designated structure (if one
946 was specified with the @ref net_io_final() method).
948 Developers should take care to check that the @ref rsocket_io::is_final flag
949 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
950 since there's no guarantee that the destructor will necessarily be executed
951 in a timely manner (this flag is set last, after all other statistics are
952 copied into the structure while in a mutex-locked state).
955 *///=========================================================================
956 ~rsocket() noexcept {
958 // --------------------------------------------------------------------------
960 // --------------------------------------------------------------------------
962 debug("destructor(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
964 debug(false); // Disable debug mode (might trigger extra clean-up in future)
965 } // -x- if _debug -x-
967 // --------------------------------------------------------------------------
968 // Free memory and resources and close handles that need to be freed/closed.
969 // --------------------------------------------------------------------------
970 if (__tls_fd != nullptr) SSL_free(__tls_fd);
971// if (__tls_sni != nullptr) tls_sni(nullptr); // Remove any SNI callbacks // WE DON'T NEED THIS
973// if (__tls_ctx != nullptr) SSL_CTX_free(__tls_ctx); // TODO: Research using SSL_up_ref(__tls_ctx)
974 if (__socket_fd != 0) ::close(__socket_fd);
975 if (__buffer != nullptr) ::free(__buffer);
976// if (__buffer != nullptr) delete []__buffer;
978 // --------------------------------------------------------------------------
979 // Copy statistics to final location. (We do this last in case there's an
980 // issue with locking; there shouldn't be, but if there is then at least we
981 // already we're not inadvertently hanging on to resources at this point that
982 // need to be freed/closed anyway.)
983 // --------------------------------------------------------------------------
984 if (__io_final_addr != nullptr) {
985 __io_final_addr->lock();
986 __io_final_addr->bytes_rx = __bytes_rx;
987 __io_final_addr->bytes_tx = __bytes_tx;
988 // ->bytes_xx = <bytes in buffer; these are lost bytes that should be subtracted from __bytes_rx>
989 __io_final_addr->crypt_rx = __crypt_rx;
990 __io_final_addr->crypt_tx = __crypt_tx;
991 // ->crypt_xx = <bytes in buffer; these are lost bytes that should be subtracted from __crypt_rx>
992 __io_final_addr->is_final = true;
993 __io_final_addr->unlock();
994 } // -x- if __io_final_addr -x-
996 }; // -x- destructor ~rsocket -x-
998 /*======================================================================*//**
1000 Accept new [inbound] socket connetions. (This is typically used in a loop.)
1003 The resulting rsocket object is created before the actual call to the @c
1007 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1008 select(), and accept()/accept4() when using multiple sockets.
1011 To prevent resource leaks, the resulting rsocket needs to be deleted after
1012 it's no longer needed.
1014 @throws randolf::rex::xEBADF The underlying socket is not open
1015 @throws randolf::rex::xECONNABORTED The connection was aborted
1016 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1017 part of the user address space
1018 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1019 @throws randolf::rex::xEHOSTUNREACH No route to host
1020 @throws randolf::rex::xEINTR Interrupted by a signal
1021 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1022 length of the address is invalid
1023 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1024 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1025 @throws randolf::rex::xENETUNREACH No route to network
1026 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1027 @throws randolf::rex::xENOBUFS Insufficient memory
1028 @throws randolf::rex::xENOMEM Insufficient memory
1029 @throws randolf::rex::xENONET Requested host is not reachable on the network
1030 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1032 @throws randolf::rex::xENOSR System ran out of stream resources
1033 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1034 doesn't refer to a socket
1035 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1036 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1037 @throws randolf::rex::xEPROTO Protocol error
1038 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1039 supported within the specified family (a.k.a., communication domain)
1040 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1041 a system call is restartable and can be intercepted-and-redirected
1042 (there is no need to catch this exception unless you are an advanced
1043 developer, in which case you likely still won't need to catch it in
1045 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1046 supported within the specified family (a.k.a., communication domain)
1047 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1048 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1049 no inbound connections are waiting
1051 @returns Newly-created rsocket object representing the connection received
1058 *///=========================================================================
1060 if (__debug) debug("accept(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1063 // --------------------------------------------------------------------------
1064 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1065 // new incoming connection.
1066 // --------------------------------------------------------------------------
1067 __tls_new_endpoint = new rsocket(this, false);
1069 // --------------------------------------------------------------------------
1070 // Wait for incoming connection, and update internal flags in client rsocket.
1071 // --------------------------------------------------------------------------
1072 __tls_new_endpoint->__socket_fd = ::accept(__socket_fd, (sockaddr*)&__tls_new_endpoint->__socket_addr, &__tls_new_endpoint->__socket_addr_size);
1073 if (__tls_new_endpoint->__socket_fd == -1) {
1074 delete __tls_new_endpoint; // Memory management
1075 //__tls_new_endpoint = nullptr;
1076 __rc_check(-1); // This part throws the exception
1078 __tls_new_endpoint->__socket_open = true;
1079 __tls_new_endpoint->__socket_connected = true;
1081 // --------------------------------------------------------------------------
1082 // Perform TLS accept() as well, but only if TLS is enabled.
1084 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1085 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1086 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1087 // --------------------------------------------------------------------------
1089 __tls_new_endpoint->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1090 __tls_new_endpoint->tls(true); // Make sure __tls_fd is configured correctly
1091 __rc_check_tls(SSL_accept(__tls_new_endpoint->__tls_fd));
1092 } // -x- if __tls -x-
1094 return __tls_new_endpoint;
1095 }; // -x- rsocket* accept -x-
1097 /*======================================================================*//**
1099 Accept new [inbound] socket connetions. (This is typically used in a loop.)
1102 The resulting rsocket object is created before the actual call to the @c
1106 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1107 select(), and accept()/accept4() when using multiple sockets.
1110 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1111 aids in the prevention of resource leaks).
1113 @throws randolf::rex::xEBADF The underlying socket is not open
1114 @throws randolf::rex::xECONNABORTED The connection was aborted
1115 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1116 part of the user address space
1117 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1118 @throws randolf::rex::xEHOSTUNREACH No route to host
1119 @throws randolf::rex::xEINTR Interrupted by a signal
1120 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1121 length of the address is invalid
1122 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1123 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1124 @throws randolf::rex::xENETUNREACH No route to network
1125 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1126 @throws randolf::rex::xENOBUFS Insufficient memory
1127 @throws randolf::rex::xENOMEM Insufficient memory
1128 @throws randolf::rex::xENONET Requested host is not reachable on the network
1129 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1131 @throws randolf::rex::xENOSR System ran out of stream resources
1132 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1133 doesn't refer to a socket
1134 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1135 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1136 @throws randolf::rex::xEPROTO Protocol error
1137 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1138 supported within the specified family (a.k.a., communication domain)
1139 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1140 a system call is restartable and can be intercepted-and-redirected
1141 (there is no need to catch this exception unless you are an advanced
1142 developer, in which case you likely still won't need to catch it in
1144 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1145 supported within the specified family (a.k.a., communication domain)
1146 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1147 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1148 no inbound connections are waiting
1150 @returns Newly-created rsocket object representing the connection received,
1151 wrapped in std::shared_ptr (a C++ smart pointer)
1157 *///=========================================================================
1158 std::shared_ptr<rsocket> accept_sp() {
1159 if (__debug) debug("accept_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1162 // --------------------------------------------------------------------------
1163 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1164 // new incoming connection.
1165 // --------------------------------------------------------------------------
1166 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1167 __tls_new_endpoint = r.get(); // The SNI callback needs this
1169 // --------------------------------------------------------------------------
1170 // Wait for incoming connection, and update internal flags in client rsocket.
1171 // --------------------------------------------------------------------------
1172 r->__socket_fd = __rc_check(::accept(__socket_fd, (sockaddr*)&r->__socket_addr, &r->__socket_addr_size));
1173 r->__socket_open = true;
1174 r->__socket_connected = true;
1176 // --------------------------------------------------------------------------
1177 // Perform TLS accept() as well, but only if TLS is enabled.
1179 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1180 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1181 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1182 // --------------------------------------------------------------------------
1183 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1184 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1185 r->tls(true); // Make sure __tls_fd is configured correctly
1186 __rc_check_tls(SSL_accept(r->__tls_fd));
1187 } // -x- if __tls -x-
1190 }; // -x- std::shared_ptr<rsocket> accept_sp -x-
1192 /*======================================================================*//**
1194 Accept new [inbound] socket connetions, with socket flags specified. (This
1195 is typically used in a loop.)
1198 The resulting rsocket object is created before the actual call to the @c
1202 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1203 select(), and accept()/accept4() when using multiple sockets.
1206 To prevent resource leaks, the resulting rsocket needs to be deleted after
1207 it's no longer needed.
1209 @throws randolf::rex::xEBADF The underlying socket is not open
1210 @throws randolf::rex::xECONNABORTED The connection was aborted
1211 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1212 part of the user address space
1213 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1214 @throws randolf::rex::xEHOSTUNREACH No route to host
1215 @throws randolf::rex::xEINTR Interrupted by a signal
1216 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1217 length of the address is invalid
1218 @throws randolf::rex::xEINVAL Invalid value in flags
1219 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1220 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1221 @throws randolf::rex::xENETUNREACH No route to network
1222 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1223 @throws randolf::rex::xENOBUFS Insufficient memory
1224 @throws randolf::rex::xENOMEM Insufficient memory
1225 @throws randolf::rex::xENONET Requested host is not reachable on the network
1226 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1228 @throws randolf::rex::xENOSR System ran out of stream resources
1229 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1230 doesn't refer to a socket
1231 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1232 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1233 @throws randolf::rex::xEPROTO Protocol error
1234 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1235 supported within the specified family (a.k.a., communication domain)
1236 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1237 a system call is restartable and can be intercepted-and-redirected
1238 (there is no need to catch this exception unless you are an advanced
1239 developer, in which case you likely still won't need to catch it in
1241 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1242 supported within the specified family (a.k.a., communication domain)
1243 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1244 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1245 no inbound connections are waiting
1247 @returns Newly-created rsocket object representing the connection received
1254 *///=========================================================================
1256 /// SOCK_NONBLOCK@n SOCK_CLOEXEC
1257 const int flags = 0) {
1258 if (__debug) debug("accept4(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1259 + ", " + std::to_string(flags)
1262 // --------------------------------------------------------------------------
1263 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1264 // new incoming connection.
1265 // --------------------------------------------------------------------------
1266 rsocket* __tls_new_endpoint = new rsocket(this, false);
1268 // --------------------------------------------------------------------------
1269 // Wait for incoming connection, and update internal flags in client rsocket.
1270 // --------------------------------------------------------------------------
1271 __tls_new_endpoint->__socket_fd = ::accept4(__socket_fd, (sockaddr*)&__tls_new_endpoint->__socket_addr, &__tls_new_endpoint->__socket_addr_size, flags);
1272 if (__tls_new_endpoint->__socket_fd == -1) {
1273 delete __tls_new_endpoint; // Memory management
1274 __rc_check(-1); // This part throws the exception
1276 __tls_new_endpoint->__socket_open = true;
1277 __tls_new_endpoint->__socket_connected = true;
1279 // --------------------------------------------------------------------------
1280 // Perform TLS accept() as well, but only if TLS is enabled.
1282 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1283 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1284 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1285 // --------------------------------------------------------------------------
1286 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1287 __tls_new_endpoint->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1288 __tls_new_endpoint->tls(true); // Make sure __tls_fd is configured correctly
1289 __rc_check_tls(SSL_accept(__tls_new_endpoint->__tls_fd));
1290 } // -x- if __tls -x-
1292 return __tls_new_endpoint;
1293 }; // -x- rsocket* accept4 -x-
1295 /*======================================================================*//**
1297 Accept new [inbound] socket connetions, with socket flags specified. (This
1298 is typically used in a loop.)
1301 The resulting rsocket object is created before the actual call to the @c
1305 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1306 select(), and accept()/accept4() when using multiple sockets.
1309 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1310 aids in the prevention of resource leaks).
1312 @throws randolf::rex::xEBADF The underlying socket is not open
1313 @throws randolf::rex::xECONNABORTED The connection was aborted
1314 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1315 part of the user address space
1316 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1317 @throws randolf::rex::xEHOSTUNREACH No route to host
1318 @throws randolf::rex::xEINTR Interrupted by a signal
1319 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1320 length of the address is invalid
1321 @throws randolf::rex::xEINVAL Invalid value in flags
1322 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1323 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1324 @throws randolf::rex::xENETUNREACH No route to network
1325 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1326 @throws randolf::rex::xENOBUFS Insufficient memory
1327 @throws randolf::rex::xENOMEM Insufficient memory
1328 @throws randolf::rex::xENONET Requested host is not reachable on the network
1329 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1331 @throws randolf::rex::xENOSR System ran out of stream resources
1332 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1333 doesn't refer to a socket
1334 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1335 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1336 @throws randolf::rex::xEPROTO Protocol error
1337 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1338 supported within the specified family (a.k.a., communication domain)
1339 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1340 a system call is restartable and can be intercepted-and-redirected
1341 (there is no need to catch this exception unless you are an advanced
1342 developer, in which case you likely still won't need to catch it in
1344 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1345 supported within the specified family (a.k.a., communication domain)
1346 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1347 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1348 no inbound connections are waiting
1350 @returns Newly-created rsocket object representing the connection received,
1351 wrapped in std::shared_ptr (a C++ smart pointer)
1357 *///=========================================================================
1358 std::shared_ptr<rsocket> accept4_sp(
1359 /// SOCK_NONBLOCK@n SOCK_CLOEXEC
1360 const int flags = 0) {
1361 if (__debug) debug("accept4_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1362 + ", " + std::to_string(flags)
1365 // --------------------------------------------------------------------------
1366 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1367 // new incoming connection.
1368 // --------------------------------------------------------------------------
1369 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1370 __tls_new_endpoint = r.get(); // The SNI callback needs this
1372 // --------------------------------------------------------------------------
1373 // Wait for incoming connection, and update internal flags in client rsocket.
1374 // --------------------------------------------------------------------------
1375 r->__socket_fd = __rc_check(::accept4(__socket_fd, (sockaddr*)&r->__socket_addr, &r->__socket_addr_size, flags));
1376 r->__socket_open = true;
1377 r->__socket_connected = true;
1379 // --------------------------------------------------------------------------
1380 // Perform TLS accept() as well, but only if TLS is enabled.
1382 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1383 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1384 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1385 // --------------------------------------------------------------------------
1386 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1387 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1388 r->tls(true); // Make sure __tls_fd is configured correctly
1389 __rc_check_tls(SSL_accept(r->__tls_fd));
1390 } // -x- if __tls -x-
1391// if (__tls_ctx != nullptr) { r->tls(true); r->__tls = (bool)__tls; } // Make sure __tls_fd is allocated appropriately
1394 }; // -x- std::shared_ptr<rsocket> accept4_sp -x-
1396 /*======================================================================*//**
1398 Override the default @ref listen backlog for this rsocket.
1401 The default backlog is SOMAXCONN (4096 on Linux, and 128 on older systems).
1403 @returns The same rsocket object so as to facilitate stacking
1406 *///=========================================================================
1408 /// Backlog queue size (0 = use SOMAXCONN, which is the system default of
1409 /// 4096 on Linux, and 128 on older systems)
1410 int backlog = 0) noexcept {
1411 __socket_backlog = backlog == 0 ? SOMAXCONN : backlog;
1413 }; // -x- rsocket* backlog -x-
1415 /*======================================================================*//**
1417 Find out what this rsocket's default listen backlog is.
1418 @returns The default listen backlog
1421 *///=========================================================================
1422 int backlog() noexcept {
1423 return __socket_backlog;
1424 }; // -x- int backlog -x-
1426 /*======================================================================*//**
1428 Bind this socket to the specified network address (and port number if the
1429 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1430 used for server deamons, but can also be used to control the address from
1431 which the local host will make an outbound connection via the @ref connect()
1432 method (this bound address is the address from which the endpoint will
1433 recieve your connection).
1435 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
1436 (or 1023 on some Operating Systems that test only for below 1024) in
1437 the absence of elevated access or the absence of a capability flag
1439 @throws randolf::rex::xEACCES If binding to an interface on systems that
1440 require elevated access for direct interface binding in absence of
1441 said elevated access
1442 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1443 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
1444 address is not available on any local interface
1445 @throws randolf::rex::xEBADF The underlying socket is not open
1446 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1447 part of the user address space
1448 @throws randolf::rex::xEINVAL Socket is already bound
1449 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
1450 valid for this socket's family (a.k.a., communication domain)
1451 @throws randolf::rex::xENOMEM Insufficient memory
1452 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1453 doesn't refer to a socket
1455 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1456 @throws randolf::rex::xEACCES Write permission or search permission denied
1457 on a component of the path prefix (@c AF_UNIX family)
1458 @throws randolf::rex::xELOOP Too many symbolic links encountered while
1459 resolving address (@c AF_UNIX family)
1460 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
1461 @throws randolf::rex::xENOENT One of the path's directory components doesn't
1462 exist (@c AF_UNIX family)
1463 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
1464 directory (@c AF_UNIX family)
1465 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
1467 @returns The same rsocket object so as to facilitate stacking
1468 @see bind(std::string, int)
1473 *///=========================================================================
1475 /// Socket address structure
1476 const struct sockaddr* addr,
1477 /// Length of socket structure
1478 const socklen_t addrlen = sizeof(sockaddr)) {
1479 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1481 + ", size=" + std::to_string(addrlen)
1483 if (addrlen > sizeof(sockaddr_storage)) randolf::rex::mk_exception("addlen is too large for bind()", EINVAL);
1484 __rc_check(::bind(__socket_fd, addr, addrlen));
1485 std::memcpy(&__socket_addr, addr, addrlen); // No errors were returned by bind(), so now we'll copy addr to __socket_addr
1487 } // -x- rsocket* bind -x-
1489 //===========================================================================
1491 // TODO: Document usage of IP_BIND_ADDRESS_NO_PORT to support connecting
1492 // from a specific IP address without losing ephemeral port selection.
1493 // Notes: https://idea.popcount.org/2014-04-03-bind-before-connect/
1495 // TODO: Support AF_BLUETOOTH addresses
1497 // https://man7.org/linux/man-pages/man7/address_families.7.html
1499 // TODO: Support AF_IPX addresses
1501 // https://man7.org/linux/man-pages/man7/address_families.7.html
1503 // TODO: Support AF_APPLETALK addresses
1504 // Reference: https://man7.org/linux/man-pages/man7/ddp.7.html
1506 // TODO: Support AF_PACKET addresses
1507 // Reference: https://man7.org/linux/man-pages/man7/packet.7.html
1509 // TODO: Support AF_X25 addresses
1510 // Reference: https://man7.org/linux/man-pages/man7/x25.7.html
1512 // TODO: Support AF_NETLINK addresses
1513 // Reference: https://man7.org/linux/man-pages/man7/netlink.7.html
1515 /*======================================================================*//**
1517 Bind this socket to the specified network address (and port number if the
1518 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1519 used for server deamons, but can also be used to control the address from
1520 which the local host will make an outbound connection via the @ref connect()
1521 method (this bound address is address from which the endpoint will recieve
1524 In addition to the standard IPv4 (AF_INET family) and IPv6 (AF_INET6 family)
1525 support, rsocket also supports a few other binding options in a manner that
1526 makes it easy to utilize, without having to handle special implementation
1527 details (because they're handled behind-the-scenese by this rsocket class).
1529 The address string supports a number of different notations and formats,
1530 which are documented, hereunder, with examples:
1533 - Network interfaces
1534 - UNIX domain sockets
1537 @par IPv4 address notations (address family @c AF_INET)
1538 Takes the form of @c x.x.x.x where each "x" represents an octet in the range
1539 of @c 0 through @c 255 (e.g., @c 127.0.0.1).
1541 Under a proper implenetation of TCP/IP, an IPv4 address can be abbreviated
1542 to fewer octets. The following examples demonstrate this (an unabbreviated
1543 IPv4 address is included for completeness):
1544 - @c 0.0.0.1 may be abbreviated to @c 1
1545 - @c 4.0.0.1 may be abbrevaited to @c 4.1
1546 - @c 4.3.0.1 may be abbreviated to @c 4.3.1
1547 - @c 4.3.2.1 is not abbreviated (this is the most common usage)
1549 @par Example of binding to IPv4 localhost
1552 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1553 #include <randolf/rex>
1554 #include <randolf/rsocket>
1556 int main(int argc, char *argv[]) {
1558 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1559 r.bind("127.0.0.1", 32768); // <-- You are here
1560 // ... other socket I/O operations
1562 } catch (const randolf::rex::xEACCES e) {
1563 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1564 return EXIT_FAILURE;
1565 } catch (const randolf::rex::xALL e) {
1566 std::cerr << "Socket exception: " << e.what() << std::endl;
1567 return EXIT_FAILURE;
1568 } catch (const std::exception e) {
1569 std::cerr << "Other exception: " << e.what() << std::endl;
1570 return EXIT_FAILURE;
1572 return EXIT_SUCCESS;
1573 } // -x- int main -x-
1577 Specifying the IP address of @c 0.0.0.0 will bind to all IPv4 addresses that
1578 are assigned to all of the host machine's network interfaces with IPv4
1581 Specifying the IP address of @c 127.0.0.1 (localhost) is useful for serving
1582 only those applications that are running on the local host and use an IPv4
1583 socket to communicate.
1586 @par IPv6 address notations (address family @c AF_INET6)
1587 Takes the form of @c x:x:x:x:x:x:x:x where each "x" represents a segment in
1588 the range of @c 0 through @c ffff in hexadecimal (e.g., `0:0:0:0:0:0:0:1`),
1589 or `x:x:x:x:x:x:y.y.y.y` where `y.y.y.y` represents an [unabbreviated] IPv4
1590 address (which merely replaces the last two IPv6 segments).
1592 Under a proper implenetation of TCP/IP, an IPv6 address can be abbreviated
1593 to fewer segments by using a sequence of two colons (`::`) to indicate that
1594 the undefined segments are @c 0 (this abbreviation can only be used once,
1595 and may represent segments at the beginning or end, or anywhere in between).
1596 The following examples demonstrate this (an unabbreviated IPv6 address is
1597 included for completeness):
1598 - `0:0:0:0:0:0:0:1` may be abbreviated to `::1`
1599 - `0:0:0:0:0:0:2:1` may be abbreviated to `::2:1`
1600 - `8:0:0:0:0:0:2:1` may be abbreviated to `8::2:1`
1601 - `8:7:0:0:0:0:2:1` may be abbreviated to `8:7::2:1`
1602 - `8:7:0:0:0:0:0:0` may be abbreviated to `8:7::`
1603 - `8:0:0:0:0:0:0:0` may be abbreviated to `8::`
1604 - `8:7:6:5:4:3:2:1` is not abbreviated
1606 @par Example of binding to IPv6 localhost
1609 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1610 #include <randolf/rex>
1611 #include <randolf/rsocket>
1613 int main(int argc, char *argv[]) {
1615 randolf::rsocket r(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1616 r.bind("::1", 32768); // <-- You are here
1617 // ... other socket I/O operations
1619 } catch (const randolf::rex::xEACCES e) {
1620 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1621 return EXIT_FAILURE;
1622 } catch (const randolf::rex::xALL e) {
1623 std::cerr << "Socket exception: " << e.what() << std::endl;
1624 return EXIT_FAILURE;
1625 } catch (const std::exception e) {
1626 std::cerr << "Other exception: " << e.what() << std::endl;
1627 return EXIT_FAILURE;
1629 return EXIT_SUCCESS;
1630 } // -x- int main -x-
1634 Specifying the IP address of @c :: will bind to all IPv6 addresses that are
1635 assigned to all of the host machine's network interfaces with IPv6 bindings.
1637 Specifying the IP address of @c ::1 (localhost) is useful for serving only
1638 those applications that are running on the local host and use an IPv6 socket
1642 @par Interface address notation (address families @c AF_INET and @c AF_INET6)
1643 Takes the form of @c if=name where "name" represents the name of a local
1646 Interface binding is useful when binding to interfaces that aren't configured
1647 with a static IP address because they were dymamically configured via the
1648 Dynamic Host Configuration Protocol (DHCP) or Stateless Address Configuration
1649 (SLAAC), or the configuration was changed manually by an administrator and
1650 you don't want your software to handle such changes gracefully, even if the
1651 new IP address is within the scope of an entirely different CIDR. (Changing
1652 between the IPv4 and IPv6 addresses is not supported, however, due to the
1653 fundamental differences between these two address families that includes
1654 differences beyond that of IP address format, although under a proper
1655 implementation of TCP/IP the binding should survive such changes when the IP
1656 address is reverted to the initial IP address family of the bound interface.)
1658 Examples of interfaces include:
1659 - `if=lo` typical for localhost virtual network adapter
1660 - `if=bond0` typical for the first bonded virtual network adapter (used in
1661 failover and load-balancing network configurations)
1662 - `if=br0` typical for the first bridge virtual network adapter
1663 - `if=eth0` typical for the first ethernet network adapter
1664 - `if=tap0` typical for the first virtual layer 2 ethernet switch (used by
1665 VPNs to extend a remote network into the local network stack)
1666 - `if=tun0` typical for the first virtual layer 3 ethernet tunnel (used by
1667 VPNs to provide access to a remote network)
1668 - `if=wlo1` typical for the first wireless network adapter
1670 @par Example of binding to interface localhost
1673 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1674 #include <randolf/rex>
1675 #include <randolf/rsocket>
1677 int main(int argc, char *argv[]) {
1679 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1680 r.bind("if=lo", 32768); // <-- You are here
1681 // ... other socket I/O operations
1683 } catch (const randolf::rex::xEACCES e) {
1684 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1685 return EXIT_FAILURE;
1686 } catch (const randolf::rex::xALL e) {
1687 std::cerr << "Socket exception: " << e.what() << std::endl;
1688 return EXIT_FAILURE;
1689 } catch (const std::exception e) {
1690 std::cerr << "Other exception: " << e.what() << std::endl;
1691 return EXIT_FAILURE;
1693 return EXIT_SUCCESS;
1694 } // -x- int main -x-
1698 This is not a standard feature of the POSIX bind() function. This rsocket
1699 class uses the setsockopt() function behind-the-scenes to configure (enable)
1700 the @c SO_BINDTODEVICE option, and then the bind() function is called with
1701 the IPv4 address @c 0.0.0.0 if the underlying socket was initialized to the
1702 @c AF_INET family, or the IPv6 address @c :: if the underlying socket was
1703 initialized to the @c AF_INET6 family.
1705 Specifying the interface address of `if=lo` (localhost) is useful for serving
1706 only those applications that are running on the local host and use an IPv4 or
1707 IPv6 socket to communicate.
1710 @par UNIX Domain Sockets address-notation (address family @c AF_UNIX)
1711 Takes the form of @c /path where "/path" represents an absolute path on the
1712 local file system (the path can also be relative, in which case it doesn't
1713 begin with a slash (`/`) character, but extra care is needed to ensure that
1714 the path will actually be at its expected location).
1716 @par Example of binding to UNIX domain sockets
1719 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1720 #include <randolf/rex>
1721 #include <randolf/rsocket>
1723 int main(int argc, char *argv[]) {
1725 randolf::rsocket r(AF_UNIX, SOCK_STREAM);
1726 r.bind("/var/run/rsocket-test-socket.tmp"); // <-- You are here
1727 // ... other socket I/O operations
1729 } catch (const randolf::rex::xEACCES e) {
1730 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1731 return EXIT_FAILURE;
1732 } catch (const randolf::rex::xALL e) {
1733 std::cerr << "Socket exception: " << e.what() << std::endl;
1734 return EXIT_FAILURE;
1735 } catch (const std::exception e) {
1736 std::cerr << "Other exception: " << e.what() << std::endl;
1737 return EXIT_FAILURE;
1739 return EXIT_SUCCESS;
1740 } // -x- int main -x-
1744 Normal socket I/O functionality is used to exchange data with another process
1745 that can open the same UNIX domain socket (normally on the same host,
1746 although it may not be outside the realm of possibility for future Linux
1747 kernels to support UNIX domain sockets on remote hosts).
1750 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
1751 (or 1023 on some Operating Systems that test only for below 1024) in
1752 the absence of elevated access or the absence of a capability flag
1754 @throws randolf::rex::xEACCES If binding to an interface on systems that
1755 require elevated access for direct interface binding in absence of
1756 said elevated access
1757 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1758 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
1759 address is not available on any local interface
1760 @throws randolf::rex::xEBADF The underlying socket is not open
1761 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1762 part of the user address space
1763 @throws randolf::rex::xEINVAL Socket is already bound
1764 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
1765 valid for this socket's family (a.k.a., communication domain)
1766 @throws randolf::rex::xENOMEM Insufficient memory
1767 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1768 doesn't refer to a socket
1770 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1771 @throws randolf::rex::xEACCES Write permission or search permission denied
1772 on a component of the path prefix (@c AF_UNIX family)
1773 @throws randolf::rex::xELOOP Too many symbolic links encountered while
1774 resolving address (@c AF_UNIX family)
1775 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
1776 @throws randolf::rex::xENOENT One of the path's directory components doesn't
1777 exist (@c AF_UNIX family)
1778 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
1779 directory (@c AF_UNIX family)
1780 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
1782 @returns The same rsocket object so as to facilitate stacking
1783 @see bind(const struct sockaddr*, const socklen_t)
1787 *///=========================================================================
1789 /// IPv4 address, IPv6 address, hostname, UNIX domain sockets path, or specialized variant (see notes, above)
1790 const std::string address,
1791 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
1792 const int port = 0) {
1793 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1795 + " port=" + std::to_string(port) + (port == 0 ? " (emphemeral or not-applicable)" : "")
1796 + ", size=" + std::to_string(__socket_addr_size)
1799//std::cout << " address: " << address << std::endl;
1800 if (address.starts_with("if=")) { // Bind to interface
1801// TODO: Use family() to simplify this section of code
1803 if (address.size() - 3 >= sizeof(ifr.ifr_name)) {} // Throw exception because ASCIIZ name will be too long
1804 address.copy(ifr.ifr_name, address.size() - 3, 3); // Copy address, excluding the "if=" portion
1805 __rc_check(::setsockopt(__socket_fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)));
1806 // TODO: Figure out how to use INADDR_ANY instead of 0 or :: (assuming this is even possible)
1807 // TODO: Test IPv6 more (telnet doesn't seem to be able to connect, and continues to work on IPv4 address)
1808 __socket_addr = *mk_sockaddr_storage(__socket_addr.ss_family == AF_INET6 ? "::" : "0", port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
1809 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
1811// } else if (address.starts_with("if_cidr=")) { // Bind to interface based on matching CIDR
1812// // REMINDER: Update static family() method as well
1813// getifaddrs: https://man7.org/linux/man-pages/man3/getifaddrs.3.html
1814// } else if (address.starts_with("if_mac=")) { // Bind to interface based on its physical/hardware machine address
1815// // REMINDER: Update static family() method as well
1816// // Resolve interface name, and then just do what if= does (above)
1819 __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
1820 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
1821// __rc_check(::bind(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
1824 } // -x- rsocket* bind -x-
1826 /*======================================================================*//**
1828 Find out what buffer size is used by the various recv() methods.
1829 @returns Buffer size (in bytes)
1830 @see buffer_size(const size_t nbytes)
1833 *///=========================================================================
1834 const int buffer_size() noexcept {
1835 return __buffer_size;
1836 }; // -x- int buffer_size -x-
1838 /*======================================================================*//**
1840 Override the default buffer size (typically 1024) used by the various recv()
1843 If resetting to the compiled-in default, use the buffer_size_reset() method
1844 instead of setting the value directly. This ensures that future versions,
1845 with a different compiled-in default, will be reset to the compiled-in value.
1846 @returns The same rsocket object so as to facilitate stacking
1848 @see buffer_size_reset
1850 *///=========================================================================
1851 rsocket* buffer_size(
1852 /// Size of the new buffer (in bytes)
1853 const size_t nbytes) noexcept {
1854 __buffer_size = nbytes;
1855 if (__debug) debug("buffer_size(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1856 + " nbytes=" + std::to_string(nbytes)
1859 }; // -x- rsocket* buffer_size -x-
1861 /*======================================================================*//**
1863 Reset the default buffer size (typically 1024) used by the various recv()
1866 This method is preferred for resetting to the compiled-in default instead of
1867 setting the value directly. This ensures that future versions, with a
1868 different compiled-in default, will be reset to the compiled-in value.
1869 @returns The same rsocket object so as to facilitate stacking
1870 @see buffer_size(const size_t nbytes)
1872 *///=========================================================================
1873 rsocket* buffer_size_reset() noexcept {
1874 __buffer_size = RSOCKET_BUFFER_SIZE;
1875 if (__debug) debug("buffer_size_reset(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1876 + " nbytes=" + std::to_string(RSOCKET_BUFFER_SIZE)
1879 }; // -x- rsocket* buffer_size_reset -x-
1881 //===========================================================================
1883 // TODO: If family is AF_UNIX then also unlink the domain socket file.
1885 /*======================================================================*//**
1887 Close this rsocket. (If this rsocket was already closed, then calling this
1888 method additional times will have no effect, and will not cause exceptions to
1891 This method may throw exceptions in some circumstances, which is extremely
1892 rare. If you prefer to close without having to contend with any exceptions
1893 (e.g., while calling close() from within a destructor), the close_passive()
1894 method will return an integer indicating success/failure instead of throwing
1895 an exception, and then you'll have to handle errno manually (which is useful
1896 in destructors because any error can merely be handled procedurally).
1898 @throws randolf::rex::xEBADF The underlying socket is not open
1899 @throws randolf::rex::xEINTR Interrupted by a signal
1900 @throws randolf::rex::xEIO An I/O error occurred
1902 @returns The same rsocket object so as to facilitate stacking
1906 *///=========================================================================
1908 if (__socket_open) {
1909 __rc_check(::close(__socket_fd));
1910 __socket_open = false;
1911 } // -x- if __socket_open -x-
1913 }; // -x- rsocket* close -x-
1915 /*======================================================================*//**
1917 Close this rsocket without throwing any exceptions (an error code is returned
1918 instead, which is useful while calling close_passive() from within a
1920 @returns 0 = success
1921 @returns -1 = error (errno will be set accordingly)
1924 *///=========================================================================
1925 int close_passive() noexcept {
1926 if (__socket_open) {
1927 int rc = ::close(__socket_fd);
1928 if (rc == 0) __socket_open = false;
1930 } // -x- if __socket_open -x-
1931 return 0; // Indicate success (because the socket was previously closed successfully)
1932 }; // -x- int close_passive -x-
1934 /*======================================================================*//**
1936 Connect this socket to a specific endpoint (which may differ from this
1937 rsocket's address that was previously configured by the @ref bind() method).
1939 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1940 @throws randolf::rex::xEADDRNOTAVAIL No ephemeral ports are available for
1941 assignment to unbound socket
1942 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
1943 @throws randolf::rex::xEAGAIN Insufficient entries in the routing cache
1944 @throws randolf::rex::xEALREADY Previous connection attempt not completed on
1946 @throws randolf::rex::xEBADF The underlying socket is not open
1947 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
1949 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1950 part of the user address space
1951 @throws randolf::rex::xEINPROGRESS Connection cannot be completed immediately
1952 on nonblocking socket (@c AF_UNIX family fails with xEAGAIN instead)
1953 @throws randolf::rex::xEINTR Interrupted by a signal
1954 @throws randolf::rex::xEISCONN Socket is already connected
1955 @throws randolf::rex::xENETUNREACH No route to network
1956 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1957 doesn't refer to a socket
1958 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1959 @throws randolf::rex::xEPERM Can't connect to a broadcast address when the
1960 socket's broadcast flag isn't enabled
1961 @throws randolf::rex::xEPROTOTYPE Socket type doesn't support the requested
1962 communications protocol (e.g., connecting a UNIX domain socket of
1963 type @c SOCK_STREAM to an endpoint expecting type @c SOCK_DGRAM)
1964 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1966 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1967 @throws randolf::rex::xEACCES Write permission or search permission denied
1968 on a component of the path prefix (@c AF_UNIX family)
1969 @throws randolf::rex::xEAGAIN Connection cannot be completed immediately on
1970 nonblocking socket (@c AF_UNIX family)
1972 @returns The same rsocket object so as to facilitate stacking
1973 @see tls_do_handshake()
1976 *///=========================================================================
1978 /// IPv4 address, IPv6 address, hostname, or UNIX domain sockets path
1979 const std::string address,
1980 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
1981 const int port = 0) {
1982 if (__debug) debug("connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1984 + " (port=" + std::to_string(port) + ")"
1985 + ", " + std::to_string(__socket_addr_size)
1988 __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
1989 __rc_check(::connect(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size), 0, rex::rex::REX_FLAGS::REX_ALT_EAGAIN);
1991// __rc_check(::connect(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
1992 __socket_connected = true;
1994 // --------------------------------------------------------------------------
1996 // --------------------------------------------------------------------------
1998 if (__debug) debug("tls_connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2000 __rc_check_tls(SSL_connect(__tls_fd));
2001 } // -x- if __tls -x-
2004 }; // -x- rsocket* connect -x-
2006 /*======================================================================*//**
2008 Find out whether debug mode is enabled.
2011 This method is threadsafe.
2012 @returns TRUE = enabled
2013 @returns FALSE = not enabled
2015 *///=========================================================================
2016 const bool debug() noexcept { return __debug; };
2018 /*======================================================================*//**
2020 Debug mode. When debug mode is enabled, output is sent to stderr by default,
2021 unless a second parameter specifies a different file handle (e.g., stdout, or
2024 debug(...) returns -1 if fd can't be written to (errno will be set in
2025 accordance with std::fprintf's behaviour since std::fprintf is used for
2026 writing debug output). Normally we'd throw an exception for such errors, but
2027 with debug is a special case because debugging needs to be as quick and
2028 convenient as possible for developers.
2030 debug(...) returns -2 if writing to stderr failed when attempting to announce
2031 that fd can't be written to (as described above).
2033 debug(...) returns -3 if some other error occurs (e.g., internal formatting
2034 error {which should not happen} or memory allocation failed, in which case
2035 there's a more serious problem that will be causing other problems elsewhere
2038 Developers may add their own debug messages by using debug(std::string),
2039 which will only be written out if debug mode is enabled. This same method is
2040 used internally, so messages will be indistinguishable from developer's
2044 This method is thread-safe.
2045 @returns 0 = success
2046 @returns -1 = error writing to stream (errno will be set accordingly)
2047 @returns -2 = error writing to stderr (errno will be set accordingly)
2048 @returns -3 = error (errno will be set accordingly)
2050 *///=========================================================================
2052 /// TRUE = enable debug mode@n
2053 /// FALSE = disable debug mode (does not close any file handles)
2054 const bool debug_flag,
2055 /// File descriptor/handle to use for debug output
2056 std::FILE* fd = stderr) noexcept {
2058 int rc = debug_fd(fd); // Attempt to change debug handle
2059 if (rc != 0) return rc; // Return error id new debug handle failed
2060 time_t tm = std::time(nullptr);
2061 std::string dt(27, 0);
2062 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2064 if (debug_flag || __debug) std::fprintf(__debug_fd,
2065 "[%s] %s: Debug mode is %s\n",
2067 __debug_prefix.c_str(),
2068 debug_flag ? "ON" : "OFF");
2069 } catch (const std::system_error& e) { // Error writing to fd
2071 std::fprintf(stderr,
2072 "[%s] %s: error writing to stream\n",
2074 __debug_prefix.c_str());
2075 } catch (const std::exception& e) {
2079 } catch (const std::exception& e) {
2082 __debug = debug_flag; // Save debug flag
2084 return 0; // Indicate success (no errors)
2085 }; // -x- int debug -x-
2087 /*======================================================================*//**
2089 Send the specified message as debug output (as long as debug mode is enabled;
2090 if disabled, no debug output will be sent).
2091 @returns 0 = success
2092 @returns -1 = error writing to stream (errno will be set accordingly)
2093 @returns -2 = error writing to stderr (errno will be set accordingly)
2094 @returns -3 = error (errno will be set accordingly)
2096 *///=========================================================================
2098 /// Debug message as an ASCIIZ string
2099 const char* msg) noexcept {
2100 return __debug ? __debug_out(std::string(msg)) : 0;
2101 }; // -x- int debug -x-
2103 /*======================================================================*//**
2104 @copydoc debug(const char*)
2106 *///=========================================================================
2108 /// Debug message as an std::string object
2109 const std::string msg) noexcept {
2110 return __debug ? __debug_out(msg) : 0;
2111 }; // -x- int debug -x-
2113 /*======================================================================*//**
2115 Find out which file descriptor/handle is used for debug output.
2116 @returns file handle/descriptor currently used for debug output
2117 *///=========================================================================
2118 const std::FILE* debug_fd() noexcept { return __debug_fd; }; // Get debug-output file handle
2120 /*======================================================================*//**
2122 Specify a different file descriptor/handle to use for debug output
2123 @returns 0 = success
2124 @returns -1 = error writing to stream (errno will be set accordingly)
2125 @returns -2 = error writing to stderr (errno will be set accordingly)
2126 @returns -3 = error (errno will be set accordingly)
2127 *///=========================================================================
2129 /// File descriptor/handle to use for debug output
2130 std::FILE* fd) noexcept { // Set debug-output file handle
2131 if (__debug_fd != fd) {
2132 debug("Current debug-output file handle unset"); // For this message, we only want to send this out if debug mode is enabled
2133 __debug_fd = fd; // Save new debug-output file handle
2134 return debug("New debug-output file handle set"); // For this message, we only want to send this out if debug mode is enabled
2136 return 0; // Indicate success (no errors)
2137 }; // -x- int debug_fd -x-
2139 /*======================================================================*//**
2141 Find out what the current prefix is set to that's used in debug output.
2143 This may be set at any time, including before or after enabling or disabling
2145 @returns debug prefix string
2147 *///=========================================================================
2148 const std::string debug_prefix() noexcept { return __debug_prefix; }; // -x- std::string debug_prefix -x-
2150 /*======================================================================*//**
2152 Change the prefix used in debug output.
2153 @returns The same rsocket object so as to facilitate stacking
2155 *///=========================================================================
2156 rsocket* debug_prefix(
2157 /// New debug prefix string
2158 const std::string prefix) noexcept {
2159 __debug_prefix = prefix;
2161 }; // -x- std::string debug_prefix -x-
2164 /*----------------------------------------------------------------------*//**
2166 Internal debug-output function. This doesn't check __debug flag because it's
2167 expected that the normal debug() methods are taking care of this.
2168 @returns 0 = success
2169 @returns -1 = error writing to stream (errno will be set accordingly)
2170 @returns -2 = error writing to stderr (errno will be set accordingly)
2171 @returns -3 = error (errno will be set accordingly)
2172 *///-------------------------------------------------------------------------
2173 const int __debug_out(
2174 /// Debugging message
2175 const std::string msg) noexcept {
2177 time_t tm = std::time(nullptr);
2178 std::string dt(27, 0);
2179 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2181 std::fprintf(__debug_fd,
2184 __debug_prefix.c_str(),
2186 } catch (const std::system_error& e) { // Error writing to fd
2188 std::fprintf(stderr,
2189 "[%s] %s: error writing to stream\n",
2191 __debug_prefix.c_str());
2192 } catch (const std::exception& e) {
2196 } catch (const std::exception& e) {
2200 return 0; // Indicate success (no errors)
2201 }; // -x- int __debug_out -x-
2203 // --------------------------------------------------------------------------
2204 // These are specialized internal debug output methods for presenting socket
2205 // options when they are get or set. These methods are marked as "private"
2206 // because they are really aren't useful outside of internal-use contexts.
2209 // Some of these methods are thread-safe.
2211 // These methods are thread-safe when they don't reference structures (e.g.,
2212 // integer {int}, unsigned integer {u_int}, and unsigned character {u_char}).
2214 // These methods are not necessarily thread-safe if they reference structures
2215 // unless those structures are defined with std::atomic, or they were
2216 // constructed on-the-fly as anonymous parameters.
2217 // --------------------------------------------------------------------------
2218 const int __debug_sockopt(
2219 /// Name of function or method
2220 const std::string func,
2221 /// The level at which the option resides; typically @c SOL_SOCKET
2223 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2225 /// The structure that this socket option will be set to
2227 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2228 + ", " + std::to_string(level)
2229 + ", " + std::to_string(option)
2230 + ", " + std::to_string(value)
2231 + ", size=" + std::to_string(sizeof(value))
2233 }; // -x- int __debug_sockopt -x-
2234 const int __debug_sockopt(
2235 /// Name of function or method
2236 const std::string func,
2237 /// The level at which the option resides; typically @c SOL_SOCKET
2239 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2241 /// The structure that this socket option will be set to
2242 const u_int value) {
2243 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2244 + ", " + std::to_string(level)
2245 + ", " + std::to_string(option)
2246 + ", u_int{" + std::to_string(value) + "}"
2247 + ", size=" + std::to_string(sizeof(value))
2249 }; // -x- int __debug_sockopt -x-
2250 const int __debug_sockopt(
2251 /// Name of function or method
2252 const std::string func,
2253 /// The level at which the option resides; typically @c SOL_SOCKET
2255 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2257 /// The structure that this socket option will be set to
2258 const u_char value) {
2259 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2260 + ", " + std::to_string(level)
2261 + ", " + std::to_string(option)
2262 + ", u_char{" + std::to_string(value) + "}"
2263 + ", size=" + std::to_string(sizeof(value))
2265 }; // -x- int __debug_sockopt -x-
2266 const int __debug_sockopt(
2267 /// Name of function or method
2268 const std::string func,
2269 /// The level at which the option resides; typically @c SOL_SOCKET
2271 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2273 /// The structure that this socket option will be set to
2274 const linger* value) {
2275 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2276 + ", " + std::to_string(level)
2277 + ", " + std::to_string(option)
2278 + ", linger{l_onoff=" + std::to_string(value->l_onoff)
2279 + ", l_linger=" + std::to_string(value->l_linger) + "}"
2280 + ", " + std::to_string(sizeof(value))
2282 }; // -x- int __debug_sockopt -x-
2283 const int __debug_sockopt(
2284 /// Name of function or method
2285 const std::string func,
2286 /// The level at which the option resides; typically @c SOL_SOCKET
2288 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2290 /// The structure that this socket option will be set to
2291 const timeval* value) {
2292 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2293 + ", " + std::to_string(level)
2294 + ", " + std::to_string(option)
2295 + ", timeval{tv_sec=" + std::to_string(value->tv_sec)
2296 + ", tv_usec=" + std::to_string(value->tv_usec) + "}"
2297 + ", " + std::to_string(sizeof(value))
2299 }; // -x- int __debug_sockopt -x-
2300 const int __debug_sockopt(
2301 /// Name of function or method
2302 const std::string func,
2303 /// The level at which the option resides; typically @c SOL_SOCKET
2305 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2307 /// The structure that this socket option will be set to
2308 const in_addr* value) {
2309 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2310 + ", " + std::to_string(level)
2311 + ", " + std::to_string(option)
2312 + ", in_addr{" + std::to_string(value->s_addr) + "}"
2313 + ", " + std::to_string(sizeof(value))
2315 }; // -x- int __debug_sockopt -x-
2316 const int __debug_sockopt(
2317 /// Name of function or method
2318 const std::string func,
2319 /// The level at which the option resides; typically @c SOL_SOCKET
2321 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2323 /// The structure that this socket option will be set to
2324 const ip_mreq* value) {
2325 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2326 + ", " + std::to_string(level)
2327 + ", " + std::to_string(option)
2328 + ", ip_mreq{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2329 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2330 + ", " + std::to_string(sizeof(value))
2332 }; // -x- int __debug_sockopt -x-
2333 const int __debug_sockopt(
2334 /// Name of function or method
2335 const std::string func,
2336 /// The level at which the option resides; typically @c SOL_SOCKET
2338 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2340 /// The structure that this socket option will be set to
2341 const ip_mreq_source* value) {
2342 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2343 + ", " + std::to_string(level)
2344 + ", " + std::to_string(option)
2345 + ", ip_mreq_source{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2346 + ", source=" + std::to_string(value->imr_sourceaddr.s_addr)
2347 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2348 + ", " + std::to_string(sizeof(value))
2350 }; // -x- int __debug_sockopt -x-
2351 const int __debug_sockopt(
2352 /// Name of function or method
2353 const std::string func,
2354 /// The level at which the option resides; typically @c SOL_SOCKET
2356 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2358 /// The structure that this socket option will be set to
2359 const icmp6_filter* value) {
2360 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2361 + ", " + std::to_string(level)
2362 + ", " + std::to_string(option)
2363 + ", icmp6_filter{[0]=" + std::to_string(value->icmp6_filt[0])
2364 + ", [1]=" + std::to_string(value->icmp6_filt[1])
2365 + ", [2]=" + std::to_string(value->icmp6_filt[2])
2366 + ", [3]=" + std::to_string(value->icmp6_filt[3])
2367 + ", [4]=" + std::to_string(value->icmp6_filt[4])
2368 + ", [5]=" + std::to_string(value->icmp6_filt[5])
2369 + ", [6]=" + std::to_string(value->icmp6_filt[6])
2370 + ", [7]=" + std::to_string(value->icmp6_filt[7]) + "}"
2371 + ", " + std::to_string(sizeof(value))
2373 }; // -x- int __debug_sockopt -x-
2374 const int __debug_sockopt(
2375 /// Name of function or method
2376 const std::string func,
2377 /// The level at which the option resides; typically @c SOL_SOCKET
2379 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2381 /// The structure that this socket option will be set to
2382 const sockaddr_in6* value) {
2383 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2384 + ", " + std::to_string(level)
2385 + ", " + std::to_string(option)
2386 + ", sockaddr_in6{family=" + std::to_string(value->sin6_family)
2387 + ", port=" + std::to_string(value->sin6_port)
2388 + ", flowinfo=" + std::to_string(value->sin6_flowinfo)
2389 + ", addr=" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[0])
2390 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[1])
2391 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[2])
2392 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[3])
2393 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[4])
2394 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[5])
2395 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[6])
2396 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[7])
2397 + ", scope_id=" + std::to_string(value->sin6_scope_id) + "}"
2398 + ", " + std::to_string(sizeof(value))
2400 }; // -x- int __debug_sockopt -x-
2401 const int __debug_sockopt(
2402 /// Name of function or method
2403 const std::string func,
2404 /// The level at which the option resides; typically @c SOL_SOCKET
2406 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2408 /// The structure that this socket option will be set to
2409 const ip6_mtuinfo* value) {
2410 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2411 + ", " + std::to_string(level)
2412 + ", " + std::to_string(option)
2413 + ", ip6_mtuinfo{addr=" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[0])
2414 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[1])
2415 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[2])
2416 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[3])
2417 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[4])
2418 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[5])
2419 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[6])
2420 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[7])
2421 + ", mtu=" + std::to_string(value->ip6m_mtu) + "}"
2422 + ", " + std::to_string(sizeof(value))
2424 }; // -x- int __debug_sockopt -x-
2425 const int __debug_sockopt(
2426 /// Name of function or method
2427 const std::string func,
2428 /// The level at which the option resides; typically @c SOL_SOCKET
2430 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2432 /// The structure that this socket option will be set to
2433 const ipv6_mreq* value) {
2434 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2435 + ", " + std::to_string(level)
2436 + ", " + std::to_string(option)
2437 + ", ipv6_mreq{addr=" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[0])
2438 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[1])
2439 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[2])
2440 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[3])
2441 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[4])
2442 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[5])
2443 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[6])
2444 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[7])
2445 + ", iface=" + std::to_string(value->ipv6mr_interface) + "}"
2446 + ", " + std::to_string(sizeof(value))
2448 }; // -x- int __debug_sockopt -x-
2449 const int __debug_sockopt(
2450 /// Name of function or method
2451 const std::string func,
2452 /// The level at which the option resides; typically @c SOL_SOCKET
2454 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2456 /// The structure that this socket option will be set to
2457 const group_req* value) {
2458 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2459 + ", " + std::to_string(level)
2460 + ", " + std::to_string(option)
2461 + ", group_req{iface=" + std::to_string(value->gr_interface)
2463 + ", " + std::to_string(sizeof(value))
2465 }; // -x- int __debug_sockopt -x-
2466 const int __debug_sockopt(
2467 /// Name of function or method
2468 const std::string func,
2469 /// The level at which the option resides; typically @c SOL_SOCKET
2471 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2473 /// The structure that this socket option will be set to
2474 const group_source_req* value) {
2475 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2476 + ", " + std::to_string(level)
2477 + ", " + std::to_string(option)
2478 + ", group_source_req{iface=" + std::to_string(value->gsr_interface)
2479 + ", <group_source_req>}"
2480 + ", " + std::to_string(sizeof(value))
2482 }; // -x- int __debug_sockopt -x-
2483 const int __debug_sockopt_other(
2484 /// Name of function or method
2485 const std::string func,
2486 /// The level at which the option resides; typically @c SOL_SOCKET
2488 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2490 /// The structure that this socket option will be set to
2491 const socklen_t size) {
2492 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2493 + ", " + std::to_string(level)
2494 + ", " + std::to_string(option)
2496 + ", " + std::to_string(size)
2498 }; // -x- int __debug_sockopt_other -x-
2501 /*======================================================================*//**
2503 Find out what the current EoL (End of Line) sequence is set to.
2505 To send an EoL sequence do not use @c send(r.eol()) because it may not be
2506 initialized yet and the endpoint you're sending to may seem unresponsive or
2507 other unexpected behaviour may occur.
2509 To send an EoL sequence properly, use @ref sendline() (no parameters is more
2510 efficient than specifying an empty string with ""), or use the specialized
2511 @ref send_eol() method (which is the most efficient option).
2513 An empty EoL sequence means that CRLF will be sent by @ref sendline(), and
2514 @ref recvline() will automatically detect from one of CR, CRLF, LF, and LFCR.
2515 @returns Current EoL sequence
2524 *///=========================================================================
2525 const std::string eol() noexcept { return __eol; }; // -x- std::string eol -x-
2527 /*======================================================================*//**
2529 Set EoL (End of Line) sequence. This sequence is used by recvline(),
2530 sendline(), and related functions, and it defaults to an empty string which
2531 means that the EoL sequence will be automatically detected on-the-fly.
2533 - @c "" (empty string) = automatically detect on-the-fly (default)
2534 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
2535 - @c \\r (CR) = Carriage Return (typical for MacOS)
2536 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
2539 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
2540 IMAP4, FINGER, NICNAME/WHOIS, etc.).
2541 @returns The same rsocket object so as to facilitate stacking
2549 *///=========================================================================
2551 /// EoL sequence as an ASCIIZ string
2552 const char* eol) noexcept {
2553 __eol = std::string(eol);
2554 __eol_out = __eol.empty() ? __CRLF : __eol;
2556 }; // -x- rsocket* eol -x-
2558 /*======================================================================*//**
2560 Set EoL (End of Line) sequence. This sequence is used by recvline(),
2561 sendline(), and related functions, and it defaults to an empty string which
2562 means that the EoL sequence will be automatically detected on-the-fly.
2564 - @c "" (empty string) = automatically detect on-the-fly (default)
2565 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
2566 - @c \\r (CR) = Carriage Return (typical for MacOS)
2567 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
2570 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
2571 IMAP4, FINGER, NICNAME/WHOIS, etc.).
2572 @returns The same rsocket object so as to facilitate stacking
2579 *///=========================================================================
2581 /// EoL sequence as an std::string object
2582 const std::string eol) noexcept {
2585 }; // -x- rsocket* eol -x-
2587 /*======================================================================*//**
2589 Configure EoL adoption policy for the @ref recvline() method. By default,
2590 @ref rsocket is configured with the EoL adoption policy enabled alongside an
2591 empty @ref eol() sequence, which results in the default operation being that
2592 the EoL sequence automatically gets detected and updated internally upon the
2593 first use of the @ref recvline() method.
2595 The EoL adoption policy is only effective when the @ref eol() sequence is not
2596 defined (which is indicated by an empty EoL sequence string).
2598 The EoL sequence is updated only when the EoL sequence string is empty, and
2599 when this EoL adoption policy is enabled.
2600 @returns The same rsocket object so as to facilitate stacking
2603 @see is_eol_adoption
2608 *///=========================================================================
2609 rsocket* eol_adoption(
2610 /// TRUE = enable EoL adoption (default)@n
2611 /// FALSE = disable EoL adoption
2612 const bool flag) noexcept {
2613 __eol_fix_printf = flag;
2615 }; // -x- rsocket* eol_adoption -x-
2617 /*======================================================================*//**
2619 Configure EoL substitution policy for the @ref printf(), @ref printfline(),
2620 @ref vprintf(), and @ref vprintfline() methods. By default, @ref rsocket
2621 substitutes printf's @c \\n sequence with the EoL sequence (if defined), and
2622 this method can be used to disable this behaviour.
2624 The @c \\n sequence used in the printf format string normally coincides with
2625 the local Operating System's newline standard, which is very likely different
2626 from the @ref rsocket endpoint's newline standard, and/or the newline
2627 standard of the protocol being implemented. This policy setting makes it
2628 possible to control whether to use the configured EoL sequence when sending a
2629 formatted string to the endpoint.
2630 @returns The same rsocket object so as to facilitate stacking
2632 @see is_eol_fix_printf
2638 *///=========================================================================
2639 rsocket* eol_fix_printf(
2640 /// TRUE = enable EoL substitution (default)@n
2641 /// FALSE = disable EoL substitution
2642 const bool flag) noexcept {
2643 __eol_fix_printf = flag;
2645 }; // -x- rsocket* eol_fix_printf -x-
2647 /*======================================================================*//**
2649 Finds the first instance of the EoL sequence and returns its offset (which is
2650 effectively the same as the size of the text, not including the characters
2651 that the EoL sequence is comprised of).
2654 This method is specialized primarily for internal use by the @ref recvline()
2656 @returns Size of EoL sequence
2657 @returns -1 if EoL sequence wasn't found
2661 *///=========================================================================
2662 const int eol_index(
2663 /// Buffer that probably contains at least one EoL sequence
2664 const std::string buffer,
2665 /// Size of string with EoL character sequence included (will be updated by this method)
2666 int* with_eol_size) noexcept {
2668 // --------------------------------------------------------------------------
2669 // An EoL character sequence was specified, so a simple search will suffice.
2670 // --------------------------------------------------------------------------
2671 if (!__eol.empty()) {
2672//std::cout << "!__eol.empty() ------------------------------ String is not empty" << std::endl;
2673 int pos = buffer.find(__eol);
2674 if (pos >= 0) *with_eol_size = pos + __eol.size(); // Update size of EoL sequence
2675//std::cout << "------1------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2677 } // -x- if !__eol.empty() -x-
2678//std::cout << "__eol.empty() ------------------------------ String is empty" << std::endl;
2680 // --------------------------------------------------------------------------
2681 // Automatic detection of EoL sequence.
2683 // Search for all four possible EoL sequences (as indicated by an empty EoL
2684 // character sequence string):
2686 // CRLF: Common for text lines with internet protocols such as SMTP, POP3,
2687 // IMAP4, HTTP, FINGER, WHOIS, etc. Also: DOS and OS/2 EoL sequence.
2689 // CR: MacOS EoL character.
2691 // LF: UNIX/Linux EoL character.
2693 // LFCR: Extremely rare, but I've encountered multiple instances where the
2694 // intended CRLF was reversed to LFCR, possibly due to a programming
2695 // error or an artifact of inappropriate/unintended endian conversion.
2696 // --------------------------------------------------------------------------
2697 int pos = buffer.find(__CR); // CR or CRLF
2698 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)
2699 *with_eol_size = pos + 1;
2700// *with_eol_size = 1;
2701//std::cout << "------2------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2703 } else if (pos >= 0) { // EoL sequence found
2704 *with_eol_size = pos + (buffer[pos + 1] == __LF ? 2 : 1); // 2=CRLF, 1=CR
2705// *with_eol_size = buffer[pos + 1] == __LF ? 2 : 1; // 2=CRLF, 1=CR
2706 if (__eol_adoption) {
2707 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
2708 __eol.resize(*with_eol_size - pos); // Truncate extra characters
2709 } // -x- if __eol_adoption -x-
2710//std::cout << "------3------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2711 return pos; // Either way, we're done
2714 pos = buffer.find(__LF); // LF or LFCR
2715 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)
2716 *with_eol_size = pos + 1;
2717// *with_eol_size = 1;
2718//std::cout << "------4------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2720 } else if (pos >= 0) { // EoL sequence found
2721 *with_eol_size = pos + (buffer[pos + 1] == __CR ? 2 : 1); // 2=LFCR, 1=LF
2722// *with_eol_size = buffer[pos + 1] == __CR ? 2 : 1; // 2=LFCR, 1=LF
2723 if (__eol_adoption) {
2724 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
2725 __eol.resize(*with_eol_size - pos); // Truncate extra characters
2726 } // -x- if __eol_adoption -x-
2727//std::cout << "------5------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2728 return pos; // Either way, we're done
2731//std::cout << "------6------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2733 }; // -x- int eol_index -x-
2735 /*======================================================================*//**
2737 Find out if the stream is at its end and that this @ref rsocket's internal
2738 buffer (if one had been set up by the @ref recvline() method) is empty. This
2739 doesn't necessarily mean that the stream is closed; but rather that the
2740 endpoint just hasn't sent any more data (yet).
2742 If the stream isn't open, then this method will always return @c true to
2743 implicitly indicate that there's no more data.
2746 For this method to work properly, you need to specify a timeout either with
2747 the timeout parameter, or by setting the @ref timeout() for this rsocket (you
2748 can still override this rsocket's timeout by specifying a timeout here).
2750 It's better (more efficient) to use this method instead of the @c MSG_PEEK
2751 flag (with the various @c recv methods) to determine whether any data is
2752 waiting on the stream (e.g., data that's received by the sockets, but not by
2753 any @c recv methods yet) because this method is specialized in handling this
2754 particular condition and responds with an easy-to-use boolean flag.
2757 EoS is an acronym for: End of Stream
2759 @throws randolf::rex::xEBADF The underlying socket is not open
2760 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2761 part of the user address space
2762 @throws randolf::rex::xEINTR Interrupted by a signal
2763 @throws randolf::rex::xENOMEM Insufficient memory
2764 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2765 doesn't refer to a socket
2766 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
2767 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
2768 is a highly improbable chance that a timeout could still occur if the
2769 data is read by another thread before the `recv(..., MSG_PEEK)` call)
2770 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
2773 @returns TRUE = End of Stream (no data, or the stream @ref is_closed())
2774 @returns FALSE = More data is ready to be received
2778 *///=========================================================================
2779// TODO: Include exceptions from recv() in documentation (above) method (once they're ready)
2781 /// Number of milliseconds to wait
2782 const int timeout = 0) {
2784 // --------------------------------------------------------------------------
2785 // Check internal buffer first.
2786 // --------------------------------------------------------------------------
2787 if (__buffer != nullptr && __buffer_head != __buffer_tail) return false;
2789 // --------------------------------------------------------------------------
2790 // Check for data that's not stored in internal buffers.
2791 // --------------------------------------------------------------------------
2792 if (__socket_open) {
2794 // --------------------------------------------------------------------------
2795 // Timeout polling (in case a timeout was specified).
2796 // --------------------------------------------------------------------------
2799 poll(POLLIN, timeout);
2801 std::shared_ptr<timeval> tv = getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO);
2802 ppoll(POLLIN, tv->tv_sec, tv->tv_usec * 1000); // 1 microsecond = 1,000 nanoseconds
2804 } catch (const randolf::rex::xETIMEDOUT e) {
2806 } // This is a specialized catch situation -- the ::recv function could still return an ETIMEDOUT error
2808 // --------------------------------------------------------------------------
2809 // Non-blocking socket will return -1 and set errno == EAGAIN or EWOULDBLOCK
2810 // if there is simply no new data. In case of graceful connection shutdown
2811 // by the remote peer, recv() will return 0.
2812 // --------------------------------------------------------------------------
2813 char z; // Temporary throw-away variable
2814 if (::recv(__socket_fd, &z, 1, MSG_PEEK | MSG_DONTWAIT) == 0) return true; // No more data; this is EoS
2816 } // -x- if __socket_open -x-
2818 return !__socket_open; // Effectively, this is: return closed();
2819 }; // -x- bool eos -x-
2821 /*======================================================================*//**
2823 Find out what the specified IP address string's family (@c SO_DOMAIN) is.
2824 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
2825 address is not available on any local interface
2826 @throws randolf::rex::xEAI_NONAME If @c address is an empty string
2827 @returns @c AF_UNSPEC (if family couldn't be determined)
2828 @returns @c AF_INET (IPv4 address)
2829 @returns @c AF_INET6 (IPv6 address)
2830 @returns @c AF_UNIX (UNIX Domain address)
2831 @returns ...or other family as applicable
2833 *///=========================================================================
2835 /// Address, similar to @ref bind() addressing, including non-standard "if="
2836 /// variant that names a network interface
2837 const std::string address,
2838 /// Preferred family to return first (used only with interface mode where the
2839 /// network interface is specified after the "if=" prefix); the default value
2840 /// of @c AF_UNSPEC will return the first family interface found
2841 const int preferred_family = AF_UNSPEC) {
2843 // --------------------------------------------------------------------------
2844 // Simple checks first.
2845 // --------------------------------------------------------------------------
2846 if (address.size() == 0) randolf::rex::mk_exception("", EAI_NONAME);
2847 if (address.front() == '/') return AF_UNIX;
2849 // --------------------------------------------------------------------------
2850 // if=<interface-name>: Same "interface" option that we support in bind()
2851 // --------------------------------------------------------------------------
2852 if (address.starts_with("if=")) {
2853 //std::cout << "address=" << address.substr(3).c_str() << std::endl; // Debug
2854 struct ifaddrs *ifaddr; // Chained interface addresses structure
2855 if (::getifaddrs(&ifaddr) == -1) { // Error occurred, so we're done here
2856 freeifaddrs(ifaddr); // Memory management
2857 randolf::rex::mk_exception("getifaddrs() error (prequisite to searching for " + address + ")", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
2858 } // -x- if getifaddrs -x
2860 // --------------------------------------------------------------------------
2861 // Iterate through interface addresses. Each address should have a different
2862 // address family/domain, and is never AF_UNSPEC (this should never happen
2863 // because it wouldn't make sense, but if the network implementation was
2864 // broken and including an AF_UNSPEC family/domain, it would be useless for
2865 // the purposes of calling the ::socket() function so our code will just
2866 // ignore it anyway.
2867 // --------------------------------------------------------------------------
2868 int first_family = AF_UNSPEC; // We're using AF_UNSPEC (0) to indicate that there isn't a family/domain
2869 for (struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
2870 if (ifa->ifa_addr == nullptr) continue; // No address structure, so this isn't useful to us at all
2871 if (ifa->ifa_addr->sa_family == AF_UNSPEC) continue; // Skip AF_UNSPEC family/domain, as we can't use this
2872 if (address.compare(3, -1, ifa->ifa_name) != 0) continue; // Not the interface name we're looking for
2874 // --------------------------------------------------------------------------
2875 // Return current address's family if preferred_family is not specified (if
2876 // it's set to AF_UNSPEC {0}), or if it matches the current address.
2877 // --------------------------------------------------------------------------
2878 //std::cout << "if=" << ifa->ifa_name << " family=" << ifa->ifa_addr->sa_family << std::endl; // Debug
2879 if (preferred_family == AF_UNSPEC || preferred_family == ifa->ifa_addr->sa_family) {
2880 first_family = ifa->ifa_addr->sa_family; // Arbitrarily re-using first_family variable
2881 freeifaddrs(ifaddr); // Memory management
2882 return first_family; // Return current family/domain (re-used first_family variable)
2883 } // -x- if preferred_family -x-
2885 // --------------------------------------------------------------------------
2886 // Keep track of only the first_family/domain that we found; if we can't find
2887 // the preferred_family, then we'll be able to return the first one we found.
2888 // --------------------------------------------------------------------------
2889 if (first_family == AF_UNSPEC && ifa->ifa_addr->sa_family != AF_UNSPEC) first_family = ifa->ifa_addr->sa_family;
2890 //std::cout << " first_family=" << first_family << std::endl; // Debug
2891 } // -x- for *ifa -x-
2893 // --------------------------------------------------------------------------
2894 // Return first_family/domain, if there was one; otherwise, throw exception.
2895 // --------------------------------------------------------------------------
2896 freeifaddrs(ifaddr); // Memory management
2897 if (first_family != AF_UNSPEC) return first_family;
2898 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);
2899 } // -x- if /^if=/ -x-
2901 // --------------------------------------------------------------------------
2902 // Attempt to detect IPv4 or IPv6 using a simple shortcut with ::inet_pton(),
2903 // which requires some additional memory allocation (that the simple checks
2904 // from earlier don't need).
2905 // --------------------------------------------------------------------------
2906 char buf[sizeof(struct in6_addr)]; // Binary address storage
2907 if (::inet_pton(AF_INET, address.c_str(), buf) == 1) return AF_INET;
2908 if (::inet_pton(AF_INET6, address.c_str(), buf) == 1) return AF_INET6;
2910 // --------------------------------------------------------------------------
2911 // Throw xEAI_FAMILY exception because no family/domain was found.
2912 // --------------------------------------------------------------------------
2913 randolf::rex::mk_exception("No family/domain available for: " + address, EAI_FAMILY);
2914 }; // -x- int family -x-
2916 /*======================================================================*//**
2918 Get peer name returns the address of the socket as a sockaddr_storage
2921 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
2922 that aids in the prevention of resource leaks).
2924 @throws randolf::rex::xEBADF The underlying socket is not open
2925 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2926 part of the user address space
2927 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
2928 valid for this socket's family (a.k.a., communication domain)
2929 @throws randolf::rex::xENOBUFS Insufficient memory
2930 @throws randolf::rex::xENOTCONN Underlying socket is not connected
2931 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2932 doesn't refer to a socket
2934 @returns sockaddr_storage structure
2937 *///=========================================================================
2938 const std::shared_ptr<sockaddr_storage> getpeername() {
2939 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
2940 socklen_t len = sizeof(sockaddr_storage);
2941 __rc_check(::getpeername(__socket_fd, (struct sockaddr*)sa.get(), &len));
2943 }; // -x- std::shared_ptr<sockaddr_storage> getpeername -x-
2945 /*======================================================================*//**
2947 Get peer name returns the address of the socket as a std::string object.
2949 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
2950 (e.g., because the @c family doesn't utilize or support an address
2951 {or the format isn't known}
2952 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
2953 @throws randolf::rex::xEBADF The underlying socket is not open
2954 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2955 part of the user address space
2956 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
2957 valid for this socket's family (a.k.a., communication domain)
2958 @throws randolf::rex::xENOBUFS Insufficient memory
2959 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
2960 @throws randolf::rex::xENOTCONN Underlying socket is not connected
2961 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2962 doesn't refer to a socket
2964 @returns string representation of peer name
2966 *///=========================================================================
2967 const std::string getpeername_ntop() {
2968 sockaddr_storage sa;
2969 socklen_t len = sizeof(sockaddr_storage);
2970 __rc_check(::getpeername(__socket_fd, (sockaddr*)&sa, &len));
2971 return inet_ntop(&sa);
2972 }; // -x- std::string getpeername_ntop -x-
2974 /*======================================================================*//**
2976 Get specified "sockaddr_storage" structure's address as a "sockaddr"
2977 structure, for sockets in one of the supported families:
2981 - AF_UNIX (Domain socket path)
2982 - AF_PACKET (Ethernet node/mac. address)
2984 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
2985 (e.g., because the @c family doesn't utilize or support an address
2986 {or the format isn't known}
2988 @returns pointer to sockaddr structure within provided sockaddr_storage
2990 @see mk_sockaddr_storage
2996 *///=========================================================================
2997 static sockaddr* getsockaddr(
2998 /// The @c sockaddr_storage structure wherein @c sockaddr will be referenced from
2999 const sockaddr_storage* sa) {
3000 switch (sa->ss_family) {
3001 case AF_INET: // IPv4 address
3002 return (sockaddr*)&(((struct sockaddr_in*)sa)->sin_addr);//->sin_addr);
3003 case AF_INET6: // IPv6 address
3004 return (sockaddr*)&(((struct sockaddr_in6*)sa)->sin6_addr);//->sin6_addr);
3005 case AF_UNIX: // UNIX (path) domain socket
3006 return (sockaddr*)&(((struct sockaddr_un*)sa)->sun_path);//->sun_path);
3007 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
3008 // case /*AF_LINK* /18: // Link layer interface (arp)
3010 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3011 return (sockaddr*)&(((struct sockaddr_ll*)sa)->sll_addr);//->sll_addr);
3012 } // -x- switch family -x-
3013 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY"); // Everything else
3014 }; // -x- sockaddr* getsockaddr -x-
3016 /*======================================================================*//**
3018 Get socket name returns the address of the socket as a "sockaddr_storage"
3021 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
3022 that aids in the prevention of resource leaks).
3024 @throws randolf::rex::xEBADF The underlying socket is not open
3025 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3026 part of the user address space
3027 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3028 valid for this socket's family (a.k.a., communication domain)
3029 @throws randolf::rex::xENOBUFS Insufficient memory
3030 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3031 doesn't refer to a socket
3033 @returns sockaddr_storage structure
3036 *///=========================================================================
3037 const std::shared_ptr<sockaddr_storage> getsockname() {
3038 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3039 socklen_t len = sizeof(sockaddr_storage);
3040 __rc_check(::getsockname(__socket_fd, (struct sockaddr*)sa.get(), &len));
3042 }; // -x- std::string getsockname -x-
3044 /*======================================================================*//**
3046 Get socket name returns the name of the socket as a std::string object.
3048 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3049 (e.g., because the @c family doesn't utilize or support an address
3050 {or the format isn't known}
3051 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3052 @throws randolf::rex::xEBADF The underlying socket is not open
3053 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3054 part of the user address space
3055 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3056 valid for this socket's family (a.k.a., communication domain)
3057 @throws randolf::rex::xENOBUFS Insufficient memory
3058 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3059 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3060 doesn't refer to a socket
3062 @returns string representation of socket name
3064 *///=========================================================================
3065 const std::string getsockname_ntop() {
3066 sockaddr_storage sa;
3067 socklen_t len = sizeof(sockaddr_storage);
3068 __rc_check(::getsockname(__socket_fd, (sockaddr*)&sa, &len));
3069 return inet_ntop(&sa);
3070 }; // -x- std::string getsockname_ntop -x-
3072 /*======================================================================*//**
3074 Get socket option details in the form of an integer.
3076 Most options return an integer, with the remaining options returning a
3077 pointer to a structure wrapped in a std::shared_ptr (a C++ smart pointer that
3078 aids in the prevention of resource leaks); the primitive types int, u_int,
3079 and u_char are not wrapped in C++ smart pointers because returning them by
3080 value is more efficient since allocating memory for an entire structure isn't
3084 It is up to the developer to know which return type is needed according to
3085 the socket option, otherwise an exception will likely be thrown -- in some
3086 cases where the wrong type will seem to work, this is due to the wrong type
3087 providing a minimally sufficient amount of memory for the storage of the
3088 resulting structure.
3091 The returned values/structures are not marked as "const" because they may
3092 need to be modified for unforseen purposes. Modifying the returend values or
3093 structures is fine because they are intended to be independent and are
3094 expected to have no direct impact on the rsocket's internal variables and
3097 Templates in C++ aren't used here because they don't work properly for our
3098 needs due to neccesity to handle both fundamental types and structures; it
3099 turns out that mixing these is impossible when using the same function name,
3100 so this just doesn't work as well as we'd like it to. (We may try to work on
3101 this again in the future as time permits to provide an additional method for
3102 obtaining socket options, but with the intention of never removing this
3103 current set of methods so as to ensure backward compatibility in the future.)
3105 @throws randolf::rex::xEBADF The underlying socket is not open
3106 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3107 part of the user address space
3108 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3109 valid for this socket's family (a.k.a., communication domain)
3110 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
3112 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3113 doesn't refer to a socket
3115 @returns socket option value
3117 *///=========================================================================
3119 /// The level at which the option resides; typically @c SOL_SOCKET
3121 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3124 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3125 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3127 }; // -x- int getsockopt_int -x-
3129 /*======================================================================*//**
3131 Get socket option details in the form of an unsigned integer.
3132 @copydetails getsockopt_int(const int, const int)
3133 @returns socket option value
3135 *///=========================================================================
3136 u_int getsockopt_u_int(
3137 /// The level at which the option resides; typically @c SOL_SOCKET
3139 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3142 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3143 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3145 }; // -x- u_int getsockopt_u_int -x-
3147 /*======================================================================*//**
3149 Get socket option details in the form of an unsigned character.
3150 @copydetails getsockopt_int(const int, const int)
3151 @returns socket option value
3153 *///=========================================================================
3154 u_char getsockopt_u_char(
3155 /// The level at which the option resides; typically @c SOL_SOCKET
3157 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3160 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3161 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3163 }; // -x- u_char getsockopt_u_char -x-
3165 /*======================================================================*//**
3167 Get socket option details in the form of a structure.
3168 @copydetails getsockopt_int(const int, const int);
3169 @returns socket option structure wrapped in std::shared_ptr
3171 *///=========================================================================
3172 std::shared_ptr<linger> getsockopt_linger(
3173 /// The level at which the option resides; typically @c SOL_SOCKET
3175 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3177 std::shared_ptr value = std::make_shared<linger>();
3178 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3179 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3181 }; // -x- linger getsockopt_linger -x-
3183 /*======================================================================*//**
3184 @copydoc getsockopt_linger(const int, const int)
3186 *///=========================================================================
3187 std::shared_ptr<timeval> getsockopt_timeval(
3188 /// The level at which the option resides; typically @c SOL_SOCKET
3190 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3192 std::shared_ptr value = std::make_shared<timeval>();
3193 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3194 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3196 }; // -x- timeval getsockopt_timeval -x-
3198 /*======================================================================*//**
3199 @copydoc getsockopt_linger(const int, const int)
3201 *///=========================================================================
3202 std::shared_ptr<in_addr> getsockopt_in_addr(
3203 /// The level at which the option resides; typically @c SOL_SOCKET
3205 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3207 std::shared_ptr value = std::make_shared<in_addr>();
3208 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3209 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3211 }; // -x- in_addr getsockopt_in_addr -x-
3213 /*======================================================================*//**
3214 @copydoc getsockopt_linger(const int, const int)
3216 *///=========================================================================
3217 std::shared_ptr<ip_mreq> getsockopt_ip_mreq(
3218 /// The level at which the option resides; typically @c SOL_SOCKET
3220 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3222 std::shared_ptr value = std::make_shared<ip_mreq>();
3223 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3224 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3226 }; // -x- ip_mreq getsockopt_ip_mreq -x-
3228 /*======================================================================*//**
3229 @copydoc getsockopt_linger(const int, const int)
3231 *///=========================================================================
3232 std::shared_ptr<ip_mreq_source> getsockopt_ip_mreq_source(
3233 /// The level at which the option resides; typically @c SOL_SOCKET
3235 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3237 std::shared_ptr value = std::make_shared<ip_mreq_source>();
3238 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3239 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3241 }; // -x- ip_mreq_source getsockopt_ip_mreq_source -x-
3243 /*======================================================================*//**
3244 @copydoc getsockopt_linger(const int, const int)
3246 *///=========================================================================
3247 std::shared_ptr<icmp6_filter> getsockopt_icmp6_filter(
3248 /// The level at which the option resides; typically @c SOL_SOCKET
3250 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3252 std::shared_ptr value = std::make_shared<icmp6_filter>();
3253 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3254 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3256 }; // -x- icmp6_filter getsockopt_icmp6_filter -x-
3258 /*======================================================================*//**
3259 @copydoc getsockopt_linger(const int, const int)
3261 *///=========================================================================
3262 std::shared_ptr<sockaddr_in6> getsockopt_sockaddr_in6(
3263 /// The level at which the option resides; typically @c SOL_SOCKET
3265 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3267 std::shared_ptr value = std::make_shared<sockaddr_in6>();
3268 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3269 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3271 }; // -x- sockaddr_in6 getsockopt_sockaddr_in6 -x-
3273 /*======================================================================*//**
3274 @copydoc getsockopt_linger(const int, const int)
3276 *///=========================================================================
3277 std::shared_ptr<ip6_mtuinfo> getsockopt_ip6_mtuinfo(
3278 /// The level at which the option resides; typically @c SOL_SOCKET
3280 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3282 std::shared_ptr value = std::make_shared<ip6_mtuinfo>();
3283 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3284 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3286 }; // -x- ip6_mtuinfo getsockopt_ip6_mtuinfo -x-
3288 /*======================================================================*//**
3289 @copydoc getsockopt_linger(const int, const int)
3291 *///=========================================================================
3292 std::shared_ptr<ipv6_mreq> getsockopt_ipv6_mreq(
3293 /// The level at which the option resides; typically @c SOL_SOCKET
3295 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3297 std::shared_ptr value = std::make_shared<ipv6_mreq>();
3298 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3299 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3301 }; // -x- ipv6_mreq getsockopt_ipv6_mreq -x-
3303 /*======================================================================*//**
3304 @copydoc getsockopt_linger(const int, const int)
3306 *///=========================================================================
3307 std::shared_ptr<group_req> getsockopt_group_req(
3308 /// The level at which the option resides; typically @c SOL_SOCKET
3310 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3312 std::shared_ptr value = std::make_shared<group_req>();
3313 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3314 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3316 }; // -x- group_req getsockopt_group_req -x-
3318 /*======================================================================*//**
3319 @copydoc getsockopt_linger(const int, const int)
3321 *///=========================================================================
3322 std::shared_ptr<group_source_req> getsockopt_group_source_req(
3323 /// The level at which the option resides; typically @c SOL_SOCKET
3325 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3327 std::shared_ptr value = std::make_shared<group_source_req>();
3328 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3329 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3331 }; // -x- group_source_req getsockopt_group_source_req -x-
3333 /*======================================================================*//**
3334 @copydoc getsockopt_linger(const int, const int)
3336 *///=========================================================================
3337 template<class T> std::shared_ptr<T> getsockopt_other(
3338 /// The level at which the option resides; typically @c SOL_SOCKET
3340 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3342 std::shared_ptr value = std::make_shared<T>();
3343 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3344 if (__debug) __debug_sockopt_other("getsockopt_other(socket{0x", level, option, socklen_t(sizeof(value)));
3346 }; // -x- T getsockopt_other -x-
3348 /*======================================================================*//**
3350 Get underlying socket's address as a std::string, for sockets in one of the
3355 - AF_UNIX (Domain socket path)
3356 - AF_PACKET (Ethernet node/mac. address)
3358 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3359 (e.g., because the @c family doesn't utilize or support an address
3360 {or the format isn't known}
3361 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3362 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3364 @returns string representation of underlying socket's address
3365 @see inet_ntop(sockaddr_storage*) static method
3368 *///=========================================================================
3369 const std::string inet_ntop() { return inet_ntop((sockaddr_storage*)&__socket_addr); } // -x- std::string inet_ntop -x-
3371 /*======================================================================*//**
3373 Get specified "sockaddr_storage" structure's address as a std::string, for
3374 sockets in one of the supported families:
3378 - AF_UNIX (Domain socket path)
3379 - AF_PACKET (Ethernet node/mac. address)
3381 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3382 (e.g., because the @c family doesn't utilize or support an address
3383 {or the format isn't known}
3384 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3385 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3387 @returns string representation of underlying socket's address
3388 @see inet_ntop() non-static method
3391 *///=========================================================================
3392 static const std::string inet_ntop(
3393 /// Source structure that [should] contain address data
3394 sockaddr_storage* sa) {
3396 switch (sa->ss_family) {
3397 case AF_INET: // IPv4 address
3399 char ntop[sizeof(sockaddr_in)]{0};
3400 const char* rc = ::inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), ntop, sizeof(ntop));
3401 str = std::string(ntop);
3404 case AF_INET6: // IPv6 address
3406 char ntop[sizeof(sockaddr_in6)]{0};
3407 const char* rc = ::inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), ntop, sizeof(ntop));
3408 str = std::string(ntop);
3411 case AF_UNIX: // UNIX (path) domain socket
3412 str = std::string(((struct sockaddr_un *)sa)->sun_path);
3414 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
3415 // case /*AF_LINK* /18: // Link layer interface (arp)
3417 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3418 str = to_mac(((struct sockaddr_ll *)sa)->sll_addr);
3420 default: // Everything else
3421 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY");
3422 } // -x- switch family -x-
3424 }; // -x- std::string inet_ntop -x-
3426 /*======================================================================*//**
3428 Find out whether an internal read buffer was allocated (this is most likely
3429 triggered by an attempt to read a line of text).
3431 The buffer_size() methods report on how much memory was allocated for the
3432 internal read buffer or to set its size (in bytes).
3433 @returns TRUE = an internal read buffer was allocated
3434 @returns FALSE = an internal read buffer was not allocated
3436 @see buffer_size(const size_t nbytes)
3438 *///=========================================================================
3439 const bool is_buffered() noexcept { return __buffer != nullptr; }; // -x- bool is_buffered -x-
3441 /*======================================================================*//**
3443 Find out whether the underlying socket is not open (which may not be the same
3444 as specifically "closed" since a newly instantiated empty socket begins in a
3445 "not open" state despite the underlying socket not having been closed).
3446 @returns TRUE = not open
3447 @returns FALSE = open
3450 *///=========================================================================
3451 const bool is_closed() noexcept { return !__socket_open; }; // -x- bool is_closed -x-
3453 /*======================================================================*//**
3455 Find out whether the underlying socket is connected with/to an endpoint.
3456 @returns TRUE = open
3457 @returns FALSE = not open
3459 *///=========================================================================
3460 const bool is_connected() noexcept { return __socket_connected; }; // -x- bool is_connected -x-
3462 /*======================================================================*//**
3464 Find out whether the default byte order for this host is LSB (small endian).
3466 If you're trying to choose which endian type to use when designing a new
3467 internet protocol, then big endian is normally the better option. However,
3468 if your new protocol will only be used by hardware that all share the same
3469 endianness, then that endianness is probably the more optimal option since it
3470 will translate to an overall lesser consumption of CPU cycles by reducing or
3471 eliminating endianness conversions.
3472 @returns TRUE = LSB (little endian)
3473 @returns FALSE = MSB (big endian / network byte order)
3475 *///=========================================================================
3476 const bool is_endian_lsb() noexcept { return !__endian_is_msb; }; // -x- bool is_endian_lsb -x-
3478 /*======================================================================*//**
3480 Find out whether the default byte order for this host is MSB (big endian).
3482 Big endian is the standard known as "network byte order" that's also used in
3483 various header fields in internet packets.
3485 If you're trying to choose which endian type to use when designing a new
3486 internet protocol, then big endian is normally the better option. However,
3487 if your new protocol will only be used by hardware that all share the same
3488 endianness, then that endianness is probably the more optimal option since it
3489 will translate to an overall lesser consumption of CPU cycles by reducing or
3490 eliminating endianness conversions.
3491 @returns TRUE = MSB (big endian / network byte order)
3492 @returns FALSE = LSB (little endian)
3494 *///=========================================================================
3495 const bool is_endian_msb() noexcept { return __endian_is_msb; }; // -x- bool is_endian_msb -x-
3497 /*======================================================================*//**
3499 Find out if the EoL adoption policy is enabled for the @ref recvline() method
3500 (see the @ref eol_adoption method to find out how the dynamically-detected
3501 EoL sequence gets adopted, and under what conditions).
3502 @returns TRUE = EoL adoption is enabled
3503 @returns FALSE = EoL adoption is disabled
3511 *///=========================================================================
3512 const bool is_eol_adoption() noexcept {
3513 return __eol_adoption;
3514 }; // -x- bool is_eol_adoption -x-
3516 /*======================================================================*//**
3518 Find out if the EoL substitution policy is enabled for the @ref printf(),
3519 @ref printfline(), @ref vprintf(), and @ref vprintfline() methods.
3520 @returns TRUE = EoL substitution is enabled
3521 @returns FALSE = EoL substitution is disabled
3531 *///=========================================================================
3532 const bool is_eol_fix_printf() noexcept {
3533 return __eol_fix_printf;
3534 }; // -x- bool is_eol_fix_printf -x-
3536 /*======================================================================*//**
3538 Find out whether the underlying socket is open.
3539 @returns TRUE = open
3540 @returns FALSE = not open
3543 *///=========================================================================
3544 const bool is_open() noexcept { return __socket_open; }; // -x- bool is_open -x-
3546 /*======================================================================*//**
3548 Find out whether encrypted communications is enabled or disabled.
3549 @returns TRUE = encrypted communications is enabled
3550 @returns FALSE = encrypted communications is disabled
3551 @see tls(bool, TLS_FLAGS)
3553 *///=========================================================================
3554 const bool is_tls() noexcept { return __tls; }; // -x- bool is_tls -x-
3556 /*======================================================================*//**
3558 Find out whether TLS context is in TLS_CLIENT mode.
3559 @returns TRUE = TLS context is in TLS_CLIENT mode
3560 @returns FALSE = TLS context is in TLS_SERVER mode
3564 *///=========================================================================
3565 const bool is_tls_client_mode() noexcept { return !__tls_server_mode; }; // -x- bool is_tls_client_mode -x-
3567 /*======================================================================*//**
3569 Find out whether egress from encryption (to unencrypted mode) is allowed.
3570 @returns TRUE = egress from encrypted communications is allowed
3571 @returns FALSE = egress from encrypted communications is not allowed
3575 *///=========================================================================
3576 const bool is_tls_egress_okay() noexcept { return __tls_egress; }; // -x- bool is_tls_egress_okay -x-
3578 /*======================================================================*//**
3580 Find out whether encrypted communications is exclusive.
3581 @returns TRUE = encrypted communications is exclusive
3582 @returns FALSE = encrypted communications is not exclusive
3585 *///=========================================================================
3586 const bool is_tls_exclusive() noexcept { return __tls_exclusive; }; // -x- bool is_tls_exclusive -x-
3588 /*======================================================================*//**
3590 Find out whether ingress to encryption (from unencrypted mode) is allowed.
3591 @returns TRUE = ingress to encrypted communications is allowed
3592 @returns FALSE = ingress to encrypted communications is not allowed
3596 *///=========================================================================
3597 const bool is_tls_ingress_okay() noexcept { return __tls_ingress; }; // -x- bool is_tls_ingress_okay -x-
3599 /*======================================================================*//**
3601 Find out whether TLS context is in TLS_SERVER mode.
3602 @returns TRUE = TLS context is in TLS_SERVER mode
3603 @returns FALSE = TLS context is in TLS_CLIENT mode
3607 *///=========================================================================
3608 const bool is_tls_server_mode() noexcept { return __tls_server_mode; }; // -x- bool is_tls_server_mode -x-
3610 /*======================================================================*//**
3612 Find out whether SNI (Server Name Identifier) is enabled (configured, which
3613 implies that an internal callback function was also set up).
3614 @returns TRUE = SNI is enabled
3615 @returns FALSE = SNI is disabled
3618 *///=========================================================================
3619 const bool is_tls_sni() noexcept { return __tls_sni != nullptr; }; // -x- bool is_tls_sni -x-
3621 /*======================================================================*//**
3623 Find out whether SNI (Server Name Identifier) was matched, which means that
3624 we're using one of the supplementary TLS certificates that are included in
3625 the associated @ref rsocket_sni object as separate TLS contexts.
3627 When this method returns @c TRUE, it means the @c OpenSSL callback (which
3628 occurs during the TLS handshake process) completed the TLS handshake with one
3629 of the TLS certificates that's included in this @c rsocket's @ref rsocket_sni
3630 object instead of the primary TLS certificate assigned to this @c rsocket,
3631 and this @c rsocket is using the respective TLS context instead of the
3632 primary (default) one.
3633 @returns TRUE = SNI was matched
3634 @returns FALSE = SNI wasn't matched
3638 *///=========================================================================
3639 const bool is_tls_sni_match() noexcept { return __tls_sni_match; }; // -x- bool is_tls_sni_match -x-
3641 /*======================================================================*//**
3643 Enable listening mode for this rsocket to prepare it to accept() new inbound
3646 The backlog defaults to SOMAXCONN (4096 on Linux, and 128 on older systems),
3647 which is common on most systems. If a higher value is supplied that exceeds
3648 @c kern.somaxconn, the kernel will automatically override the backlog with
3649 the value stored in @c kern.somaxconn without generating any errors or
3652 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
3653 @throws randolf::rex::xEADDRINUSE No ephemeral ports are available for
3654 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
3655 but it really isn't)
3656 @throws randolf::rex::xEBADF The underlying socket is not open
3657 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3658 doesn't refer to a socket
3659 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
3660 @returns The same rsocket object so as to facilitate stacking
3669 *///=========================================================================
3671 /// Backlog queue size (0 = uses rsocket's default; see @ref backlog for more
3672 /// details about this); specifying a non-zero backlog also updates rocket's
3673 /// internal default (SOMAXCONN is 4096 on Linux, and 128 on older systems)
3675 if (__debug) debug("listen(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3676 + ", " + std::to_string(backlog)
3679 // --------------------------------------------------------------------------
3680 // Use rsocket's default backlog if not specified, otherwise update it
3681 // with the new value specified here.
3682 // --------------------------------------------------------------------------
3683 if (backlog = 0) backlog = __socket_backlog;
3684 else __socket_backlog = backlog;
3686 // --------------------------------------------------------------------------
3687 // Configure underlying socket to queue up to "backlog" inbound connections.
3688 // --------------------------------------------------------------------------
3689 __rc_check(::listen(__socket_fd, backlog));
3692 }; // -x- rsocket* listen -x-
3694 /*======================================================================*//**
3696 Convert an IPv4 address, IPv6 address, ethernet packet, or UNIX domain
3697 socket to a sockaddr_storage structure.
3699 If service_name is an absolute path (that begins with a "/" charcter) and the
3700 family is set to AF_UNSPEC (the default), then the resulting family will be
3704 This method utilizes the results of getaddrinfo().
3706 Other families like AF_LINK and AF_PACKET should work, but haven't been
3707 tested thoroughly. The additional support we provide for IPv4 and IPv6
3708 addresses is to copy the port number into the resulting structure (as a
3712 The resulting sockaddr_storage structure is wrapped in std::shared_ptr (a C++
3713 smart pointer that aids in the prevention of resource leaks).
3716 This method is thread-safe.
3718 @throws randolf::rex::xEAI_ADDRFAMILY If specified network host doesn't have
3719 any addresses in the specified address family
3720 @throws randolf::rex::xEAI_AGAIN Temporary failure code from DNS server (try
3722 @throws randolf::rex::xEAI_BADFLAGS Invalid flags in @c hints.ai_flags (or
3723 `hints.ai_flags` included @c AI_CANONNAME with @c nullptr as @c name)
3724 @throws randolf::rex::xEAI_FAIL Permanent failure code from DNS server
3725 @throws randolf::rex::xEAI_FAMILY The specified family is not supported
3726 @throws randolf::rex::xEAI_MEMORY Out of memory
3727 @throws randolf::rex::xEAI_NONAME If node_name is nullptr or an empty string
3728 @throws randolf::rex::xEAI_SERVICE The specified service is not available
3729 for the specified socket type
3730 @throws randolf::rex::xEAI_SOCKTYPE The specified socket type is not
3732 @throws randolf::rex::xEAI_SYSTEM Other system error (use errno to determine
3733 what the error is, then run use @ref randolf::rex::rex::mk_exception
3734 to throw the correct exception)
3736 @returns sockaddr_storage structure
3738 *///=========================================================================
3739 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
3740 /// IP address or UNIX domain socket address to convert
3741 const char* node_name,
3742 /// Port number (or service name used by some other families)
3743 const char* service_name = nullptr,
3744 /// Optional pointer to a helpful addrinfo structure
3745 const addrinfo* hints = nullptr) {
3747 // --------------------------------------------------------------------------
3748 // Initial sanity checks.
3749 // --------------------------------------------------------------------------
3750 if (node_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME); // if (node_name == nullptr && service_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME);
3752 // --------------------------------------------------------------------------
3753 // Handle any special cases.
3754 // --------------------------------------------------------------------------
3755 switch (hints == nullptr ? AF_UNSPEC : hints->ai_family) { // Default to AF_UNSPEC in the absence of hints
3757 if (node_name[0] != '/') break;
3758 // Next entry MUST be "case AF_UNIX" for this fall-through to work
3761 // For some unknown reason, clang++ can't compile this line that
3762 // g++ has absolutely no trouble with at all:
3763 // std::shared_ptr sa = std::make_shared<sockaddr_storage>(AF_UNIX);
3764 // So, after wasting more than a year trying to figure out what the
3765 // hundreds of lines of cryptic errors meant, I eventually gave up
3766 // on clang++ for being such a dumbfuck, and used this work-around:
3767 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3768 sa->ss_family = AF_UNIX;
3769 std::strcpy(((struct sockaddr_un *)sa.get())->sun_path, node_name); // Copy path
3773 } // -x- switch hints->family -x-
3775 // --------------------------------------------------------------------------
3776 // Acquire addrinfo[] linked-list array.
3777 // --------------------------------------------------------------------------
3778 addrinfo* __addr_result; // This is temporary
3779//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;
3780 randolf::rex::mk_exception("", getaddrinfo(node_name,
3785 // --------------------------------------------------------------------------
3786 // Find first valid addrinfo[] array by trying to open a socket with it.
3787 // --------------------------------------------------------------------------
3788 for (addrinfo* ar = __addr_result; ar != nullptr; ar = ar->ai_next) {
3789//std::cout << " ... ai_family=" << ar->ai_family << " ai_socktype=" << ar->ai_socktype << " ai_flags=" << ar->ai_flags << std::endl; // *******************
3790// ***************************** TODO: Make sure this loop is working properly
3792//std::cout << " !!! ai_family=" << __addr_result->ai_family << std::endl; // *******************
3794 // --------------------------------------------------------------------------
3795 // Copy the address to "sa" and return it. The "addr" data for IPv4 and IPv6
3796 // addresses include the TCP/UDP port number (service) in first two bytes as
3797 // as an unsigned integer (u_int16), followed immediately by the IP address.
3799 // We're taking the first record only. We assume that hints{} is defined on
3800 // an as-needed basis.
3802 // Note: AF_LINK and AF_PACKET are not widely supported, but we've made an
3803 // effort to accomodate their usage.
3804 // --------------------------------------------------------------------------
3805 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3806 switch (__addr_result->ai_family) {
3807 case AF_INET: // IPv4 address
3808 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_in));
3810 case AF_INET6: // IPv6 address
3811 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_in6));
3813 /* // We're handling this earlier as a special case: TODO: Move special handling to here
3814 case AF_UNIX: // UNIX (path) domain socket
3815 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_un));
3818 case /*AF_LINK*/18: // Link layer interface (arp)
3819 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_dl));
3821 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3822 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_ll));
3824 default: // Everything else
3825 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_storage));
3826 } // -x- switch family -x-
3827 freeaddrinfo(__addr_result); // We don't need this anymore
3829 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
3831 /*======================================================================*//**
3832 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
3833 *///=========================================================================
3834 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
3835 /// IP address or UNIX domain socket address to convert
3836 const char* node_name,
3838 const u_int16_t service_name,
3839 /// Optional pointer to a helpful addrinfo structure
3840 const addrinfo* hints = nullptr) {
3841 std::string port = std::to_string(service_name);
3842 return mk_sockaddr_storage(node_name, port.c_str(), hints);
3843 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
3845 /*======================================================================*//**
3846 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
3847 *///=========================================================================
3848 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
3849 /// IP address or UNIX domain socket address to convert
3850 const std::string node_name,
3852 const u_int16_t service_name,
3853 /// Optional pointer to a helpful addrinfo structure
3854 const addrinfo* hints = nullptr) {
3855 std::string port = std::to_string(service_name);
3856 return mk_sockaddr_storage(node_name.c_str(), port.c_str(), hints);
3857 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
3859 /*======================================================================*//**
3860 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
3861 *///=========================================================================
3862 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
3863 /// IP address or UNIX domain socket address to convert
3864 const std::string node_name,
3865 /// Port number (or server name used by some other families)
3866 const std::string service_name,
3867 /// Optional pointer to a helpful addrinfo structure
3868 const addrinfo* hints = nullptr) {
3869 return mk_sockaddr_storage(node_name.c_str(), service_name.c_str(), hints);
3870 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
3872 /*======================================================================*//**
3874 Specify a name for this rsocket.
3876 This is an arbitrary name that is entirely optional, and should be regarded
3877 as similar to the naming of threads.
3878 @returns The same rsocket object so as to facilitate stacking
3879 *///=========================================================================
3881 /// Name to assign to this @c rsocket
3882 const std::string name) noexcept {
3883 // const std::lock_guard<std::mutex> lock(__name);
3886 }; // -x- rsocket* name -x-
3888 /*======================================================================*//**
3890 Find out what this rsocket's name is.
3892 The built-in SNI mechanism will overwrite this data to indicate the hostname
3893 that was specified by the TLS-encrypted endpoint that triggered an internal
3894 SNI callback -- use the @ref name_sni() method to find out which hostname is
3895 actually being used by TLS.
3896 @returns The name of this rsocket (or an empty @c std::string if this rsocket
3897 doesn't have a name)
3900 *///=========================================================================
3901 std::string name() noexcept {
3902 // const std::lock_guard<std::mutex> lock(__name);
3904 }; // -x- std::string name -x-
3906 /*======================================================================*//**
3908 Find out what this rsocket's actual TLS SNI hostname is.
3910 This is the exact - or closest-matching (in the case of wildcards) - hostname
3911 associated with an actual TLS certificate that was provided in the configured
3912 @ref rsocket_sni object.
3913 @returns The hostname associated with the TLS certificate selected with SNI
3917 *///=========================================================================
3918 std::string name_sni() noexcept {
3920 }; // -x- std::string name_sni -x-
3922 /*======================================================================*//**
3924 Get socket I/O statistics from internally-tracked socket I/O counters.
3926 The number of bytes transmitted and received is tracked internally, so that
3927 the information can be used later in logging. These statistics are available
3928 at all times, but for logging purposes it makes the most sense to copy this
3929 information after the rsocket is closed, at which time the final statistics
3930 will continue to be available until the rsocket's destructor takes over.
3933 This method is threadsafe.
3934 @returns rsocket_io wrapped in a std::shared_ptr object to help ease memory
3940 *///=========================================================================
3941 std::shared_ptr<rsocket_io> net_io() noexcept {
3942 std::shared_ptr stats = std::make_shared<rsocket_io>();
3943 stats->bytes_rx = __bytes_rx;
3944 stats->bytes_tx = __bytes_tx;
3945 stats->crypt_rx = __crypt_rx;
3946 stats->crypt_tx = __crypt_tx;
3947 stats->is_final = false;
3949 }; // -x- std::shared_ptr<rsocket_io> net_io -x-
3951 /*======================================================================*//**
3953 Get socket I/O statistics from internally-tracked socket I/O counters as a
3954 pre-formatted std::string object.
3956 The number of bytes transmitted and received is tracked internally, so that
3957 the information can be used later in logging. These statistics are available
3958 at all times, but for logging purposes it makes the most sense to copy this
3959 information after the rsocket is closed, at which time the final statistics
3960 will continue to be available until the rsocket's destructor takes over.
3963 The format string may contain any characters, with only instances of the
3964 following case-sensitive command sequences to be interpolated accordingly
3965 (invalid commands will be ignored and will remain in place, unmodified):
3967 <table cellpadding=8 cellspacing=0 border=1>
3968 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
3969 <tr><td>$$</td><td>One dollar sign ("$")</td><td>@e N/A</td></tr>
3970 <tr><td>$aR</td><td>Total (all) bytes received</td><td>bytes_rx @c + crypt_rx</td></tr>
3971 <tr><td>$aT</td><td>Total (all) bytes transmitted</td><td>bytes_tx @c + crypt_tx</td></tr>
3972 <tr><td>$bR</td><td>Unencrypted bytes received</td><td>bytes_rx</td></tr>
3973 <tr><td>$bT</td><td>Unencrypted bytes transmitted</td><td>bytes_tx</td></tr>
3974 <tr><td>$cR</td><td>Encrypted bytes recevied</td><td>crypt_rx</td></tr>
3975 <tr><td>$cT</td><td>Encrypted bytes transmitted</td><td>crypt_tx</td></tr>
3976 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
3979 Why do we use dollar signs instead of percent symbols, like other formatting
3980 functions like printf() does? So that the format string won't be in conflict
3981 with any percent-prefixed commands in printf() and similar funcions. This
3982 means that a printf() format string can be put through a first pass here to
3983 get the needed statistics interpolated into the needed positions.
3986 This method is threadsafe.
3987 @returns An interpolated format string as an std::string object.
3992 *///=========================================================================
3996 /// Length of format string (in bytes), or 0 to auto-detect length if format string is an ASCIIZ string
3998 /// Pointer to an @ref rsocket_io structure to read the statistics from (nullptr = use internal copy of current statistics)
3999 // 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)
4000 rsocket_io* addr = nullptr) noexcept {
4002 // --------------------------------------------------------------------------
4003 // Measure size of format string if an ASCIIZ string was indicated.
4004 // --------------------------------------------------------------------------
4005 if (len == 0) len = std::strlen(format);
4007 // --------------------------------------------------------------------------
4008 // Internal variables.
4009 // --------------------------------------------------------------------------
4010 rsocket_io* data = addr == nullptr ? net_io().get() : addr; // Copy current statistics so that
4011 std::string stats; // Formatted result
4012 ulong val; // Value (to be inserted)
4013 char c; // Current character
4014 std::string cmd; // Accumulated command (may be subsituted)
4015 bool flag_commas = false; // Thousands separators flag
4016 bool flag_right = false; // Flush-right flag
4018 // --------------------------------------------------------------------------
4019 // Process format string and build resulting formatted stats string.
4021 // This is designed to be fast, and I'm taking huge shortcuts here since the
4022 // commands are all the same size (except for the first one, which is only
4023 // two dollar sign characters). Processing in this loop should be quite fast
4024 // compared to using library functions to search for dollar sign characters
4025 // since data needs to be copied anyway -- why run a loop over the text twice
4026 // when can get away with doing it faster by doing it only once? This is an
4027 // optimized approach, although it probably could be even faster by not using
4028 // std::string for temporary command storage (there are other ways to do this
4029 // but I think this should suffice for now since it isn't expected to be used
4031 // --------------------------------------------------------------------------
4032 for (int i = 0; i < len; i++) {
4034 // --------------------------------------------------------------------------
4035 // First character (potentially).
4036 // --------------------------------------------------------------------------
4037 if ((c = format[i]) != '$') { // Not a dollar sign -- save it and move on
4041 cmd = c; // Start building up the command string
4043 // --------------------------------------------------------------------------
4044 // Second character: Part 1 of 2
4046 // TODO: Add support for "," commas, and "r" right-alignment.
4047 // --------------------------------------------------------------------------
4048 if (++i == len) continue; // End of format string, so we're done
4049 cmd.push_back(c = format[i]);
4050 if (c == '$') { // Consume the previous dollar sign so that $$ turns into $
4051 stats.push_back(c); // Add $ to stats string (we're only keeping one dollar sign)
4054 flag_commas = flag_right = false; // Reset these flags for a clean start
4056 // --------------------------------------------------------------------------
4058 // , = Include thousands separators (commas)
4059 // r = Flush right (leading spaces will be added)
4060 // --------------------------------------------------------------------------
4063 if (++i == len) continue; // End of format string, so we're done
4064 cmd.push_back(c = format[i]);
4066 goto net_io_flags_loop;
4067 } else if (c == 'r') {
4068 if (++i == len) continue; // End of format string, so we're done
4069 cmd.push_back(c = format[i]);
4071 goto net_io_flags_loop;
4072 } // -x- if [,r] -x-
4075 // --------------------------------------------------------------------------
4076 // Second character: Part 1 of 2
4077 // --------------------------------------------------------------------------
4078 if (c < 'a' || c > 'c') { // Not a, b, or c, so treat it as a regular character
4079 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4081 } // -x- if !~[abc] -x-
4083 // --------------------------------------------------------------------------
4085 // --------------------------------------------------------------------------
4086 if (++i == len) continue; // End of format string, so we're done
4087 cmd.push_back(c = format[i]);
4088 if (c != 'R' && c != 'T') { // Not R or T, so treat it as a regular character
4089 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4091 } // -x- if !~[RT] -x-
4093 // --------------------------------------------------------------------------
4094 // Command processing. If the command is valid, then it will be interpolated
4095 // with its expected result by replacing it with the resulting value.
4096 // --------------------------------------------------------------------------
4097 //std::cout << "[" << cmd << "]"; // Debug
4098 if (cmd.ends_with("aR")) val = data->bytes_rx + data->crypt_rx;
4099 else if (cmd.ends_with("aT")) val = data->bytes_tx + data->crypt_tx;
4100 else if (cmd.ends_with("bR")) val = data->bytes_rx ;
4101 else if (cmd.ends_with("bT")) val = data->bytes_tx ;
4102 else if (cmd.ends_with("cR")) val = data->crypt_rx;
4103 else if (cmd.ends_with("cT")) val = data->crypt_tx;
4104 else { stats.append(cmd); continue; } // This is wrong, so ignore it
4106 // --------------------------------------------------------------------------
4107 // Re-use cmd to generate formatted value.
4108 // --------------------------------------------------------------------------
4110 cmd.resize(21); // Maximum length without commas (plus character zero): 18446744073709551615
4111 cmd.resize(sprintf(cmd.data(), flag_right ? "%20lu" : "%lu", val)); // The ::sprintf function can't use cmd.c_str() here
4112 if (flag_commas) cmd = randolf::rtools::insert_commas(cmd.c_str()); // Insert commas (this makes the string longer)
4114 // --------------------------------------------------------------------------
4115 // Convert to std::string and add to the stats string.
4116 // --------------------------------------------------------------------------
4122 }; // -x- std::string net_io -x-
4124 /*======================================================================*//**
4126 Where the destructor should save final I/O statistics before this rsocket's
4127 resources are completely freed/deallocated.
4129 Developers should take care to check that the @ref rsocket_io::is_final flag
4130 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
4131 since there's no guarantee that the destructor will necessarily be executed
4132 in a timely manner (this flag is set last, after all other statistics are
4133 copied into the structure while in a mutex-locked state).
4134 @returns The same rsocket object so as to facilitate stacking
4140 *///=========================================================================
4141 rsocket* net_io_final(
4142 /// Pointer to @ref rsocket_io structure (nullptr = disabled)
4143 rsocket_io* addr) noexcept {
4144 __io_final_addr = addr;
4146 }; // -x- rsocket* net_io_final -x-
4148 /*======================================================================*//**
4150 Where the destructor should save current I/O statistics.
4152 Statistics are copied into the structure while in a mutex-locked state, but
4153 the copy itself is not an overall atomic snapshot of the I/O statistics even
4154 though each statistic is copied atomically. This means that the actual
4155 statistics could be slightly different if updates occur independently (e.g.,
4156 due to activities in a different thread), but this likely won't matter since
4157 the anticipated use for these statistics is to display or otherwise present
4158 general statistical information to a user at regular intervals.
4159 @returns The same rsocket object so as to facilitate stacking
4165 *///=========================================================================
4166 rsocket* net_io_update(
4167 /// Pointer to @ref rsocket_io structure (nullptr = do nothing)
4168 rsocket_io* addr) noexcept {
4170 // --------------------------------------------------------------------------
4171 // Copy statistics to final location. (We do this last in case there's an
4172 // issue with locking; there shouldn't be, but if there is then at least we
4173 // already we're not inadvertently hanging on to resources at this point that
4174 // need to be freed/closed anyway.)
4175 // --------------------------------------------------------------------------
4176 if (addr != nullptr) {
4178 addr->bytes_rx = __bytes_rx;
4179 addr->bytes_tx = __bytes_tx;
4180 addr->crypt_rx = __crypt_rx;
4181 addr->crypt_tx = __crypt_tx;
4182 addr->is_final = false;
4184 } // -x- if addr -x-
4187 }; // -x- rsocket* net_io_update -x-
4189 /*======================================================================*//**
4191 Poll the underlying socket using the poll() method for data that's ready for
4192 receiving (default), etc.
4195 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4196 select(), and accept()/accept4() when using multiple sockets.
4198 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4199 part of the user address space
4200 @throws randolf::rex::xEINTR Interrupted by a signal
4201 @throws randolf::rex::xENOMEM Insufficient memory
4202 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4203 doesn't refer to a socket
4204 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4205 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4206 is a highly improbable chance that a timeout could still occur if the
4207 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4208 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4211 @returns The resulting (short)pollfd.revents bitfield
4212 @returns 0 if @c timeout_behaviour is set to TIMEOUT_ZERO
4215 *///=========================================================================
4217 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4218 const short events = POLLIN,
4219 /// Number of milliseconds to wait
4220 const int timeout = 0,
4221 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4222 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4223 if (__debug) debug("poll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4224 + ", pollfd.events=" + std::to_string(events)
4225 + ", timeout=" + std::to_string(timeout)
4227 struct pollfd fds{__socket_fd, events, 0};
4228 if (__rc_check(::poll(&fds, 1, timeout)) == 0) {
4229 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4233 }; // -x- short poll -x-
4235 /*======================================================================*//**
4237 Get port number associated with underlying socket descriptor/handle.
4240 - an emphemeral port number assignment (dynamic) is intended
4241 - the underlying socket details are not defined (e.g., in the case of an
4242 empty rsocket instantiation)
4243 - port numbers are not supported/utilized by the current family (e.g., not
4244 AF_INET {IPv4} or AF_INET6 {IPv6})
4246 The port number can be set in most constructors, or via one of the socket()
4248 @returns Port number (typically TCP and UDP, although some other families
4249 such as SCTP and DCCP also utilize port numbers)
4250 @see socket_family()
4251 @see socket_protocol()
4254 *///=========================================================================
4255 const uint16_t port() noexcept {
4256 switch (__socket_addr.ss_family) {
4257 case AF_INET: // IPv4
4258 return ntohs(((sockaddr_in*)&__socket_addr)->sin_port);
4259 case AF_INET6: // IPv6
4260 return ntohs(((sockaddr_in6*)&__socket_addr)->sin6_port);
4261 default: // Everything else
4263 } // -x- switch __socket_addr.ss_family -x-
4264 }; // -x- uint16_t port -x-
4266 /*======================================================================*//**
4268 Poll the underlying socket using the ppoll() method for data that's ready for
4269 receiving (default), etc.
4272 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4273 select(), and accept()/accept4() when using multiple sockets.
4275 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4276 part of the user address space
4277 @throws randolf::rex::xEINTR Interrupted by a signal
4278 @throws randolf::rex::xENOMEM Insufficient memory
4279 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4280 doesn't refer to a socket
4281 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4282 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4283 is a highly improbable chance that a timeout could still occur if the
4284 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4285 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4288 @returns The resulting (short)pollfd.revents bitfield
4291 *///=========================================================================
4293 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4294 const short events = POLLIN,
4296 const struct timespec* tmo_p = nullptr,
4298 const sigset_t* sigmask = nullptr,
4299 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4300 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4301 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4302 + ", pollfd.events=" + std::to_string(events)
4303 + ", timespec{tv_sec=" + std::to_string(tmo_p->tv_sec)
4304 + ", tv_nsec=" + std::to_string(tmo_p->tv_nsec) + "}"
4307 struct pollfd fds{__socket_fd, events, 0};
4308 if (__rc_check(::ppoll(&fds, 1, tmo_p, sigmask)) == 0) {
4309 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4313 }; // -x- short ppoll -x-
4315 /*======================================================================*//**
4317 Poll the underlying socket using the ppoll() method for data that's ready for
4318 receiving (default), etc.
4321 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4322 select(), and accept()/accept4() when using multiple sockets.
4324 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4325 part of the user address space
4326 @throws randolf::rex::xEINTR Interrupted by a signal
4327 @throws randolf::rex::xENOMEM Insufficient memory
4328 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4329 doesn't refer to a socket
4330 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4331 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4332 is a highly improbable chance that a timeout could still occur if the
4333 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4334 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4337 @returns The resulting (short)pollfd.revents bitfield
4339 *///=========================================================================
4341 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4342 const short events = POLLIN,
4343 /// Timeout in seconds
4344 const int tv_sec = 0,
4345 /// Timeout in nanoseconds
4346 const long tv_nsec = 0,
4348 const sigset_t* sigmask = nullptr,
4349 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4350 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4351 struct timespec tmo_p{tv_sec, tv_nsec};
4352 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4353 + ", pollfd.events=" + std::to_string(events)
4354 + ", timespec{tv_sec=" + std::to_string(tmo_p.tv_sec)
4355 + ", tv_nsec=" + std::to_string(tmo_p.tv_nsec) + "}"
4358 struct pollfd fds{__socket_fd, events, 0};
4359 if (__rc_check(::ppoll(&fds, 1, &tmo_p, sigmask)) == 0) {
4360 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4364 }; // -x- short ppoll -x-
4366 /*======================================================================*//**
4368 Send a formatted string to the @ref rsocket endpoint.
4370 The @c format is described in the documentation for the POSIX or Standard C
4371 Library @c printf() function.
4372 @throws randolf::rex::xEBADF The underlying socket is not open
4373 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
4374 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
4375 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
4376 @throws randolf::rex::xENOMEM Insufficient memory
4377 @returns The same rsocket object so as to facilitate stacking
4379 @see is_eol_fix_printf
4386 *///=========================================================================
4388 /// Format string to use
4390 /// Variadic arguments
4394 ::va_start(args, format);
4395 int rc = ::vasprintf(&buf, format, args);
4396 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
4397 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
4398 if (__eol_fix_printf && !__eol.empty()) {
4399 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
4401 __send(str.c_str(), str.length());
4405 } catch (std::exception& e) { // Free buf then re-throw the exception
4406 ::free(buf); // Prevent memory leak when an exception is thrown
4409 } // -x- if __eol_fix_printf -x-
4411 }; // -x- rsocket* printf -x-
4413 /*======================================================================*//**
4415 Send a formatted string to the @ref rsocket endpoint, and append an EoL
4418 The @c format is described in the documentation for the POSIX or Standard C
4419 Library @c printf() function.
4420 @throws randolf::rex::xEBADF The underlying socket is not open
4421 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
4422 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
4423 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
4424 @throws randolf::rex::xENOMEM Insufficient memory
4425 @returns The same rsocket object so as to facilitate stacking
4428 @see is_eol_fix_printf
4435 *///=========================================================================
4436 rsocket* printfline(
4437 /// Format string to use
4439 /// Variadic arguments
4443 ::va_start(args, format);
4444 int rc = ::vasprintf(&buf, format, args);
4445 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
4446 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
4447 if (__eol_fix_printf && !__eol.empty()) {
4448 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
4451 __send(str.c_str(), str.length());
4454 __sendline(buf, rc);
4455 } catch (std::exception& e) { // Free buf then re-throw the exception
4456 ::free(buf); // Prevent memory leak when an exception is thrown
4459 } // -x- if __eol_fix_printf -x-
4461 }; // -x- rsocket* printfline -x-
4463 /*======================================================================*//**
4465 Receive data from the endpoint into a @c std::vector<char> that is allocated
4468 If nbytes is 0, then the internal @ref buffer_size() will be used as the
4469 default, which can also be changed from its compiled-in default of 1024 by
4470 using one of the buffer_size() methods.
4473 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4474 reception, but it's important to note that it does implement temporary
4475 blocking while waiting for data.
4477 @throws randolf::rex::xEBADF The underlying socket is not open
4478 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4480 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4481 part of the user address space
4482 @throws randolf::rex::xEINTR Interrupted by a signal
4483 @throws randolf::rex::xEINVAL Invalid argument passed
4484 @throws randolf::rex::xENOMEM Insufficient memory
4485 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4486 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4487 doesn't refer to a socket
4488 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4491 @returns appropriately-sized vector of characters
4492 @see recv(std::vector<char>, const int)
4493 @see recvz(const size_t, const int)
4495 *///=========================================================================
4496 std::vector<char> recv(
4497 /// Maximum number of bytes to receive
4498 const size_t nbytes = 0,
4499 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4500 const int flags = 0) {
4501 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
4502 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4504 + ", " + std::to_string(buf_size)
4505 + ", " + std::to_string(flags)
4507 std::vector<char> buf(buf_size);
4508 buf.resize(__recv(buf.data(), buf.size(), flags));
4510 }; // -x- std::vector<char> recv -x-
4512 /*======================================================================*//**
4514 Receive data from the endpoint into the @c std::vector object supplied in the
4517 The maximum number of bytes that can be received won't exceed the number of
4518 bytes that the supplied @c std::vector<char> was initialized or resized to.
4521 For @c std::vector it's important that the @c resize() method is used to
4522 pre-allocate the underlying char[] array, instead of the unfortunately-named
4523 @c reserve() method that doesn't pre-allocate, to avoid causing segmentation
4524 faults or other undefined behaviours.
4527 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4528 reception, but it's important to note that it does implement temporary
4529 blocking while waiting for data.
4531 @throws randolf::rex::xEBADF The underlying socket is not open
4532 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4534 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4535 part of the user address space
4536 @throws randolf::rex::xEINTR Interrupted by a signal
4537 @throws randolf::rex::xEINVAL Invalid argument passed
4538 @throws randolf::rex::xENOMEM Insufficient memory
4539 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4540 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4541 doesn't refer to a socket
4542 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4545 @returns The same array that was specified in the @c buf parameter
4546 @see recv(const size_t, const int)
4547 @see recvz(const size_t, const int)
4550 *///=========================================================================
4551 std::vector<char> recv(
4552 /// Target std::vector<char> to receive data into
4553 std::vector<char> buf,
4554 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4555 const int flags = 0) {
4556 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4558 + ", " + std::to_string(buf.size())
4559 + ", " + std::to_string(flags)
4561 buf.resize(__recv(buf.data(), buf.size(), flags));
4563 }; // -x- std::vector<char> recv -x-
4565 /*======================================================================*//**
4567 Receive an ASCIIZ string from the endpoint, including the NULL terminator.
4569 @throws randolf::rex::xEBADF The underlying socket is not open
4570 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4572 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4573 part of the user address space
4574 @throws randolf::rex::xEINTR Interrupted by a signal
4575 @throws randolf::rex::xEINVAL Invalid argument passed
4576 @throws randolf::rex::xENOMEM Insufficient memory
4577 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4578 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4579 doesn't refer to a socket
4580 @throws randolf::rex::xERANGE if no NULL terminator is detected (this may
4581 occur before the underlying ASCIIZ string char* array is allocated)
4582 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4585 @returns Pointer to ASCIIZ string (will need to be deleted by caller)
4586 @see send_asciiz(const char*, const int)
4588 *///=========================================================================
4590 /// Maximum number of bytes to receive
4591 const size_t nbytes = 0,
4592 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4593 const int flags = 0) {
4594 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
4595 if (__debug) debug("recv_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4596 + ", " + std::to_string(buf_size)
4597 + ", " + std::to_string(flags)
4600 // --------------------------------------------------------------------------
4601 // Calculate size of buffer (includes NULL terminator since we'll also be
4602 // receiving this from the endpoint).
4603 // --------------------------------------------------------------------------
4606 // --------------------------------------------------------------------------
4607 // Reduce buffer size to what is actually read (remember: we don't actually
4608 // know where the EoL sequence is yet, or if there even is one).
4609 // --------------------------------------------------------------------------
4610 int max = __recv(&buf, buf_size, MSG_PEEK | flags); // Look-ahead at socket stream (buffer was inflated to include EoL sequence)
4612 for (int i = 0; i < max; i++) {
4616 } // -x- if v[i] -x-
4618 if (len < 0) throw randolf::rex::xERANGE("ERANGE: NULL terminator not found");
4620 // --------------------------------------------------------------------------
4621 // I'd love to use std::shared_ptr<char*> for this next part, but it leads
4622 // to intermittent segmentation faults and/or "malloc(): corrupted top size
4623 // occurs" errors outside of this method. So, my conclusion is that char*
4624 // (and char[], as I've tried with this too) are not properly supported by:
4625 // std::shared_ptr<char*> v = std::make_shared<char*>(new char[len + 1]);
4627 // Using std::string() is really not what we want because there is confusion
4628 // concerning the NULL terminator -- with char* strings, it's crystal clear
4629 // that there must be a NULL terminator (since a length isn't tracked). Now,
4630 // if you want an std::string, just initialize as follows:
4632 // char* temp_string = r.recv_asciiz();
4633 // std::string mystr = std::string(temp_string);
4634 // delete temp_string;
4635 // --------------------------------------------------------------------------
4636 char* v = new char[len + 1];
4637 int rc = __recv(v, len + 1, flags); // Consume with NULL terminator from socket stream
4638// TODO: Free "v" in a catch-and-rethrow block (check other calls to __recv and __send too)
4641 }; // -x- char* recv_asciiz -x-
4643 /*======================================================================*//**
4645 Receive one byte (unsigned 8-bit byte) of data from the endpoint.
4647 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4648 reception, but it's important to note that it does implement temporary
4649 blocking while waiting for data.
4651 @throws randolf::rex::xEBADF The underlying socket is not open
4652 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4654 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4655 part of the user address space
4656 @throws randolf::rex::xEINTR Interrupted by a signal
4657 @throws randolf::rex::xEINVAL Invalid argument passed
4658 @throws randolf::rex::xENOMEM Insufficient memory
4659 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4660 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4661 doesn't refer to a socket
4662 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4665 @returns one unsigned character
4666 @see recv(std::vector<char>, const int)
4667 @see recvz(const size_t, const int)
4672 *///=========================================================================
4674 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4675 const int flags = 0) {
4677 if (__debug) debug("recv_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4679 + ", " + std::to_string(sizeof(buf))
4680 + ", " + std::to_string(flags)
4682 int rc = __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4684 }; // -x- byte recv_byte -x-
4686 /*======================================================================*//**
4688 Receive one character (signed 8-bit byte) of data from the endpoint.
4690 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4691 reception, but it's important to note that it does implement temporary
4692 blocking while waiting for data.
4694 @throws randolf::rex::xEBADF The underlying socket is not open
4695 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4697 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4698 part of the user address space
4699 @throws randolf::rex::xEINTR Interrupted by a signal
4700 @throws randolf::rex::xEINVAL Invalid argument passed
4701 @throws randolf::rex::xENOMEM Insufficient memory
4702 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4703 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4704 doesn't refer to a socket
4705 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4708 @returns one signed character
4709 @see recv(std::vector<char>, const int)
4710 @see recvz(const size_t, const int)
4715 *///=========================================================================
4717 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4718 const int flags = 0) {
4720 if (__debug) debug("recv_char(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4722 + ", " + std::to_string(sizeof(buf))
4723 + ", " + std::to_string(flags)
4725 int rc = __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4726 // How to detect disconnected stream when telnet user presses CTRL-C?
4727 //std::cout << "rc=" << std::to_string(rc) << std::endl; // Debug (to be removed)
4728// TODO: Investigate checking platform's char size and moving signing bit accordingly
4730 }; // -x- char recv_char -x-
4732 /*======================================================================*//**
4734 Receive a data structure from the endpoint.
4736 MSB/LSB considerations are important for any integers within your structure,
4737 so be sure to sanitize them with htons(), ntohs(), and related methods prior
4738 to sending, and then after receiving. This way, your data will be protected
4739 against corruption resulting from byte order differences when communicating
4740 between hardware architectures that differ in MSB and LSB byte ordering.
4742 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4743 reception, but it's important to note that it does implement temporary
4744 blocking while waiting for data.
4746 @throws randolf::rex::xEBADF The underlying socket is not open
4747 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4749 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4750 part of the user address space
4751 @throws randolf::rex::xEINTR Interrupted by a signal
4752 @throws randolf::rex::xEINVAL Invalid argument passed
4753 @throws randolf::rex::xENOMEM Insufficient memory
4754 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4755 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4756 doesn't refer to a socket
4757 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4760 @returns Data structure
4762 *///=========================================================================
4763 template <typename T> T recv_struct(
4764 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4765 const int flags = 0) {
4767 if (__debug) debug("recv_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4769 + ", " + std::to_string(sizeof(buf))
4770 + ", " + std::to_string(flags)
4772 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4774 }; // -x- T recv_struct -x-
4776 /*======================================================================*//**
4778 Receive one 16-bit unsigned integer of data in LSB (little endian) order from
4781 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
4782 reception, which implements temporary blocking while waiting for data.
4784 @throws randolf::rex::xEBADF The underlying socket is not open
4785 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4787 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4788 part of the user address space
4789 @throws randolf::rex::xEINTR Interrupted by a signal
4790 @throws randolf::rex::xEINVAL Invalid argument passed
4791 @throws randolf::rex::xENOMEM Insufficient memory
4792 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4793 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4794 doesn't refer to a socket
4795 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4798 @returns uint16_t (converted to local endianness)
4800 *///=========================================================================
4801 uint16_t recv_uint16_lsb(
4802 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4803 const int flags = 0) {
4805 if (__debug) debug("recv_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4807 + ", " + std::to_string(sizeof(buf))
4808 + ", " + std::to_string(flags)
4810 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4811 return !__endian_is_msb ? buf : ntohs(buf);
4812 }; // -x- uint16_t recv_uint16_lsb -x-
4814 /*======================================================================*//**
4816 Receive one 16-bit unsigned integer of data in MSB (big endian) order from
4819 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
4820 reception, which implements temporary blocking while waiting for data.
4822 @throws randolf::rex::xEBADF The underlying socket is not open
4823 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4825 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4826 part of the user address space
4827 @throws randolf::rex::xEINTR Interrupted by a signal
4828 @throws randolf::rex::xEINVAL Invalid argument passed
4829 @throws randolf::rex::xENOMEM Insufficient memory
4830 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4831 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4832 doesn't refer to a socket
4833 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4836 @returns uint16_t (converted to local endianness)
4838 *///=========================================================================
4839 uint16_t recv_uint16_msb(
4840 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4841 const int flags = 0) {
4843 if (__debug) debug("recv_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4845 + ", " + std::to_string(sizeof(buf))
4846 + ", " + std::to_string(flags)
4848 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4849 return __endian_is_msb ? buf : htons(buf);
4850 }; // -x- uint16_t recv_uint16_msb -x-
4852 /*======================================================================*//**
4854 Receive one 32-bit unsigned integer of data in LSB (little endian) order from
4857 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
4858 reception, which implements temporary blocking while waiting for data.
4860 @throws randolf::rex::xEBADF The underlying socket is not open
4861 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4863 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4864 part of the user address space
4865 @throws randolf::rex::xEINTR Interrupted by a signal
4866 @throws randolf::rex::xEINVAL Invalid argument passed
4867 @throws randolf::rex::xENOMEM Insufficient memory
4868 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4869 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4870 doesn't refer to a socket
4871 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4874 @returns uint32_t (converted to local endianness)
4876 *///=========================================================================
4877 uint32_t recv_uint32_lsb(
4878 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4879 const int flags = 0) {
4881 if (__debug) debug("recv_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4883 + ", " + std::to_string(sizeof(buf))
4884 + ", " + std::to_string(flags)
4886 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4887 return !__endian_is_msb ? buf : htonl(buf);
4888 }; // -x- uint32_t recv_uint32_lsb -x-
4890 /*======================================================================*//**
4892 Receive one 32-bit unsigned integer of data in MSB (big endian) order from
4895 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
4896 reception, which implements temporary blocking while waiting for data.
4898 @throws randolf::rex::xEBADF The underlying socket is not open
4899 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4901 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4902 part of the user address space
4903 @throws randolf::rex::xEINTR Interrupted by a signal
4904 @throws randolf::rex::xEINVAL Invalid argument passed
4905 @throws randolf::rex::xENOMEM Insufficient memory
4906 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4907 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4908 doesn't refer to a socket
4909 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4912 @returns uint32_t (converted to local endianness)
4914 *///=========================================================================
4915 uint32_t recv_uint32_msb(
4916 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4917 const int flags = 0) {
4919 if (__debug) debug("recv_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4921 + ", " + std::to_string(sizeof(buf))
4922 + ", " + std::to_string(flags)
4924 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4925 return __endian_is_msb ? buf : ntohl(buf);
4926 }; // -x- uint32_t recv_uint32_msb -x-
4928 /*======================================================================*//**
4930 Receive one 64-bit unsigned integer of data in LSB (little endian) order from
4933 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
4934 reception, which implements temporary blocking while waiting for data.
4936 @throws randolf::rex::xEBADF The underlying socket is not open
4937 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4939 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4940 part of the user address space
4941 @throws randolf::rex::xEINTR Interrupted by a signal
4942 @throws randolf::rex::xEINVAL Invalid argument passed
4943 @throws randolf::rex::xENOMEM Insufficient memory
4944 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4945 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4946 doesn't refer to a socket
4947 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4950 @returns uint64_t (converted to local endianness)
4952 *///=========================================================================
4953 uint64_t recv_uint64_lsb(
4954 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4955 const int flags = 0) {
4957 if (__debug) debug("recv_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4959 + ", " + std::to_string(sizeof(buf))
4960 + ", " + std::to_string(flags)
4962 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4963 return !__endian_is_msb ? buf : ((((uint64_t)ntohl((buf) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((buf) >> 32)));
4964 }; // -x- uint64_t recv_uint64_lsb -x-
4966 /*======================================================================*//**
4968 Receive one 64-bit unsigned integer of data in MSB (big endian) order from
4971 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
4972 reception, which implements temporary blocking while waiting for data.
4974 @throws randolf::rex::xEBADF The underlying socket is not open
4975 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4977 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4978 part of the user address space
4979 @throws randolf::rex::xEINTR Interrupted by a signal
4980 @throws randolf::rex::xEINVAL Invalid argument passed
4981 @throws randolf::rex::xENOMEM Insufficient memory
4982 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4983 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4984 doesn't refer to a socket
4985 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4988 @returns uint64_t (converted to local endianness)
4990 *///=========================================================================
4991 uint64_t recv_uint64_msb(
4992 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4993 const int flags = 0) {
4995 if (__debug) debug("recv_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4997 + ", " + std::to_string(sizeof(buf))
4998 + ", " + std::to_string(flags)
5000 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5001 return __endian_is_msb ? buf : ((((uint64_t)htonl((buf) & 0xffffffffUL)) << 32) | htonl((uint32_t)((buf) >> 32)));
5002 }; // -x- uint64_t recv_uint64_msb -x-
5004 /*======================================================================*//**
5006 Receive data from a specific endpoint.
5009 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5010 reception, but it's important to note that it does implement temporary
5011 blocking while waiting for data.
5014 This method is not compatible with TLS.
5016 @throws randolf::rex::xEBADF The underlying socket is not open
5017 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5019 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5020 part of the user address space
5021 @throws randolf::rex::xEINTR Interrupted by a signal
5022 @throws randolf::rex::xEINVAL Invalid argument passed
5023 @throws randolf::rex::xENOMEM Insufficient memory
5024 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5025 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5026 doesn't refer to a socket
5027 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5030 @returns Appropriately-sized vector of characters
5032 *///=========================================================================
5033 std::vector<char> recvfrom(
5034 /// Maximum number of bytes to receive
5035 const size_t nbytes,
5036 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5038 /// Target endpoint address structure
5039 struct sockaddr *from,
5040 /// Size of target endpoint structure
5041 socklen_t fromlen = sizeof(sockaddr)) {
5042 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5043 if (__debug) debug("recvfrom(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5045 + ", " + std::to_string(buf_size)
5046 + ", " + std::to_string(flags)
5048 + ", " + std::to_string(fromlen)
5050 std::vector<char> v(buf_size);
5051 v.resize(__track_bytes_rx(__rc_check(::recvfrom(__socket_fd, v.data(), v.size(), flags, from, &fromlen))));
5053 }; // -x- std::vector<char> recvfrom -x-
5056 /*======================================================================*//**
5058 This is an internal function that:
5059 1. receives data from the endpoint:
5060 - via underlying socket when TLS is not enabled
5061 - via the OpenSSL socket API when TLS is enabled
5062 2. checks for a socket I/O error (and throws an exception)
5063 3. tracks number of bytes received (if there were no errors)
5065 @throws randolf::rex::xEAGAIN The underlying socket timed out
5066 @throws randolf::rex::xEBADF The underlying socket is not open
5067 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5069 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5070 part of the user address space
5071 @throws randolf::rex::xEINTR Interrupted by a signal
5072 @throws randolf::rex::xEINVAL Invalid argument passed
5073 @throws randolf::rex::xENOMEM Insufficient memory
5074 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5075 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5076 doesn't refer to a socket
5077 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5080 @returns Number of bytes that were successfully received
5082 *///=========================================================================
5086 /// Length of message data
5088 /// Flags (ignored with encrypted streams, except for the MSG_PEEK flag)
5089 const int flags = 0) {
5091// 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
5093 // --------------------------------------------------------------------------
5094 // When internal buffering is not set up (which is the default operation), we
5095 // simply passing data directly.
5096 // --------------------------------------------------------------------------
5097 if (__buffer == nullptr) {
5098 return __tls ? (flags & MSG_PEEK ? __rc_check_tls(SSL_peek(__tls_fd, data, len))
5099 : __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, data, len))))
5100 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, data, len, flags)));
5101 } // -x- if !__buffer -x-
5103 // --------------------------------------------------------------------------
5104 // Consume data from socket to make up for OpenSSL's inability to read beyond
5105 // one packet of data in its SSL_peek() and SSL_read() methods, and also some
5106 // inconsistencies with certain raw (unencrypted) socket implementations that
5107 // occasionally refuse to read beyond one packet of data (at least on a first
5108 // attempt to read with or without the MSG_PEEK flag).
5109 // --------------------------------------------------------------------------
5112std::cout << "-(0)- __buffer=" << (long)__buffer << std::endl
5113 << " __buffer_head=" << (long)__buffer_head << std::endl
5114 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5115 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5116 << " __buffer_size=" << __buffer_size << std::endl
5117 << " len=" << len << std::endl;
5119 // --------------------------------------------------------------------------
5120 // Fill internal buffer. It may be more than len is set to, but this is okay
5121 // because we're committed to using internal buffering now anyway (it's far
5122 // more likely that a protocol that requires the use of recvline() once is
5123 // going to be using it repeatedly).
5124 // --------------------------------------------------------------------------
5127 // --------------------------------------------------------------------------
5128 // If the amount of data contained in the internal buffer is sufficient to
5129 // satisfy the amount of data that "len" represents, then skip the attempts
5130 // to read from the socket and just drain from the buffer what's needed.
5131 // --------------------------------------------------------------------------
5132 if ((__buffer_head >= __buffer_tail // Does the buffer wrap around?
5133 ? __buffer_head - __buffer_tail // No, so calcluation is straight-forward
5134 : (__buffer_boundary - __buffer_tail) // Yes, so we need the sum of both parts
5135 + (__buffer_head - __buffer)) >= len) goto __recv_drain;
5138 // --------------------------------------------------------------------------
5139 // Tail is behind head, so do one extra read beforehand to fill it up, which
5140 // affects the first part of a wrap-around since this is a circular buffer.
5141 // --------------------------------------------------------------------------
5142 if (__buffer_head >= __buffer_tail) { // std::cout << "__buffer_head >= __buffer_tail" << std::endl;
5143 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
5144 : __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
5145//std::cout << "n(0)=" << n << std::endl;
5146 if ((__buffer_head += n) > __buffer_boundary) __buffer_head = __buffer; // Advance head (and wrap around if we're at the boundary)
5147// TODO: Test wrap-around, make sure we don't need >= instead of >
5148 } // -x- if __buffer_head -x-
5151std::cout << "-(1)- __buffer=" << (long)__buffer << std::endl
5152 << " __buffer_head=" << (long)__buffer_head << std::endl
5153 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5154 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5155 << " __buffer_size=" << __buffer_size << std::endl
5156 << " len=" << len << std::endl;
5158//std::cout << "Buffer: [" << __buffer_tail[0] << "]" << std::endl;
5160 // --------------------------------------------------------------------------
5161 // Fill in the remaining (unused) portion of the internal buffer.
5162 // --------------------------------------------------------------------------
5163 if (__buffer_head < __buffer_tail && ioctl(__socket_fd, FIONREAD, &n) == 0) {
5164 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
5165 : __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
5166//std::cout << "n(1)=" << n << std::endl;
5167 __buffer_head += n; // Advance the head
5168 } // -x- if __buffer_head -x-
5170 // --------------------------------------------------------------------------
5171 // Check if more bytes are available for reading, then run the loop again so
5172 // that we're pulling in as many bytes as possible. This makes up for a
5173 // problem with socket I/O functions not always returning all the data that's
5174 // ready to be consumed.
5175 // --------------------------------------------------------------------------
5176// if (__buffer_head != __buffer_tail && ioctl(__socket_fd, FIONREAD, &n) == 0 && n > 0) goto __recv_loop;
5177//std::cout << "Buffer size = " << __buffer_size << std::endl; // Debug
5178 // --------------------------------------------------------------------------
5179 // Drain the internal buffer. If we can drain it completely, we'll either
5180 // reset head and tail (default), or free() the internal buffer (based on the
5181 // internal policy). TODO: Create policy control options
5183 // MSG_PEEK mode is limited to the internal buffer size. Not using this flag
5184 // is not limited, ... TODO: Figure out how to deal with this
5185 // --------------------------------------------------------------------------
5187 char* drain = __buffer_tail;
5188//std::cout << "Drain: [";
5189 for (n = 0; n < len; n++) {
5190//if (*drain < 32) std::cout << "^" << (char)(drain[0] + 64); else std::cout << drain[0];
5191 ((char*)data)[n] = *drain++; // Copy byte, and advance the drain
5192//std::cout << "{" << (long)drain << "}";
5193 if (drain > __buffer_boundary) drain = __buffer; // Wrap around // TODO: Test this
5194 if (drain == __buffer_head) break; // We've reached the limit; there's no more data // TODO: Move this to earlier to avoid buffer overrun
5197//std::cout << "] n=" << ++n << std::endl;
5199 // --------------------------------------------------------------------------
5200 // Consume data in __buffer if this isn't a MSG_PEEK operation.
5201 // --------------------------------------------------------------------------
5203std::cout << "-(2)- __buffer=" << (long)__buffer << std::endl
5204 << " __buffer_head=" << (long)__buffer_head << std::endl
5205 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5206 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5207 << " MSG_PEEK=" << (flags && MSG_PEEK ? "true" : "false") << std::endl
5208 << " len=" << len << std::endl
5209 << " drain=" << (long)drain << std::endl;
5211// if ((flags & MSG_PEEK) == 0) std::cout << "[Consuming buffer]" << std::endl;
5212 if ((flags & MSG_PEEK) == 0) __buffer_tail = drain; // Consume character(s) unless MSG_PEEK flag is set
5214std::cout << "-(3)- __buffer=" << (long)__buffer << std::endl
5215 << " __buffer_head=" << (long)__buffer_head << std::endl
5216 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5217 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5218 << " MSG_PEEK=" << (flags && MSG_PEEK ? "true" : "false") << std::endl
5219 << " len=" << len << std::endl
5220 << " drain=" << (long)drain << std::endl;
5224 }; // -x- int __recv -x-
5227 /*======================================================================*//**
5229 Receive a line of data from the endpoint, with the EoL character(s) removed.
5230 This is meant for binary data that isn't expected to always be plain text.
5232 If nbytes is 0, then the internal buffer_size will be used as the default,
5233 which can also be changed from its compiled-in default of 1024 by using one
5234 of the buffer_size() methods.
5236 Internally, the length of the @ref eol() sequence is added to the buffer size
5237 so that the maximum line length without EoL characters matches the maximum
5238 that was specified with @ref buffer_size() or with the @c nbytes parameter.
5240 The @c timeout parameter can be used to override the total overall timeout
5241 for receiving a line, which is useful for specific situations where a
5242 specific timeout desired for particular prompts, during certain data entry
5246 If you're searching for a readline() method for socket I/O, then this is most
5247 likely what you're wanting/needing.
5250 When setting the recvline timeout (either with the @c timeout parameter with
5251 this method, or by using the @ref timeout_recvline(long) method), you may
5252 also need to set the socket timeout beforehand using the @ref timeout()
5253 method, because @c recvline doesn't do this...
5256 @ref timeout_recvline sets the overall total timeout for an entire line to
5259 @ref timeout sets the timeout between individual characters received (such
5260 as keystrokes from a live end-user)
5263 If your socket timeout is longer than then recvline timeout, this will have
5264 the effect of rendering the recvling timeout as ineffective.
5266 @throws randolf::rex::xEAGAIN if timeout occurs before EoL
5267 @throws randolf::rex::xEBADF The underlying socket is not open
5268 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5270 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5271 part of the user address space
5272 @throws randolf::rex::xEINTR Interrupted by a signal
5273 @throws randolf::rex::xEINVAL Invalid argument passed
5274 @throws randolf::rex::xENOMEM Insufficient memory
5275 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5276 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5277 doesn't refer to a socket
5278 @throws randolf::rex::xERANGE if the timeout parameter is below 0
5279 @throws randolf::rex::xEOVERFLOW if no EoL character sequence is detected
5280 after recvline's buffer became full (whatever data is waiting may
5281 still be received using any of the recv_ methods with or without the
5282 MSG_PEEK flag set, including recvline() with a larger buffer size
5283 specified with the @c nbytes parameter)
5284 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5287 @returns One line of text as a std::string object.
5289 @see sendline(const std::string, const int)
5291 @see timeout_recvline
5292 @see timeout_recvline(long)
5294 *///=========================================================================
5295 std::string recvline(
5296 /// Maximum number of bytes to receive
5297 const size_t nbytes = 0,
5298 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5299 const int flags = 0,
5300 /// Line timeout (in seconds)@n
5301 /// 0 = no timeout (default), unless it was configured by way of the
5302 /// @ref timeout_recvline(long) method
5304 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size) // If 0, then set this to the internal __buffer_size default
5305 + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF
5306 if (__debug) debug("recvline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5307 + ", " + std::to_string(buf_size)
5308 + ", " + std::to_string(flags)
5311 // --------------------------------------------------------------------------
5312 // Syntax checks as part of preparation of timeout variable for faster
5313 // comparisons within line-reading loop.
5314 // --------------------------------------------------------------------------
5315 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
5316 if (timeout == 0) timeout = __recvline_timeout;
5317 timeout = timeout == 0 ? LONG_MAX : timeout + time(0);
5319 // --------------------------------------------------------------------------
5320 // Allocate line buffer.
5321 // --------------------------------------------------------------------------
5322 std::string v; v.resize(buf_size); // Add EoL size to buffer size; we'll probably be shrinking later anyway
5324 // --------------------------------------------------------------------------
5325 // Create internal buffer for __recv (if it hasn't already been created).
5327 // Buffering becomes necessary with TLS connection because SSL_peek fails to
5328 // return data that spans multiple packets. What triggers this behaviour is
5329 // the command "stty -icanon && openssl s_client host_name tcp_port" followed
5330 // by manual interactive typing some text then pressing the "[Enter]" key.
5331 // --------------------------------------------------------------------------
5332 if (__buffer == nullptr) {
5333 size_t bsize = std::max(__buffer_size, buf_size);
5334 __buffer = (char*)(::malloc(bsize)); //new char[bsize];
5335 __buffer_head = __buffer;
5336 __buffer_tail = __buffer;
5337 __buffer_boundary = __buffer + bsize;
5338 } // -x- if !__buffer -x-
5340 // --------------------------------------------------------------------------
5341 // Line-reading loop.
5342 // --------------------------------------------------------------------------
5343 int len_with_eol = 0;
5345 int br = 0; // Number of Bytes Received
5347 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)
5348 } 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
5349 //goto recvline_check;
5350std::cout << "EAGAIN caught" << std::endl;
5351 __rc_check(nanosleep(&__recvline_idle, nullptr)); // Prevent tight CPU utilization loop; EINVAL = bad parameters (e.g., tv_nsec > 999999999 is not uncommon)
5353 if (time(0) > timeout) throw randolf::rex::xEAGAIN("recvline timed out");
5354 int len = eol_index(v, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
5356 if (br >= buf_size) throw randolf::rex::xEOVERFLOW("recvline buffer became full before EoL");
5357 goto recvline_check;
5358 } // -x- if !len -x-
5360 // --------------------------------------------------------------------------
5362 // --------------------------------------------------------------------------
5363 if ((flags & MSG_PEEK) == 0) __recv(v.data(), len_with_eol, flags); // Drain 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
5364 v.resize(len); // Truncate string to exclude unused portion
5367 }; // -x- std::string recvline -x-
5369 /*======================================================================*//**
5371 Receive data in the form of a "msghdr" structure.
5373 This method is not compatible with TLS.
5375 @throws randolf::rex::xEBADF The underlying socket is not open
5376 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5378 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5379 part of the user address space
5380 @throws randolf::rex::xEINTR Interrupted by a signal
5381 @throws randolf::rex::xEINVAL Invalid argument passed
5382 @throws randolf::rex::xENOMEM Insufficient memory
5383 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5384 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5385 doesn't refer to a socket
5386 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5389 @returns pointer to "msghdr" structure
5391 *///=========================================================================
5393 /// Pointer to "msghdr" structure (or nullptr for automatic allocation)
5395 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5396 const int flags = 0) {
5397 if (__debug) debug("recvmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5399 + ", " + std::to_string(flags)
5401 if (msg == nullptr) msg = new msghdr{0};
5402 __track_bytes_rx(__rc_check(::recvmsg(__socket_fd, msg, flags)));
5404 }; // -x- msghdr* recvmsg -x-
5406 /*======================================================================*//**
5408 Receive data in the form of an "mmsghdr" structure.
5410 This method is not compatible with TLS.
5412 @throws randolf::rex::xEBADF The underlying socket is not open
5413 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5415 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5416 part of the user address space
5417 @throws randolf::rex::xEINTR Interrupted by a signal
5418 @throws randolf::rex::xEINVAL Invalid argument passed
5419 @throws randolf::rex::xENOMEM Insufficient memory
5420 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5421 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5422 doesn't refer to a socket
5423 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5426 @returns pointer to "mmsghdr" structure
5428 *///=========================================================================
5430 /// Pointer to "mmsghdr" structure (or nullptr for automatic allocation)
5431 struct mmsghdr* mmsg,
5432 /// Size of target endpoint structure
5433 const unsigned int vlen = sizeof(mmsghdr),
5434 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
5435 const int flags = 0,
5437 struct timespec* timeout = {0}) {
5438 if (__debug) debug("recvmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5440 + ", " + std::to_string(vlen)
5441 + ", " + std::to_string(flags)
5442 + ", timeout{tv_sec=" + std::to_string(timeout->tv_sec)
5443 + ", tv_nsec=" + std::to_string(timeout->tv_nsec) + "}"
5445 __track_bytes_rx(__rc_check(::recvmmsg(__socket_fd, mmsg, vlen, flags, timeout)));
5447 }; // -x- mmsghdr* recvmmsg -x-
5449 /*======================================================================*//**
5451 Receive data from the endpoint, and add a 0 (null) onto the end. This is
5452 useful when using the resulting std::vector<char> as an ASCIIZ string.
5454 If nbytes is 0, then the internal buffer_size will be used as the default,
5455 which can also be changed from its compiled-in default of 1024 by using one
5456 of the buffer_size() methods.
5459 The resulting std::vector size is always inflated by 1. This means that
5460 relying on a comparison against 0 will result in an infinite loop:
5462 for (std::vector<char> v = r.recvz(); v.size > 0; v = recvz()) { ... }
5464 So, you'll need to compare against 1 instead of 0 to compensate fot the
5465 inflated size due to the addition of null character 0:
5467 for (std::vector<char> v = r.recvz(); v.size > 1; v = recvz()) { ... }
5471 The resulting size of std::vector<char> will be <tt>nbytes + 1</tt> which
5472 ensures that any string processing functions or presentation libraries - such
5473 as @c printf() - expecting an ASCIIZ string buffer will stop at null (0), and
5474 will reliably output no more than the maximum size specified.
5476 The way to think of this is that @c nbytes specifies the maximum number of
5477 bytes (a.k.a., characters) to recieve over the underlying socket, and the
5478 final 0 (null) being added is not included in the maximum specified by the
5479 @c nbytes parameter.
5482 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5483 reception, but it's important to note that it does implement temporary
5484 blocking while waiting for data.
5486 @throws randolf::rex::xEBADF The underlying socket is not open
5487 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5489 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5490 part of the user address space
5491 @throws randolf::rex::xEINTR Interrupted by a signal
5492 @throws randolf::rex::xEINVAL Invalid argument passed
5493 @throws randolf::rex::xENOMEM Insufficient memory
5494 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5495 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5496 doesn't refer to a socket
5497 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5500 @returns appropriately-sized vector of characters + 1 (includes additional
5501 null terminator character 0)
5502 @see recv(const size_t, const int)
5503 @see recv(std::vector<char>, const int)
5505 *///=========================================================================
5506 std::vector<char> recvz(
5507 /// Maximum number of bytes to receive
5508 const size_t nbytes = 0,
5509 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5510 const int flags = 0) {
5511 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5512 if (__debug) debug("recvz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5514 + ", " + std::to_string(buf_size)
5515 + ", " + std::to_string(flags)
5517 std::vector<char> v(buf_size);
5518 v.resize(__recv(v.data(), v.size(), flags) + 1); // Add 1 to include room for final null character 0
5519 v[v.size() - 1] = (char)0; // Set final element to 0 (null)
5521 }; // -x- std::vector<char> recvz -x-
5524 /*======================================================================*//**
5526 This is an internal function that:
5527 1. sends data to the endpoint:
5528 - via underlying socket when TLS is not enabled
5529 - via the OpenSSL socket API when TLS is enabled
5530 2. checks for a socket I/O error (and throws an exception)
5531 3. tracks number of bytes transmitted (if there were no errors)
5534 This method is threadsafe because the underlying POSIX send() and OpenSSL's
5535 SSL_write() functions are threadsafe.
5537 @throws randolf::rex::xEBADF The underlying socket is not open
5538 @throws randolf::rex::xECONNRESET Connect reset by peer
5539 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5540 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5541 part of the user address space
5542 @throws randolf::rex::xEINTR Interrupted by a signal
5543 @throws randolf::rex::xEINVAL Invalid argument passed
5544 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5545 occur, but the POSIX sockets documentation lists it as one of the
5546 errors that can be returned, perhaps because some incorrectly
5547 implemented TCP/IP stacks return this error?)
5548 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5549 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5550 and 65,527 bytes for IPv6)
5551 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5552 network congestion (or, less commonly, insufficient memory)
5553 @throws randolf::rex::xENOMEM Insufficient memory
5554 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5555 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5556 doesn't refer to a socket
5557 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5558 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5559 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5561 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5563 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5564 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5565 but it really isn't)
5567 @returns Number of bytes that were successfully transmitted
5569 *///=========================================================================
5573 /// Length of message data
5575 /// Flags (ignored with encrypted streams)
5576 const int flags = 0) {
5577 if (__tls) return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data, len)));
5578 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data, len, flags)));
5579 }; // -x- int __send -x-
5581 /*======================================================================*//**
5583 This is an internal function that:
5584 1. sends data to the endpoint:
5585 - via underlying socket when TLS is not enabled
5586 - via the OpenSSL socket API when TLS is enabled
5587 2. checks for a socket I/O error (and throws an exception)
5588 3. tracks number of bytes transmitted (if there were no errors)
5591 This method is threadsafe because the underlying POSIX send() and OpenSSL's
5592 SSL_write() functions are threadsafe.
5594 @throws randolf::rex::xEBADF The underlying socket is not open
5595 @throws randolf::rex::xECONNRESET Connect reset by peer
5596 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5597 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5598 part of the user address space
5599 @throws randolf::rex::xEINTR Interrupted by a signal
5600 @throws randolf::rex::xEINVAL Invalid argument passed
5601 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5602 occur, but the POSIX sockets documentation lists it as one of the
5603 errors that can be returned, perhaps because some incorrectly
5604 implemented TCP/IP stacks return this error?)
5605 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5606 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5607 and 65,527 bytes for IPv6)
5608 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5609 network congestion (or, less commonly, insufficient memory)
5610 @throws randolf::rex::xENOMEM Insufficient memory
5611 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5612 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5613 doesn't refer to a socket
5614 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5615 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5616 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5618 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5620 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5621 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5622 but it really isn't)
5624 @returns Number of bytes that were successfully transmitted
5626 *///=========================================================================
5630 /// Number of bytes to send
5632 /// Flags (ignored with encrypted streams)
5633 const int flags = 0) {
5634 if (__tls) { // Encrypted
5635 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data , len )))
5636 + __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.c_str(), __eol_out.length())));
5637 } else { // Not encrypted
5638 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data , len , MSG_MORE | flags))) // MSG_MORE prevents extra packets from being sent
5639 + __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.c_str(), __eol_out.length(), flags)));
5640 } // -x- if __tls -x-
5641 }; // -x- int __sendline -x-
5643 /*======================================================================*//**
5645 This is an internal function that:
5646 1. sends data to the endpoint:
5647 - via underlying socket when TLS is not enabled
5648 - via the OpenSSL socket API when TLS is enabled
5649 2. checks for a socket I/O error (and throws an exception)
5650 3. tracks number of bytes transmitted (if there were no errors)
5653 This method is threadsafe because the underlying POSIX send() and OpenSSL's
5654 SSL_write() functions are threadsafe.
5656 @throws randolf::rex::xEBADF The underlying socket is not open
5657 @throws randolf::rex::xECONNRESET Connect reset by peer
5658 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5659 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5660 part of the user address space
5661 @throws randolf::rex::xEINTR Interrupted by a signal
5662 @throws randolf::rex::xEINVAL Invalid argument passed
5663 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5664 occur, but the POSIX sockets documentation lists it as one of the
5665 errors that can be returned, perhaps because some incorrectly
5666 implemented TCP/IP stacks return this error?)
5667 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5668 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5669 and 65,527 bytes for IPv6)
5670 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5671 network congestion (or, less commonly, insufficient memory)
5672 @throws randolf::rex::xENOMEM Insufficient memory
5673 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5674 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5675 doesn't refer to a socket
5676 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5677 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5678 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5680 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5682 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5683 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5684 but it really isn't)
5686 @returns Number of bytes that were successfully transmitted
5688 *///=========================================================================
5690 /// Flags (ignored with encrypted streams)
5691 const int flags = 0) {
5692 if (__tls) { // Encrypted
5693 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.c_str(), __eol_out.length())));
5694 } else { // Not encrypted
5695 return __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.c_str(), __eol_out.length(), flags)));
5696 } // -x- if __tls -x-
5697 }; // -x- int __send_eol -x-
5698 // TODO: Create a recv_eol() method that some people will probably consider to be evil (ha ha!)
5699 // recv_eol() will need to be able to auto-detect the CRLF sequence on-the-fly (if necessary) just like recvline() does
5702 /*======================================================================*//**
5704 Send data in the form of a std::string to the endpoint.
5706 This method is threadsafe.
5708 @throws randolf::rex::xEBADF The underlying socket is not open
5709 @throws randolf::rex::xECONNRESET Connect reset by peer
5710 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5711 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5712 part of the user address space
5713 @throws randolf::rex::xEINTR Interrupted by a signal
5714 @throws randolf::rex::xEINVAL Invalid argument passed
5715 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5716 occur, but the POSIX sockets documentation lists it as one of the
5717 errors that can be returned, perhaps because some incorrectly
5718 implemented TCP/IP stacks return this error?)
5719 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5720 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5721 and 65,527 bytes for IPv6)
5722 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5723 network congestion (or, less commonly, insufficient memory)
5724 @throws randolf::rex::xENOMEM Insufficient memory
5725 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5726 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5727 doesn't refer to a socket
5728 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5729 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5730 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5732 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5734 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5735 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5736 but it really isn't)
5738 @returns The same rsocket object so as to facilitate stacking
5740 *///=========================================================================
5743 const std::string msg,
5744 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
5745 const int flags = 0) {
5746 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5748 + ", " + std::to_string(msg.length())
5749 + ", " + std::to_string(flags)
5751 __send(msg.c_str(), msg.length(), flags);
5753 }; // -x- rsocket* send -x-
5755 /*======================================================================*//**
5757 Send data in the form of a std::vector<char> to the endpoint.
5759 This method is threadsafe.
5761 @throws randolf::rex::xEBADF The underlying socket is not open
5762 @throws randolf::rex::xECONNRESET Connect reset by peer
5763 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5764 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5765 part of the user address space
5766 @throws randolf::rex::xEINTR Interrupted by a signal
5767 @throws randolf::rex::xEINVAL Invalid argument passed
5768 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5769 occur, but the POSIX sockets documentation lists it as one of the
5770 errors that can be returned, perhaps because some incorrectly
5771 implemented TCP/IP stacks return this error?)
5772 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5773 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5774 and 65,527 bytes for IPv6)
5775 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5776 network congestion (or, less commonly, insufficient memory)
5777 @throws randolf::rex::xENOMEM Insufficient memory
5778 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5779 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5780 doesn't refer to a socket
5781 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5782 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5783 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5785 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5787 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5788 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5789 but it really isn't)
5791 @returns The same rsocket object so as to facilitate stacking
5793 *///=========================================================================
5796 const std::vector<char> msg,
5797 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
5798 const int flags = 0) {
5799 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5801 + ", " + std::to_string(msg.size())
5802 + ", " + std::to_string(flags)
5804 __send(msg.data(), msg.size(), flags);
5806 }; // -x- rsocket* send -x-
5808 /*======================================================================*//**
5810 Send data in the form of a C-string to the endpoint.
5812 This method is threadsafe.
5814 @throws randolf::rex::xEBADF The underlying socket is not open
5815 @throws randolf::rex::xECONNRESET Connect reset by peer
5816 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5817 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5818 part of the user address space
5819 @throws randolf::rex::xEINTR Interrupted by a signal
5820 @throws randolf::rex::xEINVAL Invalid argument passed
5821 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5822 occur, but the POSIX sockets documentation lists it as one of the
5823 errors that can be returned, perhaps because some incorrectly
5824 implemented TCP/IP stacks return this error?)
5825 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5826 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5827 and 65,527 bytes for IPv6)
5828 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5829 network congestion (or, less commonly, insufficient memory)
5830 @throws randolf::rex::xENOMEM Insufficient memory
5831 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5832 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5833 doesn't refer to a socket
5834 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5835 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5836 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5838 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5840 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5841 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5842 but it really isn't)
5844 @returns The same rsocket object so as to facilitate stacking
5847 *///=========================================================================
5849 /// Pointer to data to send
5851 /// Number of bytes to send, or 0 to auto-detect length if message is an ASCIIZ string
5853 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
5854 const int flags = 0) {
5855 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5857 + ", " + std::to_string(len)
5858 + ", " + std::to_string(flags)
5861 // --------------------------------------------------------------------------
5862 // Measure size of format string if an ASCIIZ string was indicated.
5863 // --------------------------------------------------------------------------
5864 if (len == 0) len = std::strlen(msg);
5866 // --------------------------------------------------------------------------
5868 // --------------------------------------------------------------------------
5869 __send(msg, len, flags);
5871 }; // -x- rsocket* send -x-
5873 /*======================================================================*//**
5875 Send data in the form of an ASCIIZ string to the endpoint, including the
5876 terminating NULL character.
5878 This method is threadsafe.
5880 @throws randolf::rex::xEBADF The underlying socket is not open
5881 @throws randolf::rex::xECONNRESET Connect reset by peer
5882 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5883 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5884 part of the user address space
5885 @throws randolf::rex::xEINTR Interrupted by a signal
5886 @throws randolf::rex::xEINVAL Invalid argument passed
5887 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5888 occur, but the POSIX sockets documentation lists it as one of the
5889 errors that can be returned, perhaps because some incorrectly
5890 implemented TCP/IP stacks return this error?)
5891 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5892 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5893 and 65,527 bytes for IPv6)
5894 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5895 network congestion (or, less commonly, insufficient memory)
5896 @throws randolf::rex::xENOMEM Insufficient memory
5897 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5898 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5899 doesn't refer to a socket
5900 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5901 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5902 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5904 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5906 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5907 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5908 but it really isn't)
5910 @returns The same rsocket object so as to facilitate stacking
5911 @see recv_asciiz(const size_t, const int)
5912 @see sendz(const char*, const int) which doesn't transmit the terminating
5915 *///=========================================================================
5916 rsocket* send_asciiz(
5917 /// Pointer to data to send
5919 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
5920 const int flags = 0) {
5921 if (__debug) debug("send_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5923 + ", " + std::to_string(std::strlen(msg) + 1)
5924 + ", " + std::to_string(flags)
5926 __send(msg, std::strlen(msg) + 1, flags);
5928 }; // -x- rsocket* send_asciiz -x-
5930 /*======================================================================*//**
5932 Send one 8-bit byte (one unsigned character) of data to the endpoint.
5934 This method is threadsafe.
5936 @throws randolf::rex::xEBADF The underlying socket is not open
5937 @throws randolf::rex::xECONNRESET Connect reset by peer
5938 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5939 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5940 part of the user address space
5941 @throws randolf::rex::xEINTR Interrupted by a signal
5942 @throws randolf::rex::xEINVAL Invalid argument passed
5943 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5944 occur, but the POSIX sockets documentation lists it as one of the
5945 errors that can be returned, perhaps because some incorrectly
5946 implemented TCP/IP stacks return this error?)
5947 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5948 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5949 and 65,527 bytes for IPv6)
5950 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5951 network congestion (or, less commonly, insufficient memory)
5952 @throws randolf::rex::xENOMEM Insufficient memory
5953 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5954 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5955 doesn't refer to a socket
5956 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5957 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5958 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5960 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5962 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5963 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5964 but it really isn't)
5966 @returns The same rsocket object so as to facilitate stacking
5971 *///=========================================================================
5975 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
5976 const int flags = 0) {
5977 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5979 + ", " + std::to_string(sizeof(value))
5980 + ", " + std::to_string(flags)
5982 __send(&value, sizeof(value), flags);
5984 }; // -x- rsocket* send_byte -x-
5986 /*======================================================================*//**
5988 Send one signed character (one 8-bit byte) of data to the endpoint.
5989 @copydoc send_byte(char, int)
5990 @returns The same rsocket object so as to facilitate stacking
5995 *///=========================================================================
5999 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6000 const int flags = 0) {
6001 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6003 + ", " + std::to_string(sizeof(value))
6004 + ", " + std::to_string(flags)
6006 __send(&value, sizeof(value), flags);
6008 }; // -x- rsocket* send_char -x-
6010 /*======================================================================*//**
6012 Send the EoL sequence to the endpoint.
6014 This method is threadsafe.
6016 @throws randolf::rex::xEBADF The underlying socket is not open
6017 @throws randolf::rex::xECONNRESET Connect reset by peer
6018 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6019 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6020 part of the user address space
6021 @throws randolf::rex::xEINTR Interrupted by a signal
6022 @throws randolf::rex::xEINVAL Invalid argument passed
6023 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6024 occur, but the POSIX sockets documentation lists it as one of the
6025 errors that can be returned, perhaps because some incorrectly
6026 implemented TCP/IP stacks return this error?)
6027 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6028 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6029 and 65,527 bytes for IPv6)
6030 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6031 network congestion (or, less commonly, insufficient memory)
6032 @throws randolf::rex::xENOMEM Insufficient memory
6033 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6034 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6035 doesn't refer to a socket
6036 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6037 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6038 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6040 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6042 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6043 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6044 but it really isn't)
6046 @returns The same rsocket object so as to facilitate stacking
6048 *///=========================================================================
6050 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6051 const int flags = 0) {
6052 if (__debug) debug("send_eol(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6053 + ", " + std::to_string(flags)
6057 }; // -x- rsocket* send_eol -x-
6059 /*======================================================================*//**
6061 Send one byte of data to the endpoint.
6063 MSB/LSB considerations are important for any integers within your structure,
6064 so be sure to sanitize them with htons(), ntohs(), and related methods prior
6065 to sending, and then after receiving. This way, your data will be protected
6066 against corruption resulting from byte order differences when communicating
6067 between hardware architectures that differ in MSB and LSB byte ordering.
6069 This method is threadsafe.
6071 @throws randolf::rex::xEBADF The underlying socket is not open
6072 @throws randolf::rex::xECONNRESET Connect reset by peer
6073 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6074 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6075 part of the user address space
6076 @throws randolf::rex::xEINTR Interrupted by a signal
6077 @throws randolf::rex::xEINVAL Invalid argument passed
6078 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6079 occur, but the POSIX sockets documentation lists it as one of the
6080 errors that can be returned, perhaps because some incorrectly
6081 implemented TCP/IP stacks return this error?)
6082 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6083 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6084 and 65,527 bytes for IPv6)
6085 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6086 network congestion (or, less commonly, insufficient memory)
6087 @throws randolf::rex::xENOMEM Insufficient memory
6088 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6089 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6090 doesn't refer to a socket
6091 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6092 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6093 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6095 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6097 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6098 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6099 but it really isn't)
6101 @returns The same rsocket object so as to facilitate stacking
6103 *///=========================================================================
6104 template <typename T> rsocket* send_struct(
6107 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6108 const int flags = 0) {
6109 if (__debug) debug("send_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6111 + ", " + std::to_string(sizeof(value))
6112 + ", " + std::to_string(flags)
6114 __send(&value, sizeof(value), flags);
6116 }; // -x- rsocket* send_struct -x-
6118 /*======================================================================*//**
6120 Send one 16-bit unsigned integer of data in LSB (little endian) order to the
6123 This method is threadsafe.
6125 @throws randolf::rex::xEBADF The underlying socket is not open
6126 @throws randolf::rex::xECONNRESET Connect reset by peer
6127 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6128 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6129 part of the user address space
6130 @throws randolf::rex::xEINTR Interrupted by a signal
6131 @throws randolf::rex::xEINVAL Invalid argument passed
6132 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6133 occur, but the POSIX sockets documentation lists it as one of the
6134 errors that can be returned, perhaps because some incorrectly
6135 implemented TCP/IP stacks return this error?)
6136 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6137 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6138 and 65,527 bytes for IPv6)
6139 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6140 network congestion (or, less commonly, insufficient memory)
6141 @throws randolf::rex::xENOMEM Insufficient memory
6142 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6143 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6144 doesn't refer to a socket
6145 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6146 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6147 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6149 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6151 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6152 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6153 but it really isn't)
6155 @returns The same rsocket object so as to facilitate stacking
6157 *///=========================================================================
6158 rsocket* send_uint16_lsb(
6160 const uint16_t value,
6161 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6162 const int flags = 0) {
6163 uint16_t buf = !__endian_is_msb ? value : ntohs(value);
6164 if (__debug) debug("send_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6165 + ", " + std::to_string(value)
6166 + ", " + std::to_string(sizeof(buf))
6167 + ", " + std::to_string(flags)
6169 __send(&buf, sizeof(buf), flags);
6171 }; // -x- rsocket* send_uint16_lsb -x-
6173 /*======================================================================*//**
6175 Send one 16-bit integer of data in MSB (big endian) order to the endpoint.
6177 This method is threadsafe.
6179 @throws randolf::rex::xEBADF The underlying socket is not open
6180 @throws randolf::rex::xECONNRESET Connect reset by peer
6181 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6182 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6183 part of the user address space
6184 @throws randolf::rex::xEINTR Interrupted by a signal
6185 @throws randolf::rex::xEINVAL Invalid argument passed
6186 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6187 occur, but the POSIX sockets documentation lists it as one of the
6188 errors that can be returned, perhaps because some incorrectly
6189 implemented TCP/IP stacks return this error?)
6190 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6191 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6192 and 65,527 bytes for IPv6)
6193 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6194 network congestion (or, less commonly, insufficient memory)
6195 @throws randolf::rex::xENOMEM Insufficient memory
6196 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6197 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6198 doesn't refer to a socket
6199 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6200 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6201 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6203 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6205 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6206 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6207 but it really isn't)
6209 @returns The same rsocket object so as to facilitate stacking
6211 *///=========================================================================
6212 rsocket* send_uint16_msb(
6214 const uint16_t value,
6215 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6216 const int flags = 0) {
6217 int16_t buf = __endian_is_msb ? value : htons(value);
6218 if (__debug) debug("send_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6219 + ", " + std::to_string(value)
6220 + ", " + std::to_string(sizeof(buf))
6221 + ", " + std::to_string(flags)
6223 __send(&buf, sizeof(buf), flags);
6225 }; // -x- rsocket* send_int16_msb -x-
6227 /*======================================================================*//**
6229 Send one 32-bit unsigned integer of data in LSB (little endian) order to the
6232 This method is threadsafe.
6234 @throws randolf::rex::xEBADF The underlying socket is not open
6235 @throws randolf::rex::xECONNRESET Connect reset by peer
6236 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6237 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6238 part of the user address space
6239 @throws randolf::rex::xEINTR Interrupted by a signal
6240 @throws randolf::rex::xEINVAL Invalid argument passed
6241 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6242 occur, but the POSIX sockets documentation lists it as one of the
6243 errors that can be returned, perhaps because some incorrectly
6244 implemented TCP/IP stacks return this error?)
6245 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6246 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6247 and 65,527 bytes for IPv6)
6248 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6249 network congestion (or, less commonly, insufficient memory)
6250 @throws randolf::rex::xENOMEM Insufficient memory
6251 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6252 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6253 doesn't refer to a socket
6254 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6255 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6256 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6258 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6260 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6261 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6262 but it really isn't)
6264 @returns The same rsocket object so as to facilitate stacking
6266 *///=========================================================================
6267 rsocket* send_uint32_lsb(
6269 const uint32_t value,
6270 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6271 const int flags = 0) {
6272 uint32_t buf = !__endian_is_msb ? value : ntohl(value);
6273 if (__debug) debug("send_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6274 + ", " + std::to_string(value)
6275 + ", " + std::to_string(sizeof(buf))
6276 + ", " + std::to_string(flags)
6278 __send(&buf, sizeof(buf), flags);
6280 }; // -x- rsocket* send_uint32_lsb -x-
6282 /*======================================================================*//**
6284 Send one 32-bit unsigned integer of data in MSB (big endian) order to the
6287 This method is threadsafe.
6289 @throws randolf::rex::xEBADF The underlying socket is not open
6290 @throws randolf::rex::xECONNRESET Connect reset by peer
6291 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6292 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6293 part of the user address space
6294 @throws randolf::rex::xEINTR Interrupted by a signal
6295 @throws randolf::rex::xEINVAL Invalid argument passed
6296 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6297 occur, but the POSIX sockets documentation lists it as one of the
6298 errors that can be returned, perhaps because some incorrectly
6299 implemented TCP/IP stacks return this error?)
6300 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6301 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6302 and 65,527 bytes for IPv6)
6303 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6304 network congestion (or, less commonly, insufficient memory)
6305 @throws randolf::rex::xENOMEM Insufficient memory
6306 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6307 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6308 doesn't refer to a socket
6309 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6310 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6311 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6313 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6315 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6316 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6317 but it really isn't)
6319 @returns The same rsocket object so as to facilitate stacking
6321 *///=========================================================================
6322 rsocket* send_uint32_msb(
6324 const uint32_t value,
6325 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6326 const int flags = 0) {
6327 uint32_t buf = __endian_is_msb ? value : htonl(value);
6328 if (__debug) debug("send_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6329 + ", " + std::to_string(value)
6330 + ", " + std::to_string(sizeof(buf))
6331 + ", " + std::to_string(flags)
6333 __send(&buf, sizeof(buf), flags);
6335 }; // -x- rsocket* send_uint32_msb -x-
6337 /*======================================================================*//**
6339 Send one 64-bit unsigned integer of data in LSB (little endian) order to the
6342 This method is threadsafe.
6344 @throws randolf::rex::xEBADF The underlying socket is not open
6345 @throws randolf::rex::xECONNRESET Connect reset by peer
6346 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6347 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6348 part of the user address space
6349 @throws randolf::rex::xEINTR Interrupted by a signal
6350 @throws randolf::rex::xEINVAL Invalid argument passed
6351 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6352 occur, but the POSIX sockets documentation lists it as one of the
6353 errors that can be returned, perhaps because some incorrectly
6354 implemented TCP/IP stacks return this error?)
6355 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6356 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6357 and 65,527 bytes for IPv6)
6358 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6359 network congestion (or, less commonly, insufficient memory)
6360 @throws randolf::rex::xENOMEM Insufficient memory
6361 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6362 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6363 doesn't refer to a socket
6364 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6365 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6366 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6368 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6370 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6371 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6372 but it really isn't)
6374 @returns The same rsocket object so as to facilitate stacking
6376 *///=========================================================================
6377 rsocket* send_uint64_lsb(
6379 const uint64_t value,
6380 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6381 const int flags = 0) {
6382 uint64_t buf = !__endian_is_msb ? value : ((((uint64_t)ntohl((value) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((value) >> 32)));
6383 if (__debug) debug("send_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6384 + ", " + std::to_string(value)
6385 + ", " + std::to_string(sizeof(buf))
6386 + ", " + std::to_string(flags)
6388 __send(&buf, sizeof(buf), flags);
6390 }; // -x- rsocket* send_uint64_lsb -x-
6392 /*======================================================================*//**
6394 Send one 64-bit unsigned integer of data in MSB (big endian) order to the
6397 This method is threadsafe.
6399 @throws randolf::rex::xEBADF The underlying socket is not open
6400 @throws randolf::rex::xECONNRESET Connect reset by peer
6401 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6402 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6403 part of the user address space
6404 @throws randolf::rex::xEINTR Interrupted by a signal
6405 @throws randolf::rex::xEINVAL Invalid argument passed
6406 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6407 occur, but the POSIX sockets documentation lists it as one of the
6408 errors that can be returned, perhaps because some incorrectly
6409 implemented TCP/IP stacks return this error?)
6410 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6411 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6412 and 65,527 bytes for IPv6)
6413 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6414 network congestion (or, less commonly, insufficient memory)
6415 @throws randolf::rex::xENOMEM Insufficient memory
6416 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6417 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6418 doesn't refer to a socket
6419 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6420 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6421 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6423 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6425 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6426 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6427 but it really isn't)
6429 @returns The same rsocket object so as to facilitate stacking
6431 *///=========================================================================
6432 rsocket* send_uint64_msb(
6434 const uint64_t value,
6435 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6436 const int flags = 0) {
6437 uint64_t buf = __endian_is_msb ? value : ((((uint64_t)htonl((value) & 0xffffffffUL)) << 32) | htonl((uint32_t)((value) >> 32)));
6438 if (__debug) debug("send_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6439 + ", " + std::to_string(value)
6440 + ", " + std::to_string(sizeof(buf))
6441 + ", " + std::to_string(flags)
6443 __send(&buf, sizeof(buf), flags);
6445 }; // -x- rsocket* send_uint64_msb -x-
6447 /*======================================================================*//**
6449 Send data in the form of a std::string to the endpoint, with an EoL sequence
6452 This method is threadsafe.
6454 @throws randolf::rex::xEBADF The underlying socket is not open
6455 @throws randolf::rex::xECONNRESET Connect reset by peer
6456 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6457 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6458 part of the user address space
6459 @throws randolf::rex::xEINTR Interrupted by a signal
6460 @throws randolf::rex::xEINVAL Invalid argument passed
6461 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6462 occur, but the POSIX sockets documentation lists it as one of the
6463 errors that can be returned, perhaps because some incorrectly
6464 implemented TCP/IP stacks return this error?)
6465 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6466 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6467 and 65,527 bytes for IPv6)
6468 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6469 network congestion (or, less commonly, insufficient memory)
6470 @throws randolf::rex::xENOMEM Insufficient memory
6471 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6472 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6473 doesn't refer to a socket
6474 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6475 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6476 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6478 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6480 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6481 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6482 but it really isn't)
6484 @returns The same rsocket object so as to facilitate stacking
6487 @see recvline(const size_t, const int)
6490 *///=========================================================================
6493 const std::string msg = std::string(),
6494 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6495 const int flags = 0) {
6496 if (__debug) debug("sendline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6498 + ", " + std::to_string(msg.length())
6499 + "+" + std::to_string(__eol_out.length())
6500 + ", " + std::to_string(flags)
6502 __sendline(msg.c_str(), msg.length(), flags);
6504 }; // -x- rsocket* sendline -x-
6506 /*======================================================================*//**
6508 Send data in the form of a "msghdr" structure to a specific endpoint.
6510 This method is not compatible with TLS.
6512 This method is threadsafe.
6514 @throws randolf::rex::xEBADF The underlying socket is not open
6515 @throws randolf::rex::xECONNRESET Connect reset by peer
6516 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6517 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6518 part of the user address space
6519 @throws randolf::rex::xEINTR Interrupted by a signal
6520 @throws randolf::rex::xEINVAL Invalid argument passed
6521 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6522 occur, but the POSIX sockets documentation lists it as one of the
6523 errors that can be returned, perhaps because some incorrectly
6524 implemented TCP/IP stacks return this error?)
6525 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6526 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6527 and 65,527 bytes for IPv6)
6528 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6529 network congestion (or, less commonly, insufficient memory)
6530 @throws randolf::rex::xENOMEM Insufficient memory
6531 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6532 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6533 doesn't refer to a socket
6534 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6535 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6536 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6538 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6540 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6541 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6542 but it really isn't)
6544 @returns The same rsocket object so as to facilitate stacking
6546 *///=========================================================================
6548 /// Pointer to data to send
6549 const struct msghdr* msg,
6550 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6551 const int flags = 0) {
6552 if (__debug) debug("sendmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6554 + ", " + std::to_string(flags)
6556 __track_bytes_tx(__rc_check(::sendmsg(__socket_fd, msg, flags)));
6558 }; // -x- rsocket* sendmsg -x-
6560 /*======================================================================*//**
6562 Send data in the form of a "mmsghdr" structure to a specific endpoint.
6564 This method is not compatible with TLS.
6566 This method is threadsafe.
6568 @throws randolf::rex::xEBADF The underlying socket is not open
6569 @throws randolf::rex::xECONNRESET Connect reset by peer
6570 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6571 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6572 part of the user address space
6573 @throws randolf::rex::xEINTR Interrupted by a signal
6574 @throws randolf::rex::xEINVAL Invalid argument passed
6575 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6576 occur, but the POSIX sockets documentation lists it as one of the
6577 errors that can be returned, perhaps because some incorrectly
6578 implemented TCP/IP stacks return this error?)
6579 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6580 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6581 and 65,527 bytes for IPv6)
6582 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6583 network congestion (or, less commonly, insufficient memory)
6584 @throws randolf::rex::xENOMEM Insufficient memory
6585 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6586 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6587 doesn't refer to a socket
6588 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6589 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6590 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6592 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6594 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6595 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6596 but it really isn't)
6598 @returns The same rsocket object so as to facilitate stacking
6600 *///=========================================================================
6602 /// Pointer to data to send
6603 struct mmsghdr* mmsg,
6604 /// Size of target endpoint structure
6605 const unsigned int vlen = sizeof(mmsghdr),
6606 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6607 const int flags = 0) {
6608 if (__debug) debug("sendmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6610 + ", " + std::to_string(vlen)
6611 + ", " + std::to_string(flags)
6613 __track_bytes_tx(__rc_check(::sendmmsg(__socket_fd, mmsg, vlen, flags)));
6615 }; // -x- rsocket* sendmsg -x-
6617 /*======================================================================*//**
6619 Send data in the form of a std::string to a specific endpoint.
6621 This method is not compatible with TLS.
6623 This method is threadsafe.
6625 @throws randolf::rex::xEBADF The underlying socket is not open
6626 @throws randolf::rex::xECONNRESET Connect reset by peer
6627 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6628 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6629 part of the user address space
6630 @throws randolf::rex::xEINTR Interrupted by a signal
6631 @throws randolf::rex::xEINVAL Invalid argument passed
6632 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6633 occur, but the POSIX sockets documentation lists it as one of the
6634 errors that can be returned, perhaps because some incorrectly
6635 implemented TCP/IP stacks return this error?)
6636 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6637 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6638 and 65,527 bytes for IPv6)
6639 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6640 network congestion (or, less commonly, insufficient memory)
6641 @throws randolf::rex::xENOMEM Insufficient memory
6642 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6643 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6644 doesn't refer to a socket
6645 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6646 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6647 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6649 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6651 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6652 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6653 but it really isn't)
6655 @returns The same rsocket object so as to facilitate stacking
6657 *///=========================================================================
6660 const std::string msg,
6661 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6663 /// Target endpoint address structure
6664 const struct sockaddr *to,
6665 /// Size of target endpoint structure
6666 socklen_t tolen = sizeof(sockaddr)) {
6667 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6669 + ", " + std::to_string(flags)
6671 + ", " + std::to_string(tolen)
6673 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg.c_str(), msg.length(), flags, to, tolen)));
6675 }; // -x- rsocket* sendto -x- // TODO: Create easier-to-use variants
6677 /*======================================================================*//**
6679 Send data in the form of a C-string to a specific endpoint.
6681 This method is not compatible with TLS.
6683 This method is threadsafe.
6685 @throws randolf::rex::xEBADF The underlying socket is not open
6686 @throws randolf::rex::xECONNRESET Connect reset by peer
6687 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6688 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6689 part of the user address space
6690 @throws randolf::rex::xEINTR Interrupted by a signal
6691 @throws randolf::rex::xEINVAL Invalid argument passed
6692 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6693 occur, but the POSIX sockets documentation lists it as one of the
6694 errors that can be returned, perhaps because some incorrectly
6695 implemented TCP/IP stacks return this error?)
6696 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6697 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6698 and 65,527 bytes for IPv6)
6699 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6700 network congestion (or, less commonly, insufficient memory)
6701 @throws randolf::rex::xENOMEM Insufficient memory
6702 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6703 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6704 doesn't refer to a socket
6705 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6706 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6707 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6709 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6711 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6712 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6713 but it really isn't)
6715 @returns The same rsocket object so as to facilitate stacking
6717 *///=========================================================================
6719 /// Pointer to data to send
6721 /// Number of bytes to send
6723 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6725 /// Target endpoint address structure
6726 const struct sockaddr *to,
6727 /// Size of target endpoint structure
6728 socklen_t tolen = sizeof(sockaddr)) {
6729 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6731 + ", " + std::to_string(std::strlen(msg))
6732 + ", " + std::to_string(flags)
6734 + ", " + std::to_string(tolen)
6736 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, len, flags, to, tolen)));
6738 }; // -x- rsocket* sendto -x-
6740 /*======================================================================*//**
6742 Send data in the form of an ASCIIZ string to the endpoint. The terminating
6743 NULL character won't be transmitted.
6745 This method is threadsafe.
6747 @throws randolf::rex::xEBADF The underlying socket is not open
6748 @throws randolf::rex::xECONNRESET Connect reset by peer
6749 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6750 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6751 part of the user address space
6752 @throws randolf::rex::xEINTR Interrupted by a signal
6753 @throws randolf::rex::xEINVAL Invalid argument passed
6754 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6755 occur, but the POSIX sockets documentation lists it as one of the
6756 errors that can be returned, perhaps because some incorrectly
6757 implemented TCP/IP stacks return this error?)
6758 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6759 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6760 and 65,527 bytes for IPv6)
6761 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6762 network congestion (or, less commonly, insufficient memory)
6763 @throws randolf::rex::xENOMEM Insufficient memory
6764 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6765 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6766 doesn't refer to a socket
6767 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6768 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6769 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6771 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6773 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6774 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6775 but it really isn't)
6777 @returns The same rsocket object so as to facilitate stacking
6778 @see recvz(const size_t, const int)
6779 @see send_asciiz(const char*, const int) which also transmits the terminating
6782 *///=========================================================================
6784 /// Pointer to data to send
6786 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6787 const int flags = 0) {
6788 if (__debug) debug("sendz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6790 + ", " + std::to_string(std::strlen(msg))
6791 + ", " + std::to_string(flags)
6793 __send(msg, std::strlen(msg), flags);
6795 }; // -x- rsocket* sendz -x-
6797 /*======================================================================*//**
6799 Send data in the form of an ASCIIZ string to a specific endpoint. The
6800 terminating NULL character won't be transmitted.
6802 This method is not compatible with TLS.
6804 This method is threadsafe.
6806 @throws randolf::rex::xEBADF The underlying socket is not open
6807 @throws randolf::rex::xECONNRESET Connect reset by peer
6808 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6809 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6810 part of the user address space
6811 @throws randolf::rex::xEINTR Interrupted by a signal
6812 @throws randolf::rex::xEINVAL Invalid argument passed
6813 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6814 occur, but the POSIX sockets documentation lists it as one of the
6815 errors that can be returned, perhaps because some incorrectly
6816 implemented TCP/IP stacks return this error?)
6817 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6818 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6819 and 65,527 bytes for IPv6)
6820 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6821 network congestion (or, less commonly, insufficient memory)
6822 @throws randolf::rex::xENOMEM Insufficient memory
6823 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6824 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6825 doesn't refer to a socket
6826 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6827 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6828 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6830 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6832 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6833 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6834 but it really isn't)
6836 @returns The same rsocket object so as to facilitate stacking
6837 *///=========================================================================
6839 /// Pointer to data to send
6841 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6843 /// Target endpoint address structure
6844 const struct sockaddr *to,
6845 /// Size of target endpoint structure
6846 socklen_t tolen = sizeof(sockaddr)) {
6847 if (__debug) debug("sendzto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6849 + ", " + std::to_string(std::strlen(msg))
6850 + ", " + std::to_string(flags)
6852 + ", " + std::to_string(tolen)
6854 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, std::strlen(msg), flags, to, tolen)));
6856 }; // -x- rsocket* sendzto -x-
6858 /*======================================================================*//**
6860 Set socket option to the specific integer.
6863 These setsockopt() methods take an integer or character value directly, or a
6864 pointer to a structure, and then rsocket handles the remaining tedious
6865 technical details behind-the-scenes for you when calling the underlying
6866 socket's setsockopt() function.
6868 @throws randolf::rex::xEBADF The underlying socket is not open
6869 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6870 part of the user address space
6871 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
6872 valid for this socket's family (a.k.a., communication domain)
6873 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
6875 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6876 doesn't refer to a socket
6878 @returns The same rsocket object so as to facilitate stacking
6881 *///=========================================================================
6882 rsocket* setsockopt(
6883 /// The level at which the option resides; typically @c SOL_SOCKET
6885 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
6887 /// The value that this socket option will be set to
6889 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
6890 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
6892 }; // -x- rsocket* setsockopt -x-
6894 /*======================================================================*//**
6896 Set socket option to the specific unsigned integer.
6897 @copydetails setsockopt(const int, const int, const int)
6900 For any values that require a u_int, you'll need to explicitly cast this type
6901 when specifying the value directly; for example: (u_int)32768
6903 @throws randolf::rex::xEBADF The underlying socket is not open
6904 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6905 part of the user address space
6906 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
6907 valid for this socket's family (a.k.a., communication domain)
6908 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
6910 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6911 doesn't refer to a socket
6913 @returns The same rsocket object so as to facilitate stacking
6915 *///=========================================================================
6916 rsocket* setsockopt(
6917 /// The level at which the option resides; typically @c SOL_SOCKET
6919 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
6921 /// The value that this socket option will be set to
6922 const u_int value) {
6923 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
6924 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
6926 }; // -x- rsocket* setsockopt -x-
6928 /*======================================================================*//**
6930 Set socket option to the specific unsigned character.
6931 @copydetails setsockopt(const int, const int, const int)
6933 *///=========================================================================
6934 rsocket* setsockopt(
6935 /// The level at which the option resides; typically @c SOL_SOCKET
6937 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
6939 /// The value that this socket option will be set to
6940 const u_char value) {
6941 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
6942 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
6944 }; // -x- rsocket* setsockopt -x-
6946 /*======================================================================*//**
6948 Set socket option to the specific structure.
6949 @copydetails setsockopt(const int, const int, const int)
6951 *///=========================================================================
6952 rsocket* setsockopt(
6953 /// The level at which the option resides; typically @c SOL_SOCKET
6955 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
6957 /// The structure that this socket option will be set to
6958 const linger& value) {
6959 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
6960 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
6962 }; // -x- rsocket* setsockopt -x-
6964 /*======================================================================*//**
6965 @copydoc setsockopt(const int, const int, const linger&)
6967 *///=========================================================================
6968 rsocket* setsockopt(
6969 /// The level at which the option resides; typically @c SOL_SOCKET
6971 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
6973 /// The structure that this socket option will be set to
6974 const timeval& value) {
6975 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
6976 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
6978 }; // -x- rsocket* setsockopt -x-
6980 /*======================================================================*//**
6981 @copydoc setsockopt(const int, const int, const linger&)
6983 *///=========================================================================
6984 rsocket* setsockopt(
6985 /// The level at which the option resides; typically @c SOL_SOCKET
6987 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
6989 /// The structure that this socket option will be set to
6990 const in_addr& value) {
6991 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
6992 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
6994 }; // -x- rsocket* setsockopt -x-
6996 /*======================================================================*//**
6997 @copydoc setsockopt(const int, const int, const linger&)
6999 *///=========================================================================
7000 rsocket* setsockopt(
7001 /// The level at which the option resides; typically @c SOL_SOCKET
7003 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7005 /// The structure that this socket option will be set to
7006 const ip_mreq& value) {
7007 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7008 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7010 }; // -x- rsocket* setsockopt -x-
7012 /*======================================================================*//**
7013 @copydoc setsockopt(const int, const int, const linger&)
7015 *///=========================================================================
7016 rsocket* setsockopt(
7017 /// The level at which the option resides; typically @c SOL_SOCKET
7019 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7021 /// The structure that this socket option will be set to
7022 const ip_mreq_source& value) {
7023 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7024 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7026 }; // -x- rsocket* setsockopt -x-
7028 /*======================================================================*//**
7029 @copydoc setsockopt(const int, const int, const linger&)
7031 *///=========================================================================
7032 rsocket* setsockopt(
7033 /// The level at which the option resides; typically @c SOL_SOCKET
7035 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7037 /// The structure that this socket option will be set to
7038 const icmp6_filter& value) {
7039 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7040 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7042 }; // -x- rsocket* setsockopt -x-
7044 /*======================================================================*//**
7045 @copydoc setsockopt(const int, const int, const linger&)
7047 *///=========================================================================
7048 rsocket* setsockopt(
7049 /// The level at which the option resides; typically @c SOL_SOCKET
7051 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7053 /// The structure that this socket option will be set to
7054 const sockaddr_in6& value) {
7055 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7056 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7058 }; // -x- rsocket* setsockopt -x-
7060 /*======================================================================*//**
7061 @copydoc setsockopt(const int, const int, const linger&)
7063 *///=========================================================================
7064 rsocket* setsockopt(
7065 /// The level at which the option resides; typically @c SOL_SOCKET
7067 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7069 /// The structure that this socket option will be set to
7070 const ip6_mtuinfo& value) {
7071 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7072 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7074 }; // -x- rsocket* setsockopt -x-
7076 /*======================================================================*//**
7077 @copydoc setsockopt(const int, const int, const linger&)
7079 *///=========================================================================
7080 rsocket* setsockopt(
7081 /// The level at which the option resides; typically @c SOL_SOCKET
7083 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7085 /// The structure that this socket option will be set to
7086 const ipv6_mreq& value) {
7087 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7088 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7090 }; // -x- rsocket* setsockopt -x-
7092 /*======================================================================*//**
7093 @copydoc setsockopt(const int, const int, const linger&)
7095 *///=========================================================================
7096 rsocket* setsockopt(
7097 /// The level at which the option resides; typically @c SOL_SOCKET
7099 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7101 /// The structure that this socket option will be set to
7102 const group_req& value) {
7103 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7104 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7106 }; // -x- rsocket* setsockopt -x-
7108 /*======================================================================*//**
7109 @copydoc setsockopt(const int, const int, const linger&)
7111 *///=========================================================================
7112 rsocket* setsockopt(
7113 /// The level at which the option resides; typically @c SOL_SOCKET
7115 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7117 /// The structure that this socket option will be set to
7118 const group_source_req& value) {
7119 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7120 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7122 }; // -x- rsocket* setsockopt -x-
7124 /*======================================================================*//**
7126 Shut down the underlying socket, partially or fully.
7128 <div style=padding-left:32px;>
7131 <td valign=top>SHUT_RD:</td>
7132 <td>Further receives will be disallowed.</td>
7135 <td valign=top>SHUT_WR:</td>
7136 <td>Further sends will be disallowed (this may cause actions specific
7137 to the protocol family of the socket to occur).</td>
7140 <td valign=top>SHUT_RDWR:</td>
7141 <td>Further sends and receives will be disallowed (default).</td>
7146 @throws randolf::rex::xEBADF The underlying socket is not open
7147 @throws randolf::rex::xEINVAL Invalid argument passed
7148 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7149 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7150 doesn't refer to a socket
7152 @returns The same rsocket object so as to facilitate stacking
7155 *///=========================================================================
7159 /// SHUT_RDWR (default)
7160 const int how = SHUT_RDWR) {
7161 if (__debug) debug("shutdown(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7162 + ", " + std::to_string(how)
7164 if (__tls) SSL_shutdown(__tls_fd); // We have to shut down TLS connections first, if they're active
7165 __rc_check(::shutdown(__socket_fd, how));
7166 //__socket_connected = false; // TODO: Figure out when to change this to false
7168 }; // -x- rsocket* shutdown -x-
7170 /*======================================================================*//**
7172 Complete the configuration of an rsocket that was previously initialized
7173 without any parameters (a.k.a., an "empty rsocket").
7174 @copydetails rsocket()
7175 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
7176 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
7177 @throws randolf::rex::xEALREADY If this socket() method was already used, or
7178 it was used after rsocket() initialized with at least one parameter
7179 @throws randolf::rex::xEINVAL Protocal family invalid or not available
7180 @throws randolf::rex::xEINVAL Invalid flags in type
7181 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
7182 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
7183 @throws randolf::rex::xENOBUFS Insufficient memory
7184 @throws randolf::rex::xENOMEM Insufficient memory
7185 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
7186 supported within the specified family (a.k.a., communication domain)
7187 @returns The same rsocket object so as to facilitate stacking
7189 @see socket_family()
7191 @see socket_protocol()
7195 *///=========================================================================
7197 /// Communication domain; usually one of:@n
7198 /// AF_INET (IPv4)@n
7199 /// AF_INET6 (IPv6)@n
7200 /// AF_UNIX (UNIX domain sockets)
7202 /// Communication semantics; usually one of:@n
7203 /// SOCK_STREAM (common for TCP)@n
7204 /// SOCK_DGRAM (common for UDP)
7205 const int type = SOCK_STREAM,
7206 /// Network protocol; usually one of:@n
7210 /// PF_UNSPEC (auto-detect)
7211 const int protocol = PF_UNSPEC) {
7212 __socket(family, type, protocol);
7214 }; // -x- rsocket* socket -x-
7216 /*======================================================================*//**
7218 Get underlying socket family/domain constant (SO_DOMAIN).
7219 @returns socket family/domain constant
7223 @see socket_protocol()
7226 *///=========================================================================
7227 const int socket_family() noexcept { return __socket_addr.ss_family; }; // -x- int socket_family -x-
7229 /*======================================================================*//**
7231 Get underlying socket descriptor/handle.
7232 @returns socket descriptor/handle
7233 @returns 0 = socket not yet allocated
7236 @see socket_family()
7237 @see socket_protocol()
7240 *///=========================================================================
7241 const int socket_fd() noexcept { return __socket_fd; }; // -x- int socket_fd -x-
7243 /*======================================================================*//**
7245 Set underlying socket descriptor/handle (to one that is presumed to be open).
7247 This method is only available while an underlying socket has not been created
7248 or previously assigned, such as after an empty @ref rsocket instantiation.
7249 @throws randolf::rex::xEALREADY If this socket_fd() method was already used,
7250 or it was used after socket() initialized it, or if rsocket() had
7251 initialized with at least one parameter that resulted in the creation
7252 of an underlying socket
7253 @returns The same rsocket object so as to facilitate stacking
7255 @see socket_family()
7256 @see socket_protocol()
7259 *///=========================================================================
7261 /// New socket descriptor/handle
7262 const int new_socket_fd) {
7263 if (__debug) debug("socket_fd(socket{0x" + randolf::rtools::to_hex(new_socket_fd) + "}"
7265 if (__socket_fd != 0) throw randolf::rex::xEALREADY("EALREADY");
7266 __socket_fd = new_socket_fd;
7267 __socket_addr.ss_family = getsockopt_int(SOL_SOCKET, SO_DOMAIN);
7268 __socket_type = getsockopt_int(SOL_SOCKET, SO_TYPE);
7269 __socket_protocol = getsockopt_int(SOL_SOCKET, SO_PROTOCOL);
7270 __socket_open = true;
7272 }; // -x- rsocket* socket_fd -x-
7274 /*======================================================================*//**
7276 Get underlying socket protocol constant (SO_PROTOCOL).
7277 @returns socket protocol constant
7280 @see socket_family()
7284 *///=========================================================================
7285 const int socket_protocol() noexcept { return __socket_protocol; }; // -x- int socket_protocol -x-
7287 /*======================================================================*//**
7289 Get underlying socket type constant (SO_TYPE).
7290 @returns socket type constant
7293 @see socket_family()
7295 @see socket_protocol()
7297 *///=========================================================================
7298 const int socket_type() noexcept { return __socket_type; }; // -x- int socket_type -x-
7300 /*======================================================================*//**
7302 Find out whether the underlying socket is at the out-of-band (OOB) mark.
7304 @throws randolf::rex::xEBADF The underlying socket is not open
7305 @throws randolf::rex::xEINVAL The underlying socket file descriptor is not a
7306 type to which @ref sockatmark() can be applied
7308 @returns TRUE = at OOB mark
7309 @returns FALSE = not at OOB mark
7312 *///=========================================================================
7313 const bool sockatmark() {
7314 return __rc_check(::sockatmark(__socket_fd)) ? true : false;
7315 }; // -x- bool sockatmark -x-
7317 /*======================================================================*//**
7319 Find out what the read timeout is set to on the current socket.
7321 Since getting the read timeout is such a common operation, this specialized
7322 method was created to ease software development efforts; internally we're
7323 just calling getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO).
7325 @throws randolf::rex::xEBADF The underlying socket is not open
7326 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7327 part of the user address space
7328 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7329 valid for this socket's family (a.k.a., communication domain)
7330 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7332 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7333 doesn't refer to a socket
7335 @returns @c timeval socket option structure wrapped in std::shared_ptr
7337 *///=========================================================================
7338 std::shared_ptr<timeval> timeout() {
7339 return getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO);
7340 }; // -x- std::shared_ptr<timeval> timeout -x-
7342 /*======================================================================*//**
7344 Set the recv timeout on the current socket.
7346 Since setting the read timeout is such a common operation, this specialized
7347 method was created to ease software development efforts; internally we're
7348 just calling setsockopt(SOL_SOCKET, SO_RCVTIMEO, timeval).
7350 Although a timeout of 100,000 microseconds (1/10 of a one second) may suffice
7351 in healthy and efficient networks, a more conservative setting of 1 second
7352 tends to minimally yield more reliable results. Many end-user applications
7353 use defaults of 3 to 5 seconds to cover a wider variety of unpredictable
7354 network connections (such as over shared wireless connections that are slow),
7355 and this setting should ultimately be configurable by users/administrators.
7358 The default timeout for new sockets is normally 0 (no timeout).
7360 @throws randolf::rex::xEBADF The underlying socket is not open
7361 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7362 part of the user address space
7363 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7364 valid for this socket's family (a.k.a., communication domain)
7365 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7367 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7368 doesn't refer to a socket
7370 @returns The same rsocket object so as to facilitate stacking
7372 *///=========================================================================
7374 /// timeval structure
7375 const struct timeval tv) {
7376 return setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
7377 }; // -x- rsocket* timeout -x-
7379 /*======================================================================*//**
7380 @copydoc timeout(struct timeval)
7382 *///=========================================================================
7384 /// Timeout in seconds
7385 const int tv_sec = 0,
7386 /// Timeout in microseconds
7387 const long tv_usec = 0) {
7388 struct timeval tv{tv_sec, tv_usec};
7389 return setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
7390 }; // -x- rsocket* timeout -x-
7392 /*======================================================================*//**
7394 Find out what the read timeout is set to when using the @ref recvline()
7397 @returns @c long value (0 = no timeout)
7400 @see timeout_recvline(long)
7402 *///=========================================================================
7403 long timeout_recvline() {
7404 return __recvline_timeout;
7405 }; // -x- long timeout_recvline -x-
7407 /*======================================================================*//**
7409 Set the read timeout for the @ref recvline() method (the @ref recvline()
7410 method's @c timeout parameter can override this setting).
7413 The default timeout for this recvline_timeout setting is 0 (no timeout).
7415 @throws randolf::rex::xERANGE if the timeout parameter is below 0
7417 @returns The same rsocket object so as to facilitate stacking
7420 @see timeout_recvline
7422 *///=========================================================================
7423 rsocket* timeout_recvline(
7424 /// timeval structure
7425 const long timeout) {
7426 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
7427 __recvline_timeout = timeout;
7429 }; // -x- rsocket* timeout_recvline -x-
7431 /*======================================================================*//**
7433 Enable or disable encrypted communications (from the OpenSSL library).
7436 TLS cannot be enabled before a non-encrypted rsocket is open (an rsocket is
7437 typically opened with the @ref socket() method, the @ref connect() method, or
7438 one of the @ref accept() methods). If calling @c tls(true) on an rsocket
7439 that isn't open, an exception will be thrown.
7441 If needed, a new TLS context will be instantiated and TLS will be initialized
7442 (if this hasn't already been done). TLS instantiation can be done first by
7443 calling the @ref tls_ctx() method (regardless of whether encryption is being
7444 enabled or disabled). If the default @ref TLS_FLAGS aren't sufficient for
7445 the needs of your application, then the @ref tls_ctx() method facilitates
7446 this regardless of wehther rsocket is open.
7449 The reason a TLS context is instantiated and TLS is initialized even when the
7450 status is being set to FALSE is to facilitate TLS ingress from an unencrypted
7451 connection later in the session (see the @ref TLS_NO_INGRESS flag for more
7455 If a client attempts to upgrade a TLS connection to TLS (e.g., by using a
7456 command such as @c STARTTLS, which is commonly transmitted in unencrypted
7457 form, like @c telnet-ssl does -- using `telnet-ssl -z ssl` prevents this
7458 condition), the following error that's difficult to track down may be
7459 triggered when calling any of the @c recv methods (I hope that including this
7460 information here in this documentation will be helpful):
7462 SSL_ERROR_UNKNOWN: error:0A00010B:SSL routines::wrong version number
7464 This is most likely not a programming error, but rather a problem with how
7465 users may attempt to mis-use a connection based on a misunderstanding of the
7466 communications requirements (e.g., connecting unencrypted and attempting to
7467 upgrade to TLS over a connection that's expecting TLS encrypted data from the
7468 very beginning, without involving any ingress).
7470 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7471 OpenSSL library doesn't document which errors may be returned)
7473 @returns The same rsocket object so as to facilitate stacking
7477 *///=========================================================================
7479 /// TRUE = Enable encrypted communications@n FALSE = Disable encrypted communications
7480 const bool status = true,
7481 /// Configuration parameters
7482 const int flags = TLS_FLAGS::TLS_DEFAULT) { // | TLS_FLAGS::TLS_CLIENT
7483 if (__debug) debug("tls(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7484 + ", " + (status ? "true" : "false")
7487 // --------------------------------------------------------------------------
7488 // Create default context (with "flags" passthrough), unless it was already
7489 // created (usually by one of the tls_ctx() methods).
7490 // --------------------------------------------------------------------------
7491 if (__tls_ctx == nullptr) tls_ctx((SSL_CTX*)nullptr, flags);
7493 // --------------------------------------------------------------------------
7494 // Make sure tls_fd (ssl_fd) is allocated. If not, then it needs to be
7495 // allocated and configured.
7496 // --------------------------------------------------------------------------
7497 if (status == true && __tls_fd == nullptr) {
7498 __tls_fd = SSL_new(__tls_ctx);
7499 if (__debug) debug("SSL_set_fd(<tls_fd>, socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7502 // --------------------------------------------------------------------------
7503 // Associate OpenSSL file descriptor with underlying socket file descriptor.
7504 // --------------------------------------------------------------------------
7505 __rc_check_tls(SSL_set_fd(__tls_fd, __socket_fd));
7507 // --------------------------------------------------------------------------
7508 // Enable read-ahead so that SSL_peek will work properly.
7509 // --------------------------------------------------------------------------
7510 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
7511 SSL_set_read_ahead(__tls_fd, 1); // 0=disable / 1=enable
7513// SSL_CTX_set_max_pipelines(__tls_ctx, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
7514// SSL_set_max_pipelines(__tls_fd, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
7516// SSL_CTX_set_mode(__tls_ctx, SSL_MODE_AUTO_RETRY);
7517// SSL_set_mode(__tls_fd, SSL_MODE_AUTO_RETRY);
7519 // --------------------------------------------------------------------------
7520 // We're probably not going to use BIO because we don't need it, and it adds
7521 // unnecessary overhead for what we're doing. Also, BIO doesn't provide an
7522 // alternative to the SSL_peek function, and also doesn't resolve the need
7523 // for the MSG_WAITALL flag that the ::recv() function supports.
7525 // TODO: Remove this completely, unless is can solve the problem of reading
7526 // all incoming data (needed for readline, primarily)
7527 // --------------------------------------------------------------------------
7528 __tls_rbio = SSL_get_rbio(__tls_fd);
7529 __tls_wbio = SSL_get_wbio(__tls_fd);
7531 } // -x- if !__tls_fd -x-
7534 }; // -x- rsocket* tls -x-
7536 /*======================================================================*//**
7538 Return the current TLS context (multiple TLS contexts are supported, although
7539 typically needed to support SNI with inbound connections).
7540 @returns Pointer to OpenSSL's context (normally labelled @c SSL_CTX* in the
7541 documentation for OpenSSL), or nullptr if this context was never
7542 assigned to (or created by) this rsocket
7545 *///=========================================================================
7546 SSL_CTX* tls_ctx() noexcept {
7548 }; // -x- SSL_CTX* tls_ctx -x-
7550 /*======================================================================*//**
7552 Copy the source rsocket's TLS context map and add it to this rsocket's
7553 collection; or, if the source doesn't have any TLS contexts and this rsocket
7554 doesn't have any TLS contexts in its collection, then initialize TLS and
7555 instantiate a new TLS context. In either scenario, the source rsocket will
7556 be treated as a template as all TLS flags duplicated to enable encrypted
7557 socket I/O for use in this rsocket().
7560 At least one TLS context is needed to enable encrypted socket I/O for use in
7564 Encrypted socket I/O is only possible after a TLS context has been
7565 initialized (this is not a global setting as it has per-rsocket specificity).
7568 The only @ref TLS_FLAGS flag that doesn't get transferred is @ref TLS_SERVER
7569 when no flags are specified. Specifying any flag(s) will cause this method
7570 to ignore the source rsocket's TLS flags so as to defer to this override.
7572 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7573 OpenSSL library doesn't document which errors may be returned)
7575 @returns The same rsocket object so as to facilitate stacking
7578 *///=========================================================================
7580 /// OpenSSL's TLS context to use (if not provided, a new context will be
7581 /// created automatically using OpenSSL's defaults)
7583 /// Configuration parameters
7584 const int flags = TLS_FLAGS::TLS_DEFAULT) {
7585 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7587 + ", " + std::to_string(flags)
7590 // --------------------------------------------------------------------------
7591 // TLS-related variables (OpenSSL).
7593 // Note: The __tls_fd variable cannot be allocated until after __socket_fd
7594 // has been, hence the "post" note in the documentation.
7595 // --------------------------------------------------------------------------
7596 __tls = (bool)rtemplate->__tls; // Preserve TLS mode setting
7597 if (rtemplate->__tls_ctx != nullptr) { // If TLS context is defined, then do these important things:
7598 __tls_ctx = rtemplate->__tls_ctx; // 1. copy the pointer to SSL_CTX
7599 SSL_CTX_up_ref(__tls_ctx); // 2. increment SSL_CTX's internal reference count
7600 } // -x- if __tlx_ctx -x-
7602 // --------------------------------------------------------------------------
7603 // Copy or override TLS flags.
7604 // --------------------------------------------------------------------------
7605 if (flags == TLS_FLAGS::TLS_DEFAULT) {
7606 __tls_exclusive = rtemplate->__tls_exclusive; // TLS policy
7607 __tls_egress = rtemplate->__tls_egress; // TLS policy
7608 __tls_ingress = rtemplate->__tls_ingress; // TLS policy
7609 } else { // Save flags
7610 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
7611 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
7612 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
7613 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
7614 } // -x- if flags -x-
7617 }; // -x- rsocket* tls_ctx -x-
7619 /*======================================================================*//**
7621 Initialize TLS and instantiate a TLS context, and add it to this rsocket's
7622 current collection of TLS contexts, and set it as the currently active TLS
7623 context (so that a certificate chain and private key may be added to it).
7625 At least one TLS context is needed to enable encrypted socket I/O for use in
7628 Encrypted socket I/O is only possible after a TLS context has been
7629 initialized (this is not a global setting as it has per-rsocket specificity).
7631 This is the default TLS context for this @c rsocket, which will also be used
7632 for non-SNI handshakes.
7634 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7635 OpenSSL library doesn't document which errors may be returned)
7637 @returns The same rsocket object so as to facilitate stacking
7640 *///=========================================================================
7642 /// OpenSSL's TLS context to use (if not provided, a new context will be
7643 /// created using OpenSSL's defaults)
7645 /// Configuration parameters
7646 const int flags = TLS_FLAGS::TLS_DEFAULT) {
7647 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7649 + ", " + std::to_string(flags)
7652 // --------------------------------------------------------------------------
7653 // Ignore repeated calls to this method.
7654 // --------------------------------------------------------------------------
7655 if (__tls_ctx != nullptr) return this;
7657 // --------------------------------------------------------------------------
7658 // Fire up OpenSSL's algorithms and pre-load its error strings.
7660 // These two functions have been deprecated since OpenSSL v1.1.0, so we don't
7661 // need to call them. If someone needs them, then they can always call them
7662 // in their own code anyway.
7663 // --------------------------------------------------------------------------
7664 //OpenSSL_add_all_algorithms(); // Load and register cryptography algorithms
7665 //SSL_load_error_strings(); // Load all error messages into memory
7667 // --------------------------------------------------------------------------
7668 // Save (ctx != nullptr) or create (ctx == nullptr) OpenSSL context.
7669 // --------------------------------------------------------------------------
7670 __tls_ctx = ctx == nullptr ? SSL_CTX_new(flags & TLS_FLAGS::TLS_SERVER ? TLS_server_method() : TLS_client_method()) // Create anew (default)
7671 : ctx; // Use OpenSSL context that was provided
7672 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);
7674 // --------------------------------------------------------------------------
7675 // Enable read-ahead so that SSL_peek will work properly.
7676 // --------------------------------------------------------------------------
7677 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
7679 // --------------------------------------------------------------------------
7681 // --------------------------------------------------------------------------
7682 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
7683 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
7684 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
7685 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
7688 }; // -x- rsocket* tls_ctx -x-
7690 /*======================================================================*//**
7692 Check the private key it to ensure it's consistent with the corresponding TLS
7695 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7696 OpenSSL library doesn't document which errors may be returned)
7698 @returns The same rsocket object so as to facilitate stacking
7699 @see tls_ctx_use_privatekey_file
7700 @see tls_ctx_use_privatekey_pem
7702 *///=========================================================================
7703 rsocket* tls_ctx_check_privatekey() {
7704 if (__debug) debug("tls_ctx_check_privatekey();");
7705 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);
7707 }; // -x- rsocket* tls_ctx_check_privatekey -x-
7709 /*======================================================================*//**
7711 Load a TLS certificate chain and private key in PEM format from text files
7712 and use them in the TLS context.
7714 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7715 OpenSSL library doesn't document which errors may be returned)
7717 @returns The same rsocket object so as to facilitate stacking
7718 @see tls_ctx_use_certificate_chain_and_privatekey_pems
7719 @see tls_ctx_use_certificate_chain_file
7720 @see tls_ctx_use_certificate_chain_pem
7721 @see tls_ctx_use_privatekey_file
7722 @see tls_ctx_use_privatekey_pem
7723 @see tls_ctx_check_privatekey
7725 *///=========================================================================
7726 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
7727 /// Pointer to ASCIIZ path and filename to certificate chain file (@c nullptr
7728 /// will simply be ignored)
7729 const char* chain_file,
7730 /// Pointer to ASCIIZ path and filename to private key file (@c nullptr will
7731 /// simply be ignored)
7732 const char* key_file) {
7733 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
7734 + std::string(chain_file)
7735 + ", " + std::string( key_file)
7737 if (chain_file != nullptr) tls_ctx_use_certificate_chain_file(chain_file);
7738 if ( key_file != nullptr) tls_ctx_use_privatekey_file( key_file);
7740 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
7742 /*======================================================================*//**
7744 Load a TLS certificate chain and private key in PEM format from text files
7745 and use them in the TLS context.
7747 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7748 OpenSSL library doesn't document which errors may be returned)
7750 @returns The same rsocket object so as to facilitate stacking
7751 @see tls_ctx_use_certificate_chain_and_privatekey_pems
7752 @see tls_ctx_use_certificate_chain_file
7753 @see tls_ctx_use_certificate_chain_pem
7754 @see tls_ctx_use_privatekey_file
7755 @see tls_ctx_use_privatekey_pem
7756 @see tls_ctx_check_privatekey
7758 *///=========================================================================
7759 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
7760 /// Pointer to ASCIIZ path and filename to certificate chain file (an empty
7761 /// string will simply be ignored)
7762 const std::string chain_file,
7763 /// Pointer to ASCIIZ path and filename to private key file (an empty string
7764 /// will simply be ignored)
7765 const std::string key_file) {
7766 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
7770 if (!chain_file.empty()) tls_ctx_use_certificate_chain_file(chain_file);
7771 if ( !key_file.empty()) tls_ctx_use_privatekey_file( key_file);
7773 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
7775 /*======================================================================*//**
7777 Load a TLS certificate chain and a TLS private key in PEM format from memory
7778 and use them in the TLS context.
7780 Although this functionality doesn't exist in OpenSSL (at the time of writing
7781 this method), it's provided here in a manner that has exactly the same effect
7782 as the @ref tls_ctx_use_certificate_chain_and_privatekey_files() methods, but
7783 without needing the PEM-formatted certificate chain stored in files
7787 The @c cert_pem_data and key_pem_data parameters are pointers to the memory
7788 locations that holds the PEM formatted certificate chain data and private key
7789 data, respectively. If the corresponding lengths of each of these data aren't
7790 specified or are set to zero (default), then they will be treated as multiline
7793 Behind the scenes, we're just writing the cert_pem_data and key_pem_data
7794 memory to temporary files with severely-limited permissions (), then
7795 optionally overwriting those temporary files with random data prior to
7796 deleting them (this is the default, since better security practices should be
7797 the default, but on a secured system it may not be necessary and so this
7798 option can also be disabled to save CPU cycles and reduce overall disk-write
7801 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7802 OpenSSL library doesn't document which errors may be returned)
7804 @returns The same rsocket object so as to facilitate stacking
7805 @see tls_ctx_use_certificate_chain_and_privatekey_files
7806 @see tls_ctx_use_certificate_chain_file
7807 @see tls_ctx_use_certificate_chain_pem
7808 @see tls_ctx_use_privatekey_file
7809 @see tls_ctx_use_privatekey_pem
7810 @see tls_ctx_check_privatekey
7812 *///=========================================================================
7813 rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems(
7814 /// Pointer to certificate chain data in PEM format
7815 const char* cert_pem_data,
7816 /// Pointer to private key data in PEM format
7817 const char* key_pem_data,
7818 /// Length of cert_pem_data (in bytes), or 0 to auto-detect length if cert_pem_data is an ASCIIZ string
7819 size_t cert_len = 0,
7820 /// Length of key_pem_data (in bytes), or 0 to auto-detect length if key_pem_data is an ASCIIZ string
7822 /// Whether to overwrite the temporary files with random data before deleting them
7823 const bool random_fill = true) {
7824 tls_ctx_use_certificate_chain_pem(cert_pem_data, cert_len, random_fill);
7825 tls_ctx_use_privatekey_pem( key_pem_data, key_len, random_fill);
7827 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems -x-
7829 /*======================================================================*//**
7831 Load a TLS certificate chain in PEM format from a text file and use it in the
7834 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7835 OpenSSL library doesn't document which errors may be returned)
7837 @returns The same rsocket object so as to facilitate stacking
7838 @see tls_ctx_use_certificate_chain_file
7839 @see tls_ctx_use_certificate_chain_pem
7840 @see tls_ctx_check_privatekey
7842 *///=========================================================================
7843 rsocket* tls_ctx_use_certificate_chain_file(
7844 /// Pointer to ASCIIZ path and filename to certificate chain file
7846 if (__debug) debug("tls_ctx_use_certificate_chain_file("
7849 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);
7851 }; // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
7853 /*======================================================================*//**
7855 Load a TLS certificate chain in PEM format from a text file and use it in the
7858 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7859 OpenSSL library doesn't document which errors may be returned)
7861 @returns The same rsocket object so as to facilitate stacking
7862 @see tls_ctx_use_certificate_chain_file
7863 @see tls_ctx_use_certificate_chain_pem
7864 @see tls_ctx_check_privatekey
7866 *///=========================================================================
7867 rsocket* tls_ctx_use_certificate_chain_file(
7868 /// Path and filename to certificate chain file
7869 const std::string file) {
7870 if (__debug) debug("tls_ctx_use_certificate_chain_file("
7873 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);
7875 }; // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
7877 /*======================================================================*//**
7879 Load a TLS certificate chain in PEM format from memory and use it in the TLS
7882 Although this functionality doesn't exist in OpenSSL (at the time of writing
7883 this method), it's provided here in a manner that has exactly the same effect
7884 as the @ref tls_ctx_use_certificate_chain_file() methods, but without needing
7885 the PEM-formatted certificate chain stored in a file beforehand.
7888 The @c pem_data parameter is a pointer to the memory location that holds
7889 the PEM formatted certificate chain data. If the length of this data isn't
7890 specified or is set to zero (default), then it will be treated as a multiline
7893 Behind the scenes, we're just writing the pem_data memory to a temporary
7894 file with severely-limited permissions (), then optionally overwriting that
7895 temporary file with random data prior to deleting it (this is the default,
7896 since better security practices should be the default, but on a secured
7897 system it may not be necessary and so this option can also be disabled to
7898 save CPU cycles and reduce overall disk-write I/O operations).
7900 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7901 OpenSSL library doesn't document which errors may be returned)
7903 @returns The same rsocket object so as to facilitate stacking
7904 @see tls_ctx_use_certificate_chain_file
7905 @see tls_ctx_check_privatekey
7907 *///=========================================================================
7908 rsocket* tls_ctx_use_certificate_chain_pem(
7909 /// Pointer to certificate chain data in PEM format
7910 const char* pem_data,
7911 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
7913 /// Whether to overwrite the temporary file with random data before deleting it
7914 const bool random_fill = true) {
7915 if (__debug) debug("tls_ctx_use_certificate_chain_pem(<buf>, " + std::to_string(len)
7916 + ", " + (random_fill ? "true" : "false")
7919 // --------------------------------------------------------------------------
7920 // Measure size of certificate chain if an ASCIIZ string was indicated.
7921 // --------------------------------------------------------------------------
7922 if (len == 0) len = std::strlen(pem_data);
7924 // --------------------------------------------------------------------------
7925 // Generate filename for temporary use.
7926 // --------------------------------------------------------------------------
7927 std::string file = std::filesystem::temp_directory_path();
7928 file.append("/rsocket.")
7929 .append(std::to_string(::getpid()))
7931 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
7933 // --------------------------------------------------------------------------
7934 // Open temporary file.
7935 // --------------------------------------------------------------------------
7936 FILE* fp = fopen(file.c_str(), "w+");
7937 if (fp == nullptr) randolf::rex::mk_exception("Cannot open temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
7939 ioctl(fileno(fp), FS_IOC_GETFLAGS, &attr);
7940 attr |= FS_NOATIME_FL // Don't update access time attribute
7941 | FS_NODUMP_FL // Don't include in filesystem backup dumps
7942 | FS_SECRM_FL; // Mark file for secure deletion (where supported)
7943 ioctl(fileno(fp), FS_IOC_SETFLAGS, &attr);
7945 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
7946 if (fputs(pem_data, fp) == EOF) {
7948 randolf::rex::mk_exception("Cannot write to temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
7949 } // -x- if !fputs -x-
7952 // --------------------------------------------------------------------------
7953 // Attempt to load certificate chain file, but save the error code for later
7954 // because we need to clean up the temporary file before possibly throwing an
7956 // --------------------------------------------------------------------------
7957 int rc = SSL_CTX_use_certificate_chain_file(__tls_ctx, file.c_str());
7959 // --------------------------------------------------------------------------
7960 // Overwrite the contenst of the temporary file before deleting it so as to
7961 // sabotage a simple attempt to undelete the file and access the certificate.
7963 // We're also re-using the "len" local variable because it's not needed once
7964 // we get the loop started, and it's more optimal to not allocate yet another
7965 // local variable while "len" goes to waste. :D
7966 // --------------------------------------------------------------------------
7967 if (random_fill) { // This option is configurable
7968 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
7969 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
7970 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
7971 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
7972 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
7974 } // -x- if randfill -x-
7975 fchmod(fileno(fp), 0); // Remove all permissions
7976 fclose(fp); // Close file handle
7977 unlink(file.c_str()); // Delete temporary file
7979 // --------------------------------------------------------------------------
7980 // Error check ... was delayed here until after temporary file cleanup.
7981 // --------------------------------------------------------------------------
7982 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);
7985 }; // -x- rsocket* tls_ctx_use_certificate_chain_pem -x-
7987 /*======================================================================*//**
7989 Load a TLS private key in PEM format from a text file and use it in the TLS
7992 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7993 OpenSSL library doesn't document which errors may be returned)
7995 @returns The same rsocket object so as to facilitate stacking
7996 @see tls_ctx_use_privatekey_file
7997 @see tls_ctx_use_privatekey_pem
7998 @see tls_ctx_check_privatekey
8000 *///=========================================================================
8001 rsocket* tls_ctx_use_privatekey_file(
8002 /// Pointer to ASCIIZ path-and-filename of private key file
8004 if (__debug) debug("tls_ctx_use_privatekey_file("
8007 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);
8010 }; // -x- rsocket* tls_ctx_use_privatekey_file -x-
8012 /*======================================================================*//**
8014 Load a TLS private key in PEM format from a text file and use it in the TLS
8017 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8018 OpenSSL library doesn't document which errors may be returned)
8020 @returns The same rsocket object so as to facilitate stacking
8021 @see tls_ctx_use_privatekey_file
8022 @see tls_ctx_use_privatekey_pem
8023 @see tls_ctx_check_privatekey
8025 *///=========================================================================
8026 rsocket* tls_ctx_use_privatekey_file(
8027 /// Path and filename to private key file
8028 const std::string file) {
8029 if (__debug) debug("tls_ctx_use_privatekey_file("
8032 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);
8035 }; // -x- rsocket* tls_ctx_use_privatekey_file -x-
8037 /*======================================================================*//**
8039 Load a TLS private key in PEM format from memory and use it in the TLS
8042 Although this functionality doesn't exist in OpenSSL (at the time of writing
8043 this method), it's provided here in a manner that has exactly the same effect
8044 as the @ref tls_ctx_use_privatekey_file() methods, but without needing the
8045 PEM-formatted private key stored in a file beforehand.
8048 The @c pem_data parameter is a pointer to the memory location that holds the
8049 PEM formatted private key data. If the length of this data isn't specified
8050 or is set to zero (default), then it will be treated as a multiline ASCIIZ
8053 Behind the scenes, we're just writing the pem_data memory to a temporary
8054 file (with severely-limited permissions), then optionally overwriting that
8055 temporary file with random data prior to deleting it (this is the default,
8056 since better security practices should be the default, but on a secured
8057 system it may not be necessary and so this option can also be disabled to
8058 save CPU cycles and reduce overall disk-write I/O operations).
8060 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8061 OpenSSL library doesn't document which errors may be returned)
8063 @returns The same rsocket object so as to facilitate stacking
8064 @see tls_ctx_use_privatekey_file
8065 @see tls_ctx_check_privatekey
8067 *///=========================================================================
8068 rsocket* tls_ctx_use_privatekey_pem(
8069 /// Pointer to private key data in PEM format
8070 const char* pem_data,
8071 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
8073 /// Whether to overwrite the temporary file with random data before deleting it
8074 const bool random_fill = true) {
8075 if (__debug) debug("tls_ctx_use_privatekey_pem(<buf>, " + std::to_string(len)
8076 + ", " + (random_fill ? "true" : "false")
8079 // --------------------------------------------------------------------------
8080 // Measure size of private key if an ASCIIZ string was indicated.
8081 // --------------------------------------------------------------------------
8082 if (len == 0) len = std::strlen(pem_data);
8084 // --------------------------------------------------------------------------
8085 // Generate filename for temporary use.
8086 // --------------------------------------------------------------------------
8087 std::string file = std::filesystem::temp_directory_path();
8088 file.append("/rsocket.")
8089 .append(std::to_string(::getpid()))
8091 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
8093 // --------------------------------------------------------------------------
8094 // Open temporary file.
8095 // --------------------------------------------------------------------------
8096 FILE* fp = fopen(file.c_str(), "w+");
8097 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);
8098 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
8099 if (fputs(pem_data, fp) == EOF) {
8101 randolf::rex::mk_exception("Cannot cannot write to temporary file (to use private key) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8102 } // -x- if !fputs -x-
8105 // --------------------------------------------------------------------------
8106 // Attempt to load private key file, but save the error code for later
8107 // because we need to clean up the temporary file before possibly throwing an
8109 // --------------------------------------------------------------------------
8110 int rc = SSL_CTX_use_PrivateKey_file(__tls_ctx, file.c_str(), SSL_FILETYPE_PEM);
8112 // --------------------------------------------------------------------------
8113 // Overwrite the contenst of the temporary file before deleting it so as to
8114 // sabotage a simple attempt to undelete the file and access the certificate.
8116 // We're also re-using the "len" local variable because it's not needed once
8117 // we get the loop started, and it's more optimal to not allocate yet another
8118 // local variable while "len" goes to waste. :D
8119 // --------------------------------------------------------------------------
8120 if (random_fill) { // This option is configurable
8121 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
8122 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
8123 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
8124 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
8125 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
8127 } // -x- if randfill -x-
8128 fchmod(fileno(fp), 0); // Remove all permissions
8129 fclose(fp); // Close file handle
8130 unlink(file.c_str()); // Delete temporary file
8132 // --------------------------------------------------------------------------
8133 // Error check ... was delayed here until after temporary file cleanup.
8134 // --------------------------------------------------------------------------
8135 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);
8138 }; // -x- rsocket* tls_ctx_use_privatekey_pem -x-
8140 /*======================================================================*//**
8142 Initiate the TLS handshake with the endpoint (which is presumed to be a
8144 This method makes it easier to support application-level commands such as @c
8145 STARTTLS (which are implemented in protocols like SMTP, POP3, and IMAP4).
8147 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8148 OpenSSL library doesn't document which errors may be returned)
8150 @returns The same rsocket object so as to facilitate stacking
8152 @see connect(std::string, int)
8155 *///=========================================================================
8156 rsocket* tls_do_handshake() {
8157 if (__debug) debug("tls_handshake(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8159 __rc_check_tls(SSL_do_handshake(__tls_fd));
8161 }; // -x- rsocket* tls_do_handshake -x-
8163 /*======================================================================*//**
8165 Get OpenSSL's TLS structure.
8166 @returns TLS structure
8167 @returns nullptr = TLS structure not yet allocated
8169 *///=========================================================================
8170 const SSL* tls_fd() noexcept {
8172 }; // -x- int tls_fd -x-
8174 /*======================================================================*//**
8176 Return the current @ref rsocket_sni object that this @c rsocket will use when
8177 accepting incoming encrypted connections.
8178 @returns Pointer to @c rsocket_sni object
8179 @returns nullptr = SNI is not assigned
8181 @see tls_sni(rsocket_sni*)
8183 *///=========================================================================
8184 rsocket_sni* tls_sni() noexcept {
8186 }; // -x- rsocket_sni* tls_sni -x-
8188 /*======================================================================*//**
8190 Set the current @ref rsocket_sni object that this @c rsocket will use when
8191 accepting incoming encrypted connections.
8193 Use the @ref name() method to find out which server name was supplied by the
8194 endpoint that triggered the SNI callback, regardless of whether it matches
8195 any of the TLS certificates used with this rsocket object or the rsocket_sni
8196 object that's associated with this rsocket object. If an SNI callback wasn't
8197 triggered, or if the endpoint didn't provide a server name, then it will
8198 remain unaffected (and the default {empty string} will remain unchanged).
8199 @returns The same rsocket object so as to facilitate stacking
8202 @see is_tls_sni_match
8204 *///=========================================================================
8206 /// Pointer to the @ref rsocket_sni object to use, or specify @c nullptr to
8207 /// remove SNI support from this rsocket object
8208 rsocket_sni* sni) noexcept {
8210 // --------------------------------------------------------------------------
8211 // Remove SNI support.
8212 // --------------------------------------------------------------------------
8213 if (sni == nullptr) {
8214 if (__tls_sni != nullptr) SSL_CTX_set_client_hello_cb(__tls_ctx, nullptr, this);
8216 // --------------------------------------------------------------------------
8217 // Add or set SNI support.
8218 // --------------------------------------------------------------------------
8220 SSL_CTX_set_client_hello_cb(__tls_ctx, tls_sni_callback, this); // Configure SNI callbacks for TLS.
8221 //SSL_CTX_set_tlsext_servername_callback(__tls_ctx, tls_sni_callback); // Don't use this anymore; it's outdated
8222 //SSL_CTX_set_tlsext_servername_arg(__tls_ctx, this); // Don't use this anymore; it's outdated
8224 } // -x- if !sni -x-
8226 // --------------------------------------------------------------------------
8227 // Update internal pointer to the SNI map.
8228 // --------------------------------------------------------------------------
8232 }; // -x- rsocket_sni* tls_sni -x-
8235 /*======================================================================*//**
8237 Get OpenSSL's TLS structure.
8238 @returns TLS structure
8239 @returns nullptr = TLS structure not yet allocated
8241 *///=========================================================================
8242 int static tls_sni_callback(
8243 /// OpenSSL's socket descriptor/handle
8245 /// Where to store the @c alert value
8247 /// Context-specific argument
8248 void* arg) noexcept {
8250 // --------------------------------------------------------------------------
8251 // Internal variables.
8252 // --------------------------------------------------------------------------
8253 rsocket* r = (rsocket*)arg; // We may be updating the TLS context (this is normally the new client's rsocket object)
8254 const unsigned char* out = nullptr;
8255 size_t out_size = 0;
8257 // --------------------------------------------------------------------------
8258 // Obtain a pointer to newly-allocated ClientHello fields data. If *out is
8259 // nullptr, it means that no TLSEXT_TYPE_server_name was received from the
8260 // client, and so the default TLS context will suffice. If out_size is less
8261 // than or equal to 2, then it also won't have what we need.
8262 // --------------------------------------------------------------------------
8263 if (!SSL_client_hello_get0_ext(tls_fd, TLSEXT_TYPE_server_name, &out, &out_size)
8264 || out == nullptr || out_size <= 2) return SSL_CLIENT_HELLO_SUCCESS; // 1
8266 // --------------------------------------------------------------------------
8267 // Scan for TLSEXT_NAMETYPE_host_name, but if we can't find it then we'll
8268 // just leave the default TLS context as is.
8269 // --------------------------------------------------------------------------
8270 unsigned char* p = (unsigned char*)out;
8271 size_t len = (*(p++) << 8);
8273 if (len + 2 != out_size) goto finish;
8275 // --------------------------------------------------------------------------
8276 // We're taking a shortcut by examining only the first element in the list,
8277 // but in the future we need to make this more robust in case other types of
8278 // elements precede what we're looking for.
8280 // Unfortunately, there's no documentation that properly-explains the format
8281 // of the list, so some deeper research into OpenSSL's source code will be
8282 // needed (a cursory look so far has not yielded the necessary insight).
8284 // TODO: Turn this into a loop that supports future clients that provide
8285 // multiple SNI server names in their requests. (Although this isn't
8286 // occuring at present with common end-user tools such as web browsers
8287 // and eMail software, it may happen in the future as client/server
8288 // software becomes more savvy.)
8289 // --------------------------------------------------------------------------
8290 if (out_size == 0 || *p++ != TLSEXT_NAMETYPE_host_name) goto finish;
8292 // --------------------------------------------------------------------------
8293 // Avoid buffer overrun caused by corrupt or prematurely-truncated data.
8294 // --------------------------------------------------------------------------
8295 if (--out_size <= 2) goto finish;
8297 // --------------------------------------------------------------------------
8298 // Extract and use the hostname (SNI server name) that was supplied by the
8299 // endpoint so that the correct TLS certificate can be selected and assigned.
8300 // --------------------------------------------------------------------------
8301 len = (*(p++) << 8);
8303 if (!(len + 2 > out_size)) { // Only process SNI server name string if it's at least 2 bytes long
8304 // Debug: std::cout << "Server name: " << (const char*)p << std::endl; // Debug
8306 // --------------------------------------------------------------------------
8307 // Obtain the correct TLS context (wildcards supported) that is associated
8308 // with the hostname (SNI server name) that was supplied by the endpoint.
8309 // --------------------------------------------------------------------------
8310 const char* sni_name = (const char*)p;
8311 SSL_CTX* new_ctx = r->__tls_sni->get_ctx(sni_name, true, r->tls_ctx());
8313 // --------------------------------------------------------------------------
8314 // Change TLS context so that encryption with the endpoint uses the correct
8315 // TLS certificate (otherwise the endpoint will indicate security risk errors
8316 // to end users, log files, etc., and may {should} reject the connection).
8317 // --------------------------------------------------------------------------
8318//std::cout << "SNI setup... " << sni_name << std::endl;
8319 SSL_set_SSL_CTX(tls_fd, new_ctx);
8320//std::cout << "---1--- " << r->__tls_new_endpoint << std::endl;
8321 r->__tls_new_endpoint->tls_ctx(new_ctx);
8322//std::cout << "---2---" << std::endl;
8323 r->__tls_new_endpoint->__name_sni.assign(sni_name);
8324//std::cout << "SNI name() = " << r->__tls_new_endpoint->name() << std::endl;
8325 r->__tls_new_endpoint->__tls_sni_match = true;
8327 } // -x- if len+2 -x-
8330 // --------------------------------------------------------------------------
8331 // Free the ClientHello fields data, then return SSL_CLIENT_HELLO_SUCCESS.
8332 // Even if we encountered a problem with the ClientHello fields data, we
8333 // still return SSL_CLIENT_HELLO_SUCCESS so that the TLS context will be
8334 // accepted as valid.
8336 // If we return SSL_CLIENT_HELLO_ERROR the connection will fail unnecessarily
8337 // for valid TLS certificates. If the ClientHello fields data is malformed
8338 // to a degree that doesn't satisfy OpenSSL, then OpenSSL will reject it, so
8339 // there's really no point in duplicating what OpenSSL already does properly,
8340 // which OpenSSL will pass through the standard error channels with normal
8341 // error details-and-diagnostics anyway.
8342 // --------------------------------------------------------------------------
8343 //OPENSSL_free((char*)out); // OpenSSL documentation says to do this, but it fails and crashes the program
8345 return SSL_CLIENT_HELLO_SUCCESS; // 1
8346 }; // -x- int tls_sni_callback -x-
8349 /*======================================================================*//**
8351 Convert a 48-bit integer to a machine address in the form of @c
8352 xx:xx:xx:xx:xx:xx where every instance of @c xx is a hexadecimal
8353 representation of each respective 8-bit byte portion.
8355 This method is needed because we don't want to bring in the heavy fmt::format
8356 class as a dependency.
8357 @returns Mac address as 17-character in the typical format expected by system
8360 *///=========================================================================
8361 static std::string to_mac(
8362 /// Pointer to 48-bit integer
8363 const void* addr) noexcept {
8365 h.resize(18); // 48-bit mac address needs 6 hexadecimal pairs of nybbles delimited by colons plus a NULL terminator
8366 h.resize(snprintf(h.data(),
8368 "%02x:%02x:%02x:%02x:%02x:%02x",
8374 ((u_char*)addr)[5])); // Convert, and truncate NULL terminator
8376 }; // -x- std::string to_mac -x-
8378 /*======================================================================*//**
8380 Convert a 48-bit integer to a node address in the form of @c xxxx:xxxx:xxxx
8381 where every instance of @c xxxx is a hexadecimal representation of each
8382 respective 16-bit word portion.
8384 This method is needed because we don't want to bring in the heavy fmt::format
8385 class as a dependency.
8386 @returns Node address as 14-character in the typical format expected by
8387 network administrators
8389 *///=========================================================================
8390 static std::string to_node(
8391 /// Pointer to 48-bit integer
8392 const void* addr) noexcept {
8394 h.resize(15); // 48-bit node address needs 3 hexadecimal sets of words delimited by colons plus a NULL terminator
8395 h.resize(snprintf(h.data(),
8398 ((u_int16_t*)addr)[0],
8399 ((u_int16_t*)addr)[1],
8400 ((u_int16_t*)addr)[2])); // Convert, and truncate NULL terminator
8402 }; // -x- std::string to_node -x-
8404 /*======================================================================*//**
8406 Send a formatted string to the @ref rsocket endpoint.
8408 The @c format is described in the documentation for the POSIX or Standard C
8409 Library @c printf() function.
8410 @throws randolf::rex::xEBADF The underlying socket is not open
8411 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
8412 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
8413 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
8414 @throws randolf::rex::xENOMEM Insufficient memory
8415 @returns The same rsocket object so as to facilitate stacking
8417 @see is_eol_fix_printf
8424 *///=========================================================================
8426 /// Format string to use
8428 /// Variadic arguments in @c va_list format
8431 int rc = ::vasprintf(&buf, format, args);
8432 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
8433 if (__eol_fix_printf && !__eol.empty()) {
8434 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
8436 __send(str.c_str(), str.length());
8440 } catch (std::exception& e) { // Free buf then re-throw the exception
8441 ::free(buf); // Prevent memory leak when an exception is thrown
8444 } // -x- if __eol_fix_printf -x-
8446 }; // -x- rsocket* vprintf -x-
8448 /*======================================================================*//**
8450 Send a formatted string to the @ref rsocket endpoint, and append an EoL
8453 The @c format is described in the documentation for the POSIX or Standard C
8454 Library @c printf() function.
8455 @throws randolf::rex::xEBADF The underlying socket is not open
8456 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
8457 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
8458 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
8459 @throws randolf::rex::xENOMEM Insufficient memory
8460 @returns The same rsocket object so as to facilitate stacking
8463 @see is_eol_fix_printf
8470 *///=========================================================================
8471 rsocket* vprintfline(
8472 /// Format string to use
8474 /// Variadic arguments in @c va_list format
8477 int rc = ::vasprintf(&buf, format, args);
8478 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
8479 if (__eol_fix_printf && !__eol.empty()) {
8480 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
8483 __send(str.c_str(), str.length());
8486 __sendline(buf, rc);
8487 } catch (std::exception& e) { // Free buf then re-throw the exception
8488 ::free(buf); // Prevent memory leak when an exception is thrown
8491 } // -x- if __eol_fix_printf -x-
8493 }; // -x- rsocket* vprintfline -x-
8496 /*======================================================================*//**
8497 Track unencrypted bytes received. When the number of bytes is negative or
8498 zero, it isn't recorded.
8499 This is an internal function.
8500 @returns same value provided in @ref n
8502 *///=========================================================================
8503 int __track_bytes_rx(
8504 /// Number of bytes transferred
8506 if (n > 0) __bytes_rx.fetch_add(n, std::memory_order_relaxed);
8508 }; // -x- __track_bytes_rx -x-
8510 /*======================================================================*//**
8511 Track unencrypted bytes transmitted. When the number of bytes is negative or
8512 zero, it isn't recorded.
8513 This is an internal function.
8514 @returns same value provided in @ref n
8516 *///=========================================================================
8517 int __track_bytes_tx(
8518 /// Number of bytes transferred
8520 if (n > 0) __bytes_tx.fetch_add(n, std::memory_order_relaxed);
8522 }; // -x- __track_bytes_tx -x-
8524 /*======================================================================*//**
8525 Track encrypted bytes received. When the number of bytes is negative or
8526 zero, it isn't recorded.
8527 This is an internal function.
8528 @returns same value provided in @ref n
8530 *///=========================================================================
8531 int __track_crypt_rx(
8532 /// Number of bytes transferred
8534 if (n > 0) __crypt_rx.fetch_add(n, std::memory_order_relaxed);
8536 }; // -x- __track_crypt_rx -x-
8538 /*======================================================================*//**
8539 Track encrypted bytes transmitted. When the number of bytes is negative or
8540 zero, it isn't recorded.
8541 This is an internal function.
8542 @returns same value provided in @ref n
8544 *///=========================================================================
8545 int __track_crypt_tx(
8546 /// Number of bytes transferred
8548 if (n > 0) __crypt_tx.fetch_add(n, std::memory_order_relaxed);
8550 }; // -x- __track_crypt_tx -x-
8552 }; // -x- class rsocket -x-
8554}; // -x- namespace randolf -x-
8556// Save this for a future sendlines() methods.
8557// const void* msg_ptr = msg.c_str(); // Prevent repeated calls to c_str() method
8558// const int len = msg.length(); // Prevent repeated calls to length() method