4#include <randolf/rline>
5#include <randolf/rring>
6#include <randolf/rring_bam>
7#include <randolf/rsocket_io>
8#include <randolf/rsocket_sni>
9#include <randolf/rtools>
10#include <randolf/sockaddr_dl.h>
13#include <bit> // std::endian
14#include <cstdarg> // std::va_list
15#include <cstring> // std::strlen
16#include <ctime> // std::strftime
17#include <exception> // std::exception
18#include <filesystem> // std::filesystem::temp_directory_path
19#include <initializer_list>
20#include <iostream> // std::put_date
21#include <map> // std::map
22#include <memory> // std::shared_ptr
23#include <mutex> // std::mutex
26//#include <sstream> // std::ostringstream
27#include <unordered_map> // std::unordered_map
33#include <string.h> // strerror()
38#include <linux/fs.h> // Flags for ioctl()
40#include <net/if.h> // ifreq structure used by bind()
42#include <netinet/icmp6.h>
43#include <netinet/in.h>
45#include <netpacket/packet.h> // struct sockaddr_ll
47#include <openssl/err.h>
48#include <openssl/ossl_typ.h>
49#include <openssl/ssl.h>
52#include <sys/socket.h>
53#include <sys/stat.h> // fchmod()
54#include <sys/time.h> // TIMEVAL_TO_TIMESPEC macro
58static_assert((sizeof(__time_t) * CHAR_BIT) >= 64, "__time_t of at least 64-bit is required");
62 /*======================================================================*//**
64 This @ref rsocket class provides an easy-to-use and thoroughy-implemented
65 object-oriented socket I/O interface for C++, intended to make socket I/O
66 programming (with or without TLS encryption) easier and more enjoyable.
68 Here's a short list of benefits that are helpful in developing high quality
69 code that's consistent-and-reliable, and improves overall productivity:
71 - eliminating the need to repeatedly write blocks of code that check for
72 errors, by throwing exceptions instead (see @ref randolf::rex::rex class
73 for details and the long list of exceptions that are supported)
74 - eliminating the need to track socket descriptors
75 - eliminating the need to handle encrypted I/O separately (most functions)
76 - eliminating the need to manage memory for many common structures used to
77 interface with socket options, etc.
78 - eliminating the need to record socket I/O statistics with every call to
79 underlying socket I/O functions (see @ref randolf::rsocket_io for
81 - text-line reading/writing with an adapative approach (invented by
82 Randolf Richardson in the 1980s for a custom BBS software project) to
83 automatically detect EoL (End-of-Line) character sequences (unless the
84 developer provides a specific sequence via an @ref eol method) that can
85 determine whether an endpoint is sending Linux/UNIX (standard), MacOS,
86 DOS CR/LF, or even broken LF/CR (reverse of DOS) EoL sequences
87 - transparent support for encryption with many additional features,
88 including STARTTLS, ingress/egress policy enforcement, and SNI
89 - eliminating the complexity of handling events with poll(), select(), and
90 related functions (see the @ref randolf::rsocket_mux class for details)
91 - providing a variety of other useful features that make it easier to
92 communicate with socket endpoints, such as receiving/sending an entire
93 structure via a single call to the new-and-specialized @ref recv_struct
94 or @ref send_struct methods, respectively
96 An rsocket is either the endpoint that our underlying socket will connect to,
97 or it's the server daemon that our underying socket will listen() to and
98 accept() [inbound] connections from.
102 Using the C interface, the programming must check for errors by testing the
103 response codes (most of which are consistent, with a few subtle outliers),
104 which leads to a lot of additional error-checking code with the potential for
105 unintended errors (a.k.a., bugs). This style is necessary in C, but with C++
106 the way to handle errors is with exceptions, so I created this rsocket class
107 to handle all these tedious details behind-the-scenes and, for any socket
108 errors, to generate exceptions so that source code can be greatly simplified
109 (and, as a result, also easier to read and review).
111 Pre-allocating buffers is also handled internally, which is particularly
112 useful when making repeated calls to recv() and related methods. These
113 methods return std::shared_ptr<@c structure > (a C++ smart pointer that aids
114 in the prevention of resource leaks) or std::vector<char> (resized to the
115 actual number of bytes received) as appropriate which eliminates the need to
116 track @c size_t separately.
119 Lower-case letter "r" is regularly used in partial example code to represent
120 an instantiated rsocket object.
122 An ASCIIZ string is a C-string (char* array) that includes a terminating null
123 (0) character at the end.
125 The following custom qualifiers are incorporated into headings by Doxygen
126 alongside method titles throughout the documentation:
127 - @c POSIX denotes a method that is based on POSIX functions by the same
128 name and don't deviate significantly from the POSIX function arguments
129 (intended to be helpful to developers transitioning to/from rsocket or
130 working on source code that utilizes @ref rsocket and POSIX functions)
131 - @c TLS denotes that a method works properly with TLS-encrypted sockets
132 (most of the POSIX functions have been made to work properly with TLS,
133 but for the few rare cases of functions that can't be made to work with
134 TLS an effort has also been made to mention this using Doxygen's
135 "warning" sections in addition to omitting the TLS qualifier)
137 @par Getting started with a few simple examples
139 This is an example of connecting to an HTTP server, using the "GET" command
140 to request the home page (using HTTP/1.0), then receiving-and-displaying the
141 resulting web page's contents via STDOUT (or sending an error message to
142 STDERR). Finally, we exit with an EXIT_SUCCESS (or EXIT_FAILURE) code.
145 #include <iostream> // std::cout, std::cerr, std::endl, etc.
146 #include <randolf/rex>
147 #include <randolf/rsocket>
149 int main(int argc, char *argv[]) {
151 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
152 r.connect("www.example.com", 80);
153 r.sendline("GET / HTTP/1.0");
154 r.sendline("Host: www.example.com");
155 r.sendline("Connection: close");
157 while (r.is_open()) {
158 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
159 } // -x- while data -x-
161 } catch (const randolf::rex::xALL e) {
162 std::cerr << "Socket exception: " << e.what() << std::endl;
164 } catch (const std::exception e) {
165 std::cerr << "Other exception: " << e.what() << std::endl;
169 } // -x- int main -x-
172 Parameter stacking is supported (with methods that return @c rsocket*); in
173 this example, notice that semicolons (";") and "r." references are omittted
174 (when compared with the above):
177 #include <iostream> // std::cout, std::cerr, std::endl, etc.
178 #include <randolf/rex>
179 #include <randolf/rsocket>
181 int main(int argc, char *argv[]) {
183 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
184 r.connect("www.example.com", 80)
185 ->sendline("GET / HTTP/1.0")
186 ->sendline("Host: www.example.com")
187 ->sendline("Connection: close")
189 while (r.is_open()) {
190 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
191 } // -x- while data -x-
193 } catch (const randolf::rex::xALL e) {
194 std::cerr << "Socket exception: " << e.what() << std::endl;
196 } catch (const std::exception e) {
197 std::cerr << "Other exception: " << e.what() << std::endl;
201 } // -x- int main -x-
206 This is meant to be a comprehensive socket class for C++, which is intended
207 to make socket I/O coding easier for developers with some key features:
209 - easy conversion from C-style POSIX sockets due to API consistency
210 - transparent TLS support (OpenSSL dependency)
211 - keys and certificate chains can also be loaded from memory
212 - SNI support (see the @ref rsocket_sni class for more information)
213 - underlying socket handle is accessible (via the @ref socket_fd() method)
214 - socket options are easier to get and to set
215 - sensible support for future or unknown socket options
216 - errors are presented as ~100 separate exception classes: @ref rex::rex
217 - one parent exception class makes it easier to catch all socket errors
218 - a few exceptions groups are also provided to catch groups of errors
219 - new buffers are returned as std::shared_ptr's to eliminate memory leaks
220 - constructors with sensible defaults help to simplify coding
221 - documentation includes code samples (with @c \#include lines as needed)
222 - debug output with helpful output (and option to set output file handle)
223 - low-overhead is considered (this is why there's a bit more overloading)
224 - thread-safety is noted where it is absolutely available (if any caution
225 is warranted, it will also be noted)
226 - can send ASCIIZ (C-strings) without needing to specify string length
227 - can send @c std::string (which tracks its own string length)
228 - each socket can optionally have a @ref name arbitrarily assigned (e.g.,
229 by an algorithm, or even populated with the connecting user's name, or
230 whatever purpose the development goals find it useful for)
232 Additional features that are not part of the typical POSIX standard, but
233 deserve special mention because they are needed so often:
235 - easy access to internal I/O counters (see @ref rsocket_io for details)
236 - your rsocket_io structure can be updated automatically by rsocket's
237 destructor after underlying socket is closed (see @c net_io_final()
239 - printf(), printfline(), vprintf(), vprintfline() // Formatted strings
240 - with automatic EoL sequence substitution (and/or the addition of)
241 - recv_asciiz(), send_asciiz() // ASCIIZ string I/O operations
242 - recv_byte() , send_byte() // 8-bit byte operations (LSB/MSB is N/A)
243 - recv_struct(), send_struct() // Multi-byte operations
244 - recv_uint16(), send_uint16() // 16-bit operations with LSB/MSB variants
245 - recv_uint32(), send_uint32() // 32-bit operations with LSB/MSB variants
246 - recv_uint64(), send_uint64() // 64-bit operations with LSB/MSB variants
247 - recvline(), recv_rline(), sendline() // ASCII text line I/O operations
248 - class-wide configurable newline sequence (defaults to @e autodetect)
249 - @ref rsocket_group class for grouping rsockets (and automatic inclusion
250 by accept() and accept4() methods; this may be a different group from
251 whichever group the parent rsocket is in, if it's even in one)
252 - option to send data to all rsocket objects in the rsocket_group
253 - with support from the rsocket_mux class (for multiplexing operations)
254 - automatic naming policies (possibly like net_io() formatting style)
256 Some advanced features are planned that exceed what the basic socket I/O
257 functions provide, but are also needed:
259 - recv_uint128()/send_uint128() // 128-bit operations w/ LSB/MSB variants
260 - recv_uint(n)/send_uint(n) where "n" specifies the number of bits (which
261 must be a multiple of 8), with LSB/MSB variants
262 - auto-detection of inbound TLS connection (this is turned off by default)
263 - This is not the same as STARTTLS (an application-level command, for
264 which the @ref tls_do_handshake() method will likely be used)
265 - simple timing tracking options using timing_start() and timing_stop()
266 methods, the results of which can be retrieved with timing_get() or a
267 similarly-named group of methods
269 Other features that are not a high priority:
271 - internal support for portability to Microsoft Windows, which is a major
272 undertaking that I know will be time-consuming since Windows Sockets
273 exhibit some nuanced behaviours and are not consistent with POSIX
274 sockets APIs that are used by Linux, UNIX, MacOS, and pretty much all
275 other Operating Systems. Because of this, MS-Windows portability just
276 isn't a high priority for me (without sufficient demand and sufficient
277 funding so I can commit my time without missing mortgage payments,
278 student loan payments {for my kids}, various living expenses, etc.).
281 The @c __time_t structure must be 64-bit (or larger). This becomes espcially
282 important for the @ref recvline method, in addition to Year 2038 compliance.
286 I use the term "ASCIIZ string" to indicate an array of characters that's
287 terminated by a 0 (a.k.a., null). Although this is very much the same as a
288 C-string, the difference is that in many API functions a C-string must often
289 be accompanied by its length value. When referring to an ASCIIZ string, I'm
290 intentionally indicating that the length of the string is not needed because
291 the string is null-terminated. (This term was also commonly used in assembly
292 language programming in the 1970s, 1980s, and 1990s, and as far as I know is
293 still used by machine language programmers today.)
295 UTF-8 works without any problems as it is backward-compatible to 8-bit ASCII,
296 and because @c std::string uses 8-bit bytes to store strings internally. Do
297 keep in mind that the manipulation of UTF-8 substrings will require working
298 with UTF-8 codepoints that may consume one or more bytes, which is beyond the
299 scope of the impartial (to UTF-8 and ASCII) functionality that this rsocket
302 UTF-8 newline codepoints NEL (c2 85), LS (e2 80 a8), and PS (e2 80 a9) garner
303 no special handling at this time - unlike CR/LF (0d/0a) - and are merely
304 treated as non-newline codepoints. There is a possibility of adding support
305 for this in the future, but additional research and planning is required to
306 make sure this works properly. What is most likely is that some UTF-8 flags
307 will be added to support each of these (which will probably be disabled by
308 default) that will be integrated into the readline() methods. This also
309 depends on how widely used these particular codepoints are, and pending
310 further research to determine whether these really are supposed to be used
311 functionally as newlines...
313 So far, there are two UTF-8 codepoints that absolutely are not functional,
314 yet which a small number of people have mistakenly assumed are:
315 - <sup>C</sup><sub>R</sub> = superscript "C" with subscript "R" (e2 90 9d)
316 - <sup>N</sup><sub>L</sub> = superscript "N" with subscript "L" (e2 90 a4)
318 The special characters above are intended to represent the Carriage-Return
319 and New-Line respectively in documentation such as ASCII character reference
320 charts, which were used mostly by IBM in the 1970s and 1980s for instruction
321 manuals (and in other documentation), and also on a few keyboard overlays.
325 I created this class to make it easier to write internet server daemons. I
326 started out using C-style socket functions (because C++ doesn't come with a
327 socket class), but I found that I didn't enjoy mixing something as important
328 and detailed as socket I/O in a procedural way into the object-oriented
329 paradigm that C++ provides.
331 After looking for existing solutions (none of which served as comprehensive
332 replacements for socket I/O), I embarked on creating the rsocket class, and
333 then I began to understand why this probably hadn't been done -- it's a
334 massive undertaking, primarily because there are a lot of functions that are
335 needed to handle socket I/O. Further, [at the time of this writing] the @c
336 sockaddr_storage structure wasn't as widely used as it should be, and so
337 information about it tended to be scarce, incomplete, or incorrect (further
338 research, and diving down into some pretty deep "rabbit holes," was required
339 to understand this properly, which was worthwhile because it resulted in
340 having transparent support for IPv4 and IPv6 without breaking backward
341 compatibility for code expecting specific structures).
343 Moving error codes into exceptions is also a major effort because they are
344 diverse and plentiful, and there are so many errors that can occur at various
345 stages for many different reasons. There are also a few outlier functions
346 that require slightly different approaches to error handling due to subtly
347 different rules for handling their errors, and so the exception-generation
348 wasn't as straight-forward as one might optimistically expect, but this is
349 one of the many benefits of the object-oriented programming pardigm because
350 handling edge cases internally results in a consistent error-handling
351 interface using exceptions that also simplifies the source code. (Need to
352 handle a specific set of conditions? Catch the relevant exceptions for those
353 cases in an inner set of exceptions, and just catch all the others in a more
354 general way without the added complexity of repeatedly checking for errors
355 every step along the way.)
357 So, I dedicated time to make this work, and with the intention of making it
358 an open source project once I got it into a state that's ready for the
359 general public. This required putting my other C++ projects on hold, which
360 was fine because they didn't have strict deadlines and using this socket
361 class in them will speed up development in the long-term anyway, so it's
362 clearly worth the effort to me ... and I sincerely hope that my efforts will
363 be helpful to others too.
365 My background in programming began when I was a young child, teaching myself
366 BASIC and then machine language (when I found BASIC to be too limited) before
367 moving on to other languages like Perl and Java many years later. Eventually
368 I circled around to C (which I chose to learn the hard way by writing some
369 PostgreSQL extensions) and then C++ a few years after that. I have a lot of
370 experience with socket communications, including fully-featured DNS resolver
371 library code in machine language for Novell's NetWare that used C calling
372 conventions and supported varargs, which worked well for the few developers
373 who needed or wanted it.
376 - 2022-Nov-09 v1.00 Initial version
377 - 2022-Nov-26 v1.00 Moved exception handling to a separate class
378 - 2022-Nov-28 v1.00 Completed readline/send functionality
379 - 2022-Dec-03 v1.00 Added endianness transparency
380 - 2022-Dec-04 v1.00 Added printf() support
381 - 2022-Dec-24 v1.00 Added socket MUXing
382 - 2023-Feb-22 v1.00 Added TLS/SSL support
383 - 2023-Mar-10 v1.00 Added TLS-SNI support
384 - 2023-Apr-19 v1.00 Added TLS certificate loading from memory
385 - 2023-May-24 v1.00 Added support for clang++ compilation
386 - 2023-Jun-09 v1.00 Improvements to dynamic internal read buffering
387 - 2023-Oct-31 v1.00 Improvements to various classes
388 - 2024-Feb-21 v1.00 Added is_buffered() method
389 - 2024-Mar-31 v1.00 Completed @ref recvline (implemented a work-around for
390 a subtle SSL_peek failure to extract additional data
391 when a user at an end-point is communicating with
392 "icanon" mode enabled)
393 - 2024-May-24 v1.00 Changed source code to accomodate @c clang++ compiler
394 - 2024-Jun-04 v1.00 Added @c POSIX and @c TLS qualifiers to all applicable
395 methods (Doxygen incorporates into the documentation)
396 - 2024-Oct-23 v1.00 Various minor improvements to the documentation since
398 - 2024-Nov-05 v1.00 Added eol_consumed_seq() method
399 - 2024-Nov-10 v1.00 Added discard() method
400 - 2024-Nov-17 v1.00 Added recv_rline() method
401 - 2024-Nov-19 v1.00 Added recv_as_string() method
402 - 2024-Nov-22 v1.00 Added discard_line() method
403 - 2024-Nov-28 v1.00 Switched internal ring buffer to a proper ring buffer
404 that's now available in the rring class (which was made
405 initially for this purpose)
406 - 2024-Dec-08 v1.00 Improved shutdown() method to handle SSL shutdown fully
407 and added a parameter to prevent calling SSL_shutdown()
408 - 2024-Dec-10 v1.00 Changed eos() method to use faster ioctl POSIX function
409 and OpenSSL's SSL_has_pending function (with TLS)
410 - 2024-Dec-10 v1.00 Added RECVLINE_FLAGS enum to expand the functionality
411 of how the recvline() and recv_rline methods deal with
412 data in specific scenarios, which helps to satisify the
413 needs of specific advanced data processing scenarios
415 @author Randolf Richardson
416 *///=========================================================================
419 // --------------------------------------------------------------------------
420 // The rsocket_group class needs access to some of our internal variables.
421 // --------------------------------------------------------------------------
422 friend class rsocket_group;
424 // --------------------------------------------------------------------------
426 // --------------------------------------------------------------------------
428 int __socket_type = 0;
429 int __socket_protocol = 0;
430 struct sockaddr_storage __socket_addr = {}; // Initialize to all elements to their default values
431 socklen_t __socket_addr_size = sizeof(__socket_addr); // We need to point to this (and it might be modified)
432 int __socket_backlog = SOMAXCONN; // Default backlog is 4096 on Linux, and 128 on older systems
434 // --------------------------------------------------------------------------
435 // Socket flags (internal, but with get-methods to provide read-only access).
436 // --------------------------------------------------------------------------
437 std::atomic_bool __socket_open = false; // Socket is open by way of socket() function
438 std::atomic_bool __socket_connected = false; // Socket is connected
440 // --------------------------------------------------------------------------
441 // TLS flags and variables.
442 // --------------------------------------------------------------------------
443 std::atomic_bool __tls = false; // Indicates whether socket I/O is encrypted with OpenSSL
444 SSL_CTX* __tls_ctx = nullptr; // TLS context (nullptr == no encryption initialized)
445 rsocket_sni* __tls_sni = nullptr; // SNI maps
446 bool __tls_sni_match = false; // Set to TRUE only if SNI matched in the SNI callback
447 SSL* __tls_fd = nullptr; // TLS connection structure (nullptr == no connection)
448 BIO* __tls_rbio = nullptr; // TLS BIO input stream
449 BIO* __tls_wbio = nullptr; // TLS BIO output stream
450 bool __tls_exclusive = false; // See enum TLS_FLAGS::TLS_EXCLUSIVE
451 bool __tls_egress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_EGRESS
452 bool __tls_ingress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_INGRESS
453 bool __tls_server_mode = true; // See enum TLS_FLAGS::TLS_SERVER
454 inline static std::atomic_int __fn_counter = 0; // Used in generating unique-and-threadsafe temporary filenames
455 rsocket* __tls_new_endpoint = nullptr; // Weak pointer to newly-spawned endpoint's rsocket object
457 // --------------------------------------------------------------------------
458 // Keep track of test for whether this host stores integers using big endian.
459 // --------------------------------------------------------------------------
460 inline static const bool __endian_is_msb = htons(42) == 42; // TRUE==MSB / FALSE==LSB
462 // --------------------------------------------------------------------------
463 // General variables. Atomic variables are used for variables as needed to
464 // support operations in a thread-safe manner.
466 // Dynamic buffer expansion for extra-long lines resolves problems with the
467 // default of 4,096 bytes for the read-ahead policy in Linux sockets. These
468 // are handled by the randolf::rring class, and echoed here.
469 // --------------------------------------------------------------------------
470 std::atomic_bool __debug = false;
471 std::atomic<std::FILE*> __debug_fd = stderr;
472 std::string __debug_prefix = "rsocket-debug";
473 #define RSOCKET_BUFFER_SIZE 8192 // Hard-coded default buffer size
474 size_t __buffer_size = RSOCKET_BUFFER_SIZE; // Buffer size used by recv() when a buffer size isn't provided
475 rring* __buffer = nullptr;
476 rring_bam* __buffer_bam = nullptr; // Map of rring's memory blocks
477 std::string __name; // Arbitrary name of this rsocket (used by some applications)
478 std::string __name_sni; // SNI hostname (only used when tls_sni is configured)
480 // --------------------------------------------------------------------------
481 // EoL sequence variables.
482 // --------------------------------------------------------------------------
483 static const char __CR = (char)13; // CTRL-M / ASCII 13 (0Dh) / "\r" / Used by eol() methods
484 static const char __LF = (char)10; // CTRL-J / ASCII 10 (0Ah) / "\n" / Used by eol() methods
485 static constexpr const char* __CRLF = "\x0d\x0a"; // Used by eol() methods
486 std::string __eol; // Used by recvline() method; maintained by eol() methods
487 std::string __eol_consumed_seq; // The EoL sequence that was successfully received by the most recent use of the recvline() method
488 bool __eol_adoption = true; // Whether to adopt the dynamically detected EoL sequence when __eol is empty (the default)
489 std::string __eol_out = std::string(__CRLF); // Used by sendline() method; maintained by eol() methods; also used by sendline() method
490 bool __eol_fix_printf = true; // Whether to replace "\n" sequence in printf() methods to EoL sequence
492 // --------------------------------------------------------------------------
493 // Fine-tuning for __recvline(). This is used with nanosleep().
495 // To test, also use commands in canonical mode, like this:
496 // stty -icanon && openssl s_client host:port
497 // stty -icanon && netcat host port
498 // stty -icanon && telnet host port
499 // --------------------------------------------------------------------------
500 long __recvline_timeout = 0; // Number of seconds (0 = unlimited; maximum for "long" equates to approximately 292 billion years since the UNIX/Linux epoch)
502 // --------------------------------------------------------------------------
503 // Statistical variables.
504 // --------------------------------------------------------------------------
505 rsocket_io* __io_final_addr = nullptr; // Used by ~rsocket() destructor, net_io_final(), and net_io_update()
506 std::atomic_ulong __bytes_rx = 0; // Total number of bytes received
507 std::atomic_ulong __bytes_tx = 0; // Total number of bytes transmitted
508 std::atomic_ulong __crypt_rx = 0; // Total number of encrypted bytes recevied
509 std::atomic_ulong __crypt_tx = 0; // Total number of encrypted bytes transmitted
512 /*======================================================================*//**
514 Optional flags used with rsocket's @ref recvline() and @ref recv_rline()
515 methods to specify relevant text-line reading policies/semantics.
518 *///=========================================================================
519 enum RECVLINE_FLAGS: int {
521 /*----------------------------------------------------------------------*//**
522 The RECVLINE_DEFAULT flag isn't necessary, but it's included here for
523 completeness as it accomodates programming styles that prefer to emphasize
524 when defaults are being relied upon.
525 *///-------------------------------------------------------------------------
526 RECVLINE_DEFAULT = 0,
528 /*----------------------------------------------------------------------*//**
529 The RECVLINE_NO_DISCARD_ON_OVERFLOW flag prevents data from being discarded
530 when an @ref randolf::rex::xEOVERFLOW exception is thrown (which is caused
531 when a line of text exceeds the specified maximum line length).
534 The @c MSG_PEEK flag also prevents the automatic discarding of data,
535 regardless of whether the @ref randolf::rex::xEOVERFLOW is thrown, thus it
536 differs from this flag in multiple ways that vary with how and why it's used.
539 Data will need to be consumed in a different way, such as by attempting to
540 read into a larger buffer than what had been allocated, or by receiving
541 portions or discarding some or all that remains.
543 By default, when the @ref randolf::rex::xEOVERFLOW exception is thrown, all
544 pending data (up to and including the upcoming EoL sequence, or the EoS,
545 whichever comes first) is discarded.
547 *///-------------------------------------------------------------------------
548 RECVLINE_KEEP_ON_OVERFLOW = 1,
550 /*----------------------------------------------------------------------*//**
551 The RECVLINE_PARTIAL flag receives an incomplete line of text and returns it
552 instead of throwing the @ref randolf::rex::xEOVERFLOW exception, and causes
553 the @ref eol_consumed_seq() method to return an empty string (which indicates
554 that no EoL sequence was consumed).
555 @see eol_consumed_seq()
556 *///-------------------------------------------------------------------------
557 RECVLINE_PARTIAL = 2,
559 }; // -x- enum RECVLINE_FLAGS -x-
561 /*======================================================================*//**
563 Optional flags used with various methods to determine whether they will throw
564 an @ref randolf::rex::xETIMEDOUT exception or merely return a @c nullptr, an
565 empty set, or a 0 (zero) when a timeout duration elapses.
567 If you're not sure of whether to use @ref TIMEOUT_EMPTY, @ref TIMEOUT_NULL,
568 or @ref TIMEOUT_ZERO in your code, then I suggest using @ref TIMEOUT_NULL
569 since @c NULL will likely be the most recognizable in most code reviews.
572 You'll know when this is an option because the method will support this.
573 *///=========================================================================
574 enum TIMEOUT_BEHAVIOUR: bool {
576 /*----------------------------------------------------------------------*//**
577 Indicate that an @ref randolf::rex::xETIMEDOUT exception should be thrown
578 when the timeout duration elapses.
579 *///-------------------------------------------------------------------------
580 TIMEOUT_EXCEPTION = true,
582 /*----------------------------------------------------------------------*//**
583 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
584 when the timeout duration elapses.
585 *///-------------------------------------------------------------------------
586 TIMEOUT_EMPTY = false,
588 /*----------------------------------------------------------------------*//**
589 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
590 when the timeout duration elapses.
591 *///-------------------------------------------------------------------------
592 TIMEOUT_NULL = false,
594 /*----------------------------------------------------------------------*//**
595 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
596 when the timeout duration elapses.
597 *///-------------------------------------------------------------------------
598 TIMEOUT_ZERO = false,
600 }; // -x- enum TIMEOUT_BEHAVIOUR -x-
602 /*======================================================================*//**
604 Optional flags used with rsocket's @ref tls() and @ref tls_ctx() methods to
605 specify relevant policies/semantics.
606 *///=========================================================================
607 enum TLS_FLAGS: int {
609 /*----------------------------------------------------------------------*//**
610 The TLS_DEFAULT flag isn't necessary, but it's included here for completeness
611 as it accomodates programming styles that prefer to emphasize when defaults
612 are being relied upon.
613 *///-------------------------------------------------------------------------
616 /*----------------------------------------------------------------------*//**
617 Only encrypted connections are permitted, initially, and all attempts to
618 begin with unencrypted connections will consistently fail.
620 Encrypted connections must begin with a cryptographic handshake packet, or
621 else the connection will be rejected as due to being reasonably assumed to be
624 If this flag isn't set, an application-level mechanism can be used to upgrade
625 to TLS encryption (this is common with connections that begin unencrypted,
626 and issue a proprietary command {such as the @c STARTTLS command in SMTP} to
627 upgrade to TLS later on {of which the @ref tls() method is used to affect an
628 ingress; see the @ref TLS_NO_INGRESS flag for additional information}).
630 Creating an exclusively @c unencrypted connection is accomplished by not
633 @see TLS_NO_EGRESS to prevent support for application-level downgrades to
634 unencrypted connections
635 *///-------------------------------------------------------------------------
638 /*----------------------------------------------------------------------*//**
639 Mid-stream upgrades to encrypted connections are not permitted (e.g., via
640 application-level initiations like the @c STARTTLS command as seen in SMTP),
641 which will also cause calls to the @ref tls() method to fail fast after plain
642 non-encrypted data has already been sent or received.
644 This flag is not necessary when the @ref TLS_EXCLUSIVE flag is set.
645 @see is_tls_ingress_okay()
646 @see tls_do_handshake()
648 *///-------------------------------------------------------------------------
651 /*----------------------------------------------------------------------*//**
652 Mid-stream downgrades to unencrypted connections are not permitted (e.g., via
653 application-level initiations like a hypothetical @c STOPTLS command as seen
654 in FTPS), which will also cause calls to the @ref tls() method to fail fast
655 after encrypted data has already been sent or received.
657 This flag may be combined with the @ref TLS_EXCLUSIVE flag to prevent a
658 connection from being downgraded programatically.
660 Although egress to an unencrypted connection doesn't occur automatically
661 (since egress can only be affected programatically to support commands at the
662 application level), this flag is useful to prevent third-party code from
663 downgrading an encrypted @ref rsocket to unencrypted.
665 Supporting unencrypted communications is strongly discouraged over public
666 networks (e.g., the internet) because unencrypted streams are trivially
667 susceptible to man-in-the-middle attacks that can alter the contents of the
668 data in both directions (which is a particularly dangerous prospect for
669 sending/receiving sensitive information).
670 @see is_tls_egress_okay()
672 *///-------------------------------------------------------------------------
675 /*----------------------------------------------------------------------*//**
676 This is a convenience flag that provides an option for developers to be more
677 clear in their use of the @ref tls() and @ref tls_ctx() methods to indicate
678 intent to rely on what is already the default.
679 @see tls_do_handshake()
681 *///-------------------------------------------------------------------------
684 /*----------------------------------------------------------------------*//**
685 Indicates that this rsocket will be for a server daemon, and to initialize a
686 new TLS context (when one isn't being provided) using OpenSSL's
687 @c TLS_server_method() function instead of OpenSSL's @c TLS_client_method()
688 function (the latter is the default because most code is anticipated to be
692 Setting the CLIENT/SERVER flag incorrectly will typically cause the following
693 error that's difficult to track down, which is usually triggered by calling
694 @ref accept, @ref accept4(), or @ref connect(), and so I hope that including
695 this information here in this documentation will be helpful:
697 error:140C5042:SSL routines:ssl_undefined_function:called a function you should not call
700 The absence of this flag has the same effect as specifying the @ref
703 *///-------------------------------------------------------------------------
706 }; // -x- enum TLS_FLAGS -x-
709 /*======================================================================*//**
710 Return Code check, and throws an rsocket-specific exception if an error
711 occurred, which is indicated by @c -1 (a lot of example code shows @c < @c 1
712 comparisons, but we specifically test for @c -1 because the documentation
713 clearly states @c -1. This is important because a system that can support an
714 extremely high number of socket handles might, in theory, assign handles with
715 values that get interpreted as negative integers, and so less-than-zero tests
716 would result in dropped packets or dropped sockets (any such socket code that
717 allocates such handles obviously must not ever allocate @c -1 since this
718 would definitely be misinterpreted as an error).
720 If rc is not @c -1, then it is simply returned as is.
722 If n is nonzero and rc is 0, then n will override errno. (This option is
723 provided to accomodate the few socket library functions that return values
724 that are never errors, and expect the developer to rely on other means of
725 detecting whether an error occurred. This is an example of where Object
726 Oriented Programming is helpful in making things better.)
727 @returns Original value of @c rc (if no exceptions were thrown)
728 *///=========================================================================
729 const int __rc_check(
732 /// Override @c errno (if not 0)
734 /// Exception handling flags (see @ref rex::REX_FLAGS for details)
735 const int flags = rex::rex::REX_FLAGS::REX_DEFAULT) {
736 if (rc == -1 || (rc == 0 && n != 0)) {
737 const int socket_errno = n == 0 ? errno : n; // If "n" is not zero, use it instead
738 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
739 randolf::rex::mk_exception("", socket_errno, flags); // This function doesn't return (this code ends here)
742 }; // -x- int __rc_check -x-
744 /*======================================================================*//**
745 Similar to @ref __rc_check(), but specialized for handling OpenSSL return
747 *///=========================================================================
748 const int __rc_check_tls(
749 /// Return code (from OpenSSL's API functions)
752 if (__debug) debug("__rc_check_tls(" + std::to_string(rc) + ") throwing exception"); // TODO: Remove this
753 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)
756 }; // -x- int __rc_check -x-
758 /*======================================================================*//**
759 Internal function that opens the socket. This is used by the constructors
760 and their accompanying socket() methods.
762 Throws randolf::rex::xEALREADY exception if the socket is already open to
763 prevent multiple calls to @c open, which can be particularly problematic if
764 any of the additional function calls specify different parameters -- this
765 helps developer(s) to avoid unexpected results if they're inadvertently using
766 the same rsocket object when they intend to use different rsocket objects.
767 *///=========================================================================
769 /// Communication domain; usually one of:@n
771 /// AF_INET6 (IPv6)@n
772 /// AF_UNIX (UNIX domain sockets)
774 /// Communication semantics; usually one of:@n
775 /// SOCK_STREAM (common for TCP)@n
776 /// SOCK_DGRAM (common for UDP)
778 /// Network protocol; usually one of:@n
782 const int protocol) {
783 if (__debug) debug("socket(" + std::to_string(family)
784 + ", " + std::to_string(type)
785 + ", " + std::to_string(protocol)
788 // --------------------------------------------------------------------------
790 // --------------------------------------------------------------------------
791 if (__socket_open) throw randolf::rex::xEALREADY("EALREADY: Socket is already open"); // Socket has already been opened
792 if (family == AF_UNSPEC) throw randolf::rex::xEAFNOSUPPORT("EAFNOSUPPORT: AF_UNSPEC family (SO_DOMAIN) is not supported by socket()");
794 // --------------------------------------------------------------------------
795 // Build minimum parts of __socket_addr structure.
796 // --------------------------------------------------------------------------
797 __socket_addr.ss_family = family;
798 __socket_type = type;
799 __socket_protocol = protocol;
801 // --------------------------------------------------------------------------
802 // Create new socket handle, and save it, then set internal variable to
803 // indicate that the socket is open.
804 // --------------------------------------------------------------------------
805 __socket_fd = __rc_check(::socket(__socket_addr.ss_family, __socket_type, __socket_protocol));
806 __socket_open = true;
808 }; // -x- void __socket -x-
811 /*======================================================================*//**
813 Instantiate an empty rsocket without actually opening a socket, and therefore
814 also without throwing any exceptions (useful in header-file definitions).
817 Instantiating an empty rsocket is particularly useful for header-file
818 definitions since exceptions can't be handled outside of subroutines, and
819 it's also useful for enabling debug() mode @em before setting the socket's
820 configuration with one of the socket() methods; for example:
822 randolf::rsocket r; // Empty rsocket (empty / incomplete initialization)
823 r.debug(true); // Enable debug mode
824 r.socket(...); // Required to complete rsocket initialization
829 The built-in defaults, when not provided, are as follows ("family" is also
830 known as the "communication domain"):
831 - @c family = AF_INET
832 - @c type = SOCK_STREAM
833 - @c protocol = PF_UNSPEC
835 You will need to use one of the socket(...) methods to specify socket details
836 after defining rsocket objects with empty constructors so that you can catch
837 runtime exceptions. (This also provides you with an option to enable debug
838 mode during runtime prior to attempting to open an rsocket.)
843 #include <iostream> // std::cout, std::cerr, std::endl, etc.
844 #include <randolf/rex>
845 #include <randolf/rsocket>
847 randolf::rsocket r; // <-- Empty rsocket initialization (no exceptions)
849 int main(int argc, char *argv[]) {
851 r.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Required to complete rsocket initialization
852 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
853 // ... other socket I/O operations
855 } catch (const randolf::rex::xALL e) {
856 std::cerr << "Socket exception: " << e.what() << std::endl;
858 } catch (const std::exception e) {
859 std::cerr << "Other exception: " << e.what() << std::endl;
863 } // -x- int main -x-
869 @see socket_protocol()
872 *///=========================================================================
873 rsocket() noexcept {}; // -x- constructor rsocket -x-
875 /*======================================================================*//**
877 Instantiate an rsocket based on a minimal subset of the settings in the
878 specified rsocket (using it as a template), without actually opening a
879 socket, and therefore also without throwing any exceptions.
882 This constructor does not suffice as a full clone()-like operation, and is
883 minimal because it's used internally by the @ref accept() and @ref accept4()
886 Details that are absorbed from the template/source rsocket (which eliminates
887 the need to assign, set, and configure various parameters (TLS and TLS SNI
888 parameters will be copied in a passive way by default):
889 - Socket family (SO_DOMAIN)
890 - Socket type (SO_TYPE)
891 - Socket protocol (SO_PROTOCOL)
892 - TLS details (status, context {which includes loaded certificates},
893 policies specified with @ref TLS_FLAGS, etc.)
895 - TLS context (you'll still need to call `tls_ctx(r->tls_ctx())`)
896 - TLS SNI map (you'll still need to call `tls_sni(r->tls_sni())`)
899 The TLS Context will not be initialized because it needs a real socket to
900 draw from. If using TLS, you'll need to use the @ref tls() method after
901 the underlying socket has been initiated.
903 When @c flag_create_socket is set to FALSE, no exceptions will be thrown.
909 @see socket_protocol()
912 *///=========================================================================
914 /// Source rsocket object to use as a template to absorb settings from
915 const rsocket* rtemplate,
916 /// TRUE = create a new socket handle (default)@n
917 /// FALSE = don't create a new socket because a new one will be assigned or
918 /// created later (all variants of the @ref accept() methods do this)
919 const bool flag_create_socket = true) {
921 // --------------------------------------------------------------------------
922 // General socket variables.
923 // --------------------------------------------------------------------------
924 if (flag_create_socket) {
925 __socket(rtemplate->__socket_addr.ss_family,
926 rtemplate->__socket_type,
927 rtemplate->__socket_protocol);
928 } else { // !flag_create_socket
929 __socket_addr.ss_family = rtemplate->__socket_addr.ss_family;
930 __socket_type = rtemplate->__socket_type;
931 __socket_protocol = rtemplate->__socket_protocol;
932 } // -x- if flag_create_socket -x-
933 __name = rtemplate->__name;
935 // --------------------------------------------------------------------------
936 // TLS and SNI settings, but not whether TLS is enabled.
937 // --------------------------------------------------------------------------
938 __tls_ctx = rtemplate->__tls_ctx;
939 __tls_sni = rtemplate->__tls_sni;
941 // --------------------------------------------------------------------------
943 // --------------------------------------------------------------------------
944 __eol.assign( rtemplate->__eol); // Copy source std::string's contents (not a reference)
945 __eol_fix_printf = rtemplate->__eol_fix_printf;
947 }; // -x- constructor rsocket -x-
949 /*======================================================================*//**
951 Instantiate an rsocket with IP/host address and [optional] port number.
953 This is either the endpoint that our underlying socket will be connecting to,
954 or it's the local address of the server daemon that our socket will listen()
955 to and accept() inbound connections from.
959 The built-in defaults, when not provided, are as follows ("family" is also
960 known as the "communication domain"):
961 - @c family = AF_INET
962 - @c type = SOCK_STREAM
963 - @c protocol = PF_UNSPEC
965 The socket() methods do the same work as the constructors with matching
966 arguments, and are provided as convenience methods intended to augment
967 empty rsocket constructors used in header files, but do require an address to
968 be specified (for protocols that need port numbers, such as TCP or UDP, a
969 "port" number also needs to be specified since the default port 0 will result
970 in the dynamic allocation of a port number by the system).
972 For UNIX domain sockets use family AF_UNIX, type SOCK_STREAM, and protocol
973 IPPROTO_IP when instantiating or opening an rsocket.
978 #include <iostream> // std::cout, std::cerr, std::endl, etc.
979 #include <randolf/rex>
980 #include <randolf/rsocket>
982 int main(int argc, char *argv[]) {
984 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Same as socket() method
985 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
986 r.bind("127.0.0.1", 32768);
987 // ... other socket I/O operations
989 } catch (const randolf::rex::xALL e) {
990 std::cerr << "Socket exception: " << e.what() << std::endl;
992 } catch (const std::exception e) {
993 std::cerr << "Other exception: " << e.what() << std::endl;
997 } // -x- int main -x-
1000 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
1001 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
1002 @throws randolf::rex::xEINVAL Protocal family invalid or not available
1003 @throws randolf::rex::xEINVAL Invalid flags in type
1004 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1005 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1006 @throws randolf::rex::xENOBUFS Insufficient memory
1007 @throws randolf::rex::xENOMEM Insufficient memory
1008 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1009 supported within the specified family (a.k.a., communication domain)
1013 *///=========================================================================
1015 /// Communication domain; usually one of:@n
1016 /// AF_INET (IPv4)@n
1017 /// AF_INET6 (IPv6)@n
1018 /// AF_UNIX (UNIX domain sockets)
1020 /// Communication semantics; usually one of:@n
1021 /// SOCK_STREAM (common for TCP)@n
1022 /// SOCK_DGRAM (common for UDP)
1023 const int type = SOCK_STREAM,
1024 /// Network protocol; usually one of:@n
1028 /// PF_UNSPEC (auto-detect)
1029 const int protocol = PF_UNSPEC) {
1030 __socket(family, type, protocol);
1031 }; // -x- constructor rsocket -x-
1033 /*======================================================================*//**
1035 Destructor, which closes any underlying sockets, frees any TLS structures
1036 that were allocated by OpenSSL, and performs any other necessary clean-up
1037 before finally copying the I/O statistics to a designated structure (if one
1038 was specified with the @ref net_io_final() method).
1040 Developers should take care to check that the @ref rsocket_io::is_final flag
1041 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
1042 since there's no guarantee that the destructor will necessarily be executed
1043 in a timely manner (this flag is set last, after all other statistics are
1044 copied into the structure while in a mutex-locked state).
1047 *///=========================================================================
1048 ~rsocket() noexcept {
1050 // --------------------------------------------------------------------------
1052 // --------------------------------------------------------------------------
1054 debug("destructor(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1056 debug(false); // Disable debug mode (might trigger extra clean-up in future)
1057 } // -x- if _debug -x-
1059 // --------------------------------------------------------------------------
1060 // Free memory and resources and close handles that need to be freed/closed.
1061 // --------------------------------------------------------------------------
1062 if (__tls_fd != nullptr) SSL_free(__tls_fd);
1063// if (__tls_sni != nullptr) tls_sni(nullptr); // Remove any SNI callbacks // WE DON'T NEED THIS
1064// if (__tls_ctx != nullptr) SSL_CTX_free(__tls_ctx); // TODO: Research using SSL_up_ref(__tls_ctx)
1065 if (__socket_fd != 0) ::close(__socket_fd); // POSIX close() is ensured with ::close()
1066 if (__buffer_bam != nullptr) free(__buffer_bam); // Release the rring_bam structure
1067 if (__buffer != nullptr) delete __buffer; // Release the rring buffer
1069 // --------------------------------------------------------------------------
1070 // Copy statistics to final location. (We do this last in case there's an
1071 // issue with locking; there shouldn't be, but if there is then at least we
1072 // already we're not inadvertently hanging on to resources at this point that
1073 // need to be freed/closed anyway.)
1074 // --------------------------------------------------------------------------
1075 if (__io_final_addr != nullptr) {
1076 __io_final_addr->lock();
1077 __io_final_addr->bytes_rx = __bytes_rx;
1078 __io_final_addr->bytes_tx = __bytes_tx;
1079 // ->bytes_xx = <bytes in buffer; these are lost bytes that should be subtracted from __bytes_rx>
1080 __io_final_addr->crypt_rx = __crypt_rx;
1081 __io_final_addr->crypt_tx = __crypt_tx;
1082 // ->crypt_xx = <bytes in buffer; these are lost bytes that should be subtracted from __crypt_rx>
1083 __io_final_addr->is_final = true;
1084 __io_final_addr->unlock();
1085 } // -x- if __io_final_addr -x-
1087 }; // -x- destructor ~rsocket -x-
1089 /*======================================================================*//**
1091 Accept new [inbound] socket connetions. (This is typically used in a loop.)
1094 The resulting rsocket object is created before the actual call to the @c
1098 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1099 select(), and accept()/accept4() when using multiple sockets.
1102 To prevent resource leaks, the resulting rsocket needs to be deleted after
1103 it's no longer needed.
1105 @throws randolf::rex::xEBADF The underlying socket is not open
1106 @throws randolf::rex::xECONNABORTED The connection was aborted
1107 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1108 part of the user address space
1109 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1110 @throws randolf::rex::xEHOSTUNREACH No route to host
1111 @throws randolf::rex::xEINTR Interrupted by a signal
1112 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1113 length of the address is invalid
1114 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1115 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1116 @throws randolf::rex::xENETUNREACH No route to network
1117 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1118 @throws randolf::rex::xENOBUFS Insufficient memory
1119 @throws randolf::rex::xENOMEM Insufficient memory
1120 @throws randolf::rex::xENONET Requested host is not reachable on the network
1121 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1123 @throws randolf::rex::xENOSR System ran out of stream resources
1124 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1125 doesn't refer to a socket
1126 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1127 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1128 @throws randolf::rex::xEPROTO Protocol error
1129 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1130 supported within the specified family (a.k.a., communication domain)
1131 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1132 a system call is restartable and can be intercepted-and-redirected
1133 (there is no need to catch this exception unless you are an advanced
1134 developer, in which case you likely still won't need to catch it in
1136 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1137 supported within the specified family (a.k.a., communication domain)
1138 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1139 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1140 no inbound connections are waiting
1142 @returns Newly-created rsocket object representing the connection received
1149 *///=========================================================================
1151 if (__debug) debug("accept(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1154 // --------------------------------------------------------------------------
1155 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1156 // new incoming connection.
1157 // --------------------------------------------------------------------------
1158 __tls_new_endpoint = new rsocket(this, false);
1160 // --------------------------------------------------------------------------
1161 // Wait for incoming connection, and update internal flags in client rsocket.
1162 // --------------------------------------------------------------------------
1163 __tls_new_endpoint->__socket_fd = ::accept(__socket_fd, (sockaddr*)&__tls_new_endpoint->__socket_addr, &__tls_new_endpoint->__socket_addr_size);
1164 if (__tls_new_endpoint->__socket_fd == -1) {
1165 delete __tls_new_endpoint; // Memory management
1166 //__tls_new_endpoint = nullptr;
1167 __rc_check(-1); // This part throws the exception
1169 __tls_new_endpoint->__socket_open = true;
1170 __tls_new_endpoint->__socket_connected = true;
1172 // --------------------------------------------------------------------------
1173 // Perform TLS accept() as well, but only if TLS is enabled.
1175 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1176 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1177 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1178 // --------------------------------------------------------------------------
1180 __tls_new_endpoint->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1181 __tls_new_endpoint->tls(true); // Make sure __tls_fd is configured correctly
1182 __rc_check_tls(SSL_accept(__tls_new_endpoint->__tls_fd));
1183 } // -x- if __tls -x-
1185 return __tls_new_endpoint;
1186 }; // -x- rsocket* accept -x-
1188 /*======================================================================*//**
1190 Accept new [inbound] socket connetions. (This is typically used in a loop.)
1193 The resulting rsocket object is created before the actual call to the @c
1197 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1198 select(), and accept()/accept4() when using multiple sockets.
1201 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1202 aids in the prevention of resource leaks).
1204 @throws randolf::rex::xEBADF The underlying socket is not open
1205 @throws randolf::rex::xECONNABORTED The connection was aborted
1206 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1207 part of the user address space
1208 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1209 @throws randolf::rex::xEHOSTUNREACH No route to host
1210 @throws randolf::rex::xEINTR Interrupted by a signal
1211 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1212 length of the address is invalid
1213 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1214 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1215 @throws randolf::rex::xENETUNREACH No route to network
1216 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1217 @throws randolf::rex::xENOBUFS Insufficient memory
1218 @throws randolf::rex::xENOMEM Insufficient memory
1219 @throws randolf::rex::xENONET Requested host is not reachable on the network
1220 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1222 @throws randolf::rex::xENOSR System ran out of stream resources
1223 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1224 doesn't refer to a socket
1225 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1226 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1227 @throws randolf::rex::xEPROTO Protocol error
1228 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1229 supported within the specified family (a.k.a., communication domain)
1230 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1231 a system call is restartable and can be intercepted-and-redirected
1232 (there is no need to catch this exception unless you are an advanced
1233 developer, in which case you likely still won't need to catch it in
1235 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1236 supported within the specified family (a.k.a., communication domain)
1237 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1238 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1239 no inbound connections are waiting
1241 @returns Newly-created rsocket object representing the connection received,
1242 wrapped in std::shared_ptr (a C++ smart pointer)
1248 *///=========================================================================
1249 std::shared_ptr<rsocket> accept_sp() {
1250 if (__debug) debug("accept_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1253 // --------------------------------------------------------------------------
1254 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1255 // new incoming connection.
1256 // --------------------------------------------------------------------------
1257 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1258 __tls_new_endpoint = r.get(); // The SNI callback needs this
1260 // --------------------------------------------------------------------------
1261 // Wait for incoming connection, and update internal flags in client rsocket.
1262 // --------------------------------------------------------------------------
1263 r->__socket_fd = __rc_check(::accept(__socket_fd, (sockaddr*)&r->__socket_addr, &r->__socket_addr_size));
1264 r->__socket_open = true;
1265 r->__socket_connected = true;
1267 // --------------------------------------------------------------------------
1268 // Perform TLS accept() as well, but only if TLS is enabled.
1270 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1271 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1272 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1273 // --------------------------------------------------------------------------
1274 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1275 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1276 r->tls(true); // Make sure __tls_fd is configured correctly
1277 __rc_check_tls(SSL_accept(r->__tls_fd));
1278 } // -x- if __tls -x-
1281 }; // -x- std::shared_ptr<rsocket> accept_sp -x-
1283 /*======================================================================*//**
1285 Accept new [inbound] socket connetions, with socket flags specified. (This
1286 is typically used in a loop.)
1289 The resulting rsocket object is created before the actual call to the @c
1293 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1294 select(), and accept()/accept4() when using multiple sockets.
1297 To prevent resource leaks, the resulting rsocket needs to be deleted after
1298 it's no longer needed.
1300 @throws randolf::rex::xEBADF The underlying socket is not open
1301 @throws randolf::rex::xECONNABORTED The connection was aborted
1302 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1303 part of the user address space
1304 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1305 @throws randolf::rex::xEHOSTUNREACH No route to host
1306 @throws randolf::rex::xEINTR Interrupted by a signal
1307 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1308 length of the address is invalid
1309 @throws randolf::rex::xEINVAL Invalid value in flags
1310 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1311 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1312 @throws randolf::rex::xENETUNREACH No route to network
1313 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1314 @throws randolf::rex::xENOBUFS Insufficient memory
1315 @throws randolf::rex::xENOMEM Insufficient memory
1316 @throws randolf::rex::xENONET Requested host is not reachable on the network
1317 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1319 @throws randolf::rex::xENOSR System ran out of stream resources
1320 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1321 doesn't refer to a socket
1322 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1323 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1324 @throws randolf::rex::xEPROTO Protocol error
1325 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1326 supported within the specified family (a.k.a., communication domain)
1327 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1328 a system call is restartable and can be intercepted-and-redirected
1329 (there is no need to catch this exception unless you are an advanced
1330 developer, in which case you likely still won't need to catch it in
1332 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1333 supported within the specified family (a.k.a., communication domain)
1334 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1335 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1336 no inbound connections are waiting
1338 @returns Newly-created rsocket object representing the connection received
1345 *///=========================================================================
1347 /// SOCK_NONBLOCK@n SOCK_CLOEXEC
1348 const int posix_flags = 0) {
1349 if (__debug) debug("accept4(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1350 + ", " + std::to_string(posix_flags)
1353 // --------------------------------------------------------------------------
1354 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1355 // new incoming connection.
1356 // --------------------------------------------------------------------------
1357 rsocket* __tls_new_endpoint = new rsocket(this, false);
1359 // --------------------------------------------------------------------------
1360 // Wait for incoming connection, and update internal flags in client rsocket.
1361 // --------------------------------------------------------------------------
1362 __tls_new_endpoint->__socket_fd = ::accept4(__socket_fd, (sockaddr*)&__tls_new_endpoint->__socket_addr, &__tls_new_endpoint->__socket_addr_size, posix_flags);
1363 if (__tls_new_endpoint->__socket_fd == -1) {
1364 delete __tls_new_endpoint; // Memory management
1365 __rc_check(-1); // This part throws the exception
1367 __tls_new_endpoint->__socket_open = true;
1368 __tls_new_endpoint->__socket_connected = true;
1370 // --------------------------------------------------------------------------
1371 // Perform TLS accept() as well, but only if TLS is enabled.
1373 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1374 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1375 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1376 // --------------------------------------------------------------------------
1377 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1378 __tls_new_endpoint->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1379 __tls_new_endpoint->tls(true); // Make sure __tls_fd is configured correctly
1380 __rc_check_tls(SSL_accept(__tls_new_endpoint->__tls_fd));
1381 } // -x- if __tls -x-
1383 return __tls_new_endpoint;
1384 }; // -x- rsocket* accept4 -x-
1386 /*======================================================================*//**
1388 Accept new [inbound] socket connetions, with socket flags specified. (This
1389 is typically used in a loop.)
1392 The resulting rsocket object is created before the actual call to the @c
1396 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1397 select(), and accept()/accept4() when using multiple sockets.
1400 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1401 aids in the prevention of resource leaks).
1403 @throws randolf::rex::xEBADF The underlying socket is not open
1404 @throws randolf::rex::xECONNABORTED The connection was aborted
1405 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1406 part of the user address space
1407 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1408 @throws randolf::rex::xEHOSTUNREACH No route to host
1409 @throws randolf::rex::xEINTR Interrupted by a signal
1410 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1411 length of the address is invalid
1412 @throws randolf::rex::xEINVAL Invalid value in flags
1413 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1414 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1415 @throws randolf::rex::xENETUNREACH No route to network
1416 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1417 @throws randolf::rex::xENOBUFS Insufficient memory
1418 @throws randolf::rex::xENOMEM Insufficient memory
1419 @throws randolf::rex::xENONET Requested host is not reachable on the network
1420 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1422 @throws randolf::rex::xENOSR System ran out of stream resources
1423 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1424 doesn't refer to a socket
1425 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1426 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1427 @throws randolf::rex::xEPROTO Protocol error
1428 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1429 supported within the specified family (a.k.a., communication domain)
1430 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1431 a system call is restartable and can be intercepted-and-redirected
1432 (there is no need to catch this exception unless you are an advanced
1433 developer, in which case you likely still won't need to catch it in
1435 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1436 supported within the specified family (a.k.a., communication domain)
1437 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1438 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1439 no inbound connections are waiting
1441 @returns Newly-created rsocket object representing the connection received,
1442 wrapped in std::shared_ptr (a C++ smart pointer)
1448 *///=========================================================================
1449 std::shared_ptr<rsocket> accept4_sp(
1450 /// SOCK_NONBLOCK@n SOCK_CLOEXEC
1451 const int posix_flags = 0) {
1452 if (__debug) debug("accept4_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1453 + ", " + std::to_string(posix_flags)
1456 // --------------------------------------------------------------------------
1457 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1458 // new incoming connection.
1459 // --------------------------------------------------------------------------
1460 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1461 __tls_new_endpoint = r.get(); // The SNI callback needs this
1463 // --------------------------------------------------------------------------
1464 // Wait for incoming connection, and update internal flags in client rsocket.
1465 // --------------------------------------------------------------------------
1466 r->__socket_fd = __rc_check(::accept4(__socket_fd, (sockaddr*)&r->__socket_addr, &r->__socket_addr_size, posix_flags));
1467 r->__socket_open = true;
1468 r->__socket_connected = true;
1470 // --------------------------------------------------------------------------
1471 // Perform TLS accept() as well, but only if TLS is enabled.
1473 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1474 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1475 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1476 // --------------------------------------------------------------------------
1477 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1478 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1479 r->tls(true); // Make sure __tls_fd is configured correctly
1480 __rc_check_tls(SSL_accept(r->__tls_fd));
1481 } // -x- if __tls -x-
1482// if (__tls_ctx != nullptr) { r->tls(true); r->__tls = (bool)__tls; } // Make sure __tls_fd is allocated appropriately
1485 }; // -x- std::shared_ptr<rsocket> accept4_sp -x-
1487 /*======================================================================*//**
1489 Override the default @ref listen backlog for this rsocket.
1492 The default backlog is SOMAXCONN (4096 on Linux, and 128 on older systems).
1494 @returns The same rsocket object so as to facilitate stacking
1497 *///=========================================================================
1499 /// Backlog queue size (0 = use SOMAXCONN, which is the system default of
1500 /// 4096 on Linux, and 128 on older systems)
1501 int backlog = 0) noexcept {
1502 __socket_backlog = backlog == 0 ? SOMAXCONN : backlog;
1504 }; // -x- rsocket* backlog -x-
1506 /*======================================================================*//**
1508 Find out what this rsocket's default listen backlog is.
1509 @returns The default listen backlog
1512 *///=========================================================================
1513 int backlog() noexcept {
1514 return __socket_backlog;
1515 }; // -x- int backlog -x-
1517 /*======================================================================*//**
1519 Bind this socket to the specified network address (and port number if the
1520 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1521 used for server deamons, but can also be used to control the address from
1522 which the local host will make an outbound connection via the @ref connect()
1523 method (this bound address is the address from which the endpoint will
1524 recieve your connection).
1526 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
1527 (or 1023 on some Operating Systems that test only for below 1024) in
1528 the absence of elevated access or the absence of a capability flag
1530 @throws randolf::rex::xEACCES If binding to an interface on systems that
1531 require elevated access for direct interface binding in absence of
1532 said elevated access
1533 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1534 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
1535 address is not available on any local interface
1536 @throws randolf::rex::xEBADF The underlying socket is not open
1537 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1538 part of the user address space
1539 @throws randolf::rex::xEINVAL Socket is already bound
1540 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
1541 valid for this socket's family (a.k.a., communication domain)
1542 @throws randolf::rex::xENOMEM Insufficient memory
1543 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1544 doesn't refer to a socket
1546 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1547 @throws randolf::rex::xEACCES Write permission or search permission denied
1548 on a component of the path prefix (@c AF_UNIX family)
1549 @throws randolf::rex::xELOOP Too many symbolic links encountered while
1550 resolving address (@c AF_UNIX family)
1551 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
1552 @throws randolf::rex::xENOENT One of the path's directory components doesn't
1553 exist (@c AF_UNIX family)
1554 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
1555 directory (@c AF_UNIX family)
1556 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
1558 @returns The same rsocket object so as to facilitate stacking
1559 @see bind(std::string, int)
1564 *///=========================================================================
1566 /// Socket address structure
1567 const struct sockaddr* addr,
1568 /// Length of socket structure
1569 const socklen_t addrlen = sizeof(sockaddr)) {
1570 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1572 + ", size=" + std::to_string(addrlen)
1574 if (addrlen > sizeof(sockaddr_storage)) randolf::rex::mk_exception("addlen is too large for bind()", EINVAL);
1575 __rc_check(::bind(__socket_fd, addr, addrlen));
1576 std::memcpy(&__socket_addr, addr, addrlen); // No errors were returned by bind(), so now we'll copy addr to __socket_addr
1578 } // -x- rsocket* bind -x-
1580 //===========================================================================
1582 // TODO: Document usage of IP_BIND_ADDRESS_NO_PORT to support connecting
1583 // from a specific IP address without losing ephemeral port selection.
1584 // Notes: https://idea.popcount.org/2014-04-03-bind-before-connect/
1586 // TODO: Support AF_BLUETOOTH addresses
1588 // https://man7.org/linux/man-pages/man7/address_families.7.html
1590 // TODO: Support AF_IPX addresses
1592 // https://man7.org/linux/man-pages/man7/address_families.7.html
1594 // TODO: Support AF_APPLETALK addresses
1595 // Reference: https://man7.org/linux/man-pages/man7/ddp.7.html
1597 // TODO: Support AF_PACKET addresses
1598 // Reference: https://man7.org/linux/man-pages/man7/packet.7.html
1600 // TODO: Support AF_X25 addresses
1601 // Reference: https://man7.org/linux/man-pages/man7/x25.7.html
1603 // TODO: Support AF_NETLINK addresses
1604 // Reference: https://man7.org/linux/man-pages/man7/netlink.7.html
1606 /*======================================================================*//**
1608 Bind this socket to the specified network address (and port number if the
1609 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1610 used for server deamons, but can also be used to control the address from
1611 which the local host will make an outbound connection via the @ref connect()
1612 method (this bound address is address from which the endpoint will recieve
1615 In addition to the standard IPv4 (AF_INET family) and IPv6 (AF_INET6 family)
1616 support, rsocket also supports a few other binding options in a manner that
1617 makes it easy to utilize, without having to handle special implementation
1618 details (because they're handled behind-the-scenese by this rsocket class).
1620 The address string supports a number of different notations and formats,
1621 which are documented, hereunder, with examples:
1624 - Network interfaces
1625 - UNIX domain sockets
1628 @par IPv4 address notations (address family @c AF_INET)
1629 Takes the form of @c x.x.x.x where each "x" represents an octet in the range
1630 of @c 0 through @c 255 (e.g., @c 127.0.0.1).
1632 Under a proper implenetation of TCP/IP, an IPv4 address can be abbreviated
1633 to fewer octets. The following examples demonstrate this (an unabbreviated
1634 IPv4 address is included for completeness):
1635 - @c 0.0.0.1 may be abbreviated to @c 1
1636 - @c 4.0.0.1 may be abbrevaited to @c 4.1
1637 - @c 4.3.0.1 may be abbreviated to @c 4.3.1
1638 - @c 4.3.2.1 is not abbreviated (this is the most common usage)
1640 @par Example of binding to IPv4 localhost
1643 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1644 #include <randolf/rex>
1645 #include <randolf/rsocket>
1647 int main(int argc, char *argv[]) {
1649 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1650 r.bind("127.0.0.1", 32768); // <-- You are here
1651 // ... other socket I/O operations
1653 } catch (const randolf::rex::xEACCES e) {
1654 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1655 return EXIT_FAILURE;
1656 } catch (const randolf::rex::xALL e) {
1657 std::cerr << "Socket exception: " << e.what() << std::endl;
1658 return EXIT_FAILURE;
1659 } catch (const std::exception e) {
1660 std::cerr << "Other exception: " << e.what() << std::endl;
1661 return EXIT_FAILURE;
1663 return EXIT_SUCCESS;
1664 } // -x- int main -x-
1668 Specifying the IP address of @c 0.0.0.0 will bind to all IPv4 addresses that
1669 are assigned to all of the host machine's network interfaces with IPv4
1672 Specifying the IP address of @c 127.0.0.1 (localhost) is useful for serving
1673 only those applications that are running on the local host and use an IPv4
1674 socket to communicate.
1677 @par IPv6 address notations (address family @c AF_INET6)
1678 Takes the form of @c x:x:x:x:x:x:x:x where each "x" represents a segment in
1679 the range of @c 0 through @c ffff in hexadecimal (e.g., `0:0:0:0:0:0:0:1`),
1680 or `x:x:x:x:x:x:y.y.y.y` where `y.y.y.y` represents an [unabbreviated] IPv4
1681 address (which merely replaces the last two IPv6 segments).
1683 Under a proper implenetation of TCP/IP, an IPv6 address can be abbreviated
1684 to fewer segments by using a sequence of two colons (`::`) to indicate that
1685 the undefined segments are @c 0 (this abbreviation can only be used once,
1686 and may represent segments at the beginning or end, or anywhere in between).
1687 The following examples demonstrate this (an unabbreviated IPv6 address is
1688 included for completeness):
1689 - `0:0:0:0:0:0:0:1` may be abbreviated to `::1`
1690 - `0:0:0:0:0:0:2:1` may be abbreviated to `::2:1`
1691 - `8:0:0:0:0:0:2:1` may be abbreviated to `8::2:1`
1692 - `8:7:0:0:0:0:2:1` may be abbreviated to `8:7::2:1`
1693 - `8:7:0:0:0:0:0:0` may be abbreviated to `8:7::`
1694 - `8:0:0:0:0:0:0:0` may be abbreviated to `8::`
1695 - `8:7:6:5:4:3:2:1` is not abbreviated
1697 @par Example of binding to IPv6 localhost
1700 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1701 #include <randolf/rex>
1702 #include <randolf/rsocket>
1704 int main(int argc, char *argv[]) {
1706 randolf::rsocket r(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1707 r.bind("::1", 32768); // <-- You are here
1708 // ... other socket I/O operations
1710 } catch (const randolf::rex::xEACCES e) {
1711 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1712 return EXIT_FAILURE;
1713 } catch (const randolf::rex::xALL e) {
1714 std::cerr << "Socket exception: " << e.what() << std::endl;
1715 return EXIT_FAILURE;
1716 } catch (const std::exception e) {
1717 std::cerr << "Other exception: " << e.what() << std::endl;
1718 return EXIT_FAILURE;
1720 return EXIT_SUCCESS;
1721 } // -x- int main -x-
1725 Specifying the IP address of @c :: will bind to all IPv6 addresses that are
1726 assigned to all of the host machine's network interfaces with IPv6 bindings.
1728 Specifying the IP address of @c ::1 (localhost) is useful for serving only
1729 those applications that are running on the local host and use an IPv6 socket
1733 @par Interface address notation (address families @c AF_INET and @c AF_INET6)
1734 Takes the form of @c if=name where "name" represents the name of a local
1737 Interface binding is useful when binding to interfaces that aren't configured
1738 with a static IP address because they were dymamically configured via the
1739 Dynamic Host Configuration Protocol (DHCP) or Stateless Address Configuration
1740 (SLAAC), or the configuration was changed manually by an administrator and
1741 you don't want your software to handle such changes gracefully, even if the
1742 new IP address is within the scope of an entirely different CIDR. (Changing
1743 between the IPv4 and IPv6 addresses is not supported, however, due to the
1744 fundamental differences between these two address families that includes
1745 differences beyond that of IP address format, although under a proper
1746 implementation of TCP/IP the binding should survive such changes when the IP
1747 address is reverted to the initial IP address family of the bound interface.)
1749 Examples of interfaces include:
1750 - `if=lo` typical for localhost virtual network adapter
1751 - `if=bond0` typical for the first bonded virtual network adapter (used in
1752 failover and load-balancing network configurations)
1753 - `if=br0` typical for the first bridge virtual network adapter
1754 - `if=eth0` typical for the first ethernet network adapter
1755 - `if=tap0` typical for the first virtual layer 2 ethernet switch (used by
1756 VPNs to extend a remote network into the local network stack)
1757 - `if=tun0` typical for the first virtual layer 3 ethernet tunnel (used by
1758 VPNs to provide access to a remote network)
1759 - `if=wlo1` typical for the first wireless network adapter
1761 @par Example of binding to interface localhost
1764 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1765 #include <randolf/rex>
1766 #include <randolf/rsocket>
1768 int main(int argc, char *argv[]) {
1770 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1771 r.bind("if=lo", 32768); // <-- You are here
1772 // ... other socket I/O operations
1774 } catch (const randolf::rex::xEACCES e) {
1775 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1776 return EXIT_FAILURE;
1777 } catch (const randolf::rex::xALL e) {
1778 std::cerr << "Socket exception: " << e.what() << std::endl;
1779 return EXIT_FAILURE;
1780 } catch (const std::exception e) {
1781 std::cerr << "Other exception: " << e.what() << std::endl;
1782 return EXIT_FAILURE;
1784 return EXIT_SUCCESS;
1785 } // -x- int main -x-
1789 This is not a standard feature of the POSIX bind() function. This rsocket
1790 class uses the setsockopt() function behind-the-scenes to configure (enable)
1791 the @c SO_BINDTODEVICE option, and then the bind() function is called with
1792 the IPv4 address @c 0.0.0.0 if the underlying socket was initialized to the
1793 @c AF_INET family, or the IPv6 address @c :: if the underlying socket was
1794 initialized to the @c AF_INET6 family.
1796 Specifying the interface address of `if=lo` (localhost) is useful for serving
1797 only those applications that are running on the local host and use an IPv4 or
1798 IPv6 socket to communicate.
1801 @par UNIX Domain Sockets address-notation (address family @c AF_UNIX)
1802 Takes the form of @c /path where "/path" represents an absolute path on the
1803 local file system (the path can also be relative, in which case it doesn't
1804 begin with a slash (`/`) character, but extra care is needed to ensure that
1805 the path will actually be at its expected location).
1807 @par Example of binding to UNIX domain sockets
1810 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1811 #include <randolf/rex>
1812 #include <randolf/rsocket>
1814 int main(int argc, char *argv[]) {
1816 randolf::rsocket r(AF_UNIX, SOCK_STREAM);
1817 r.bind("/var/run/rsocket-test-socket.tmp"); // <-- You are here
1818 // ... other socket I/O operations
1820 } catch (const randolf::rex::xEACCES e) {
1821 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1822 return EXIT_FAILURE;
1823 } catch (const randolf::rex::xALL e) {
1824 std::cerr << "Socket exception: " << e.what() << std::endl;
1825 return EXIT_FAILURE;
1826 } catch (const std::exception e) {
1827 std::cerr << "Other exception: " << e.what() << std::endl;
1828 return EXIT_FAILURE;
1830 return EXIT_SUCCESS;
1831 } // -x- int main -x-
1835 Normal socket I/O functionality is used to exchange data with another process
1836 that can open the same UNIX domain socket (normally on the same host,
1837 although it may not be outside the realm of possibility for future Linux
1838 kernels to support UNIX domain sockets on remote hosts).
1841 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
1842 (or 1023 on some Operating Systems that test only for below 1024) in
1843 the absence of elevated access or the absence of a capability flag
1845 @throws randolf::rex::xEACCES If binding to an interface on systems that
1846 require elevated access for direct interface binding in absence of
1847 said elevated access
1848 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1849 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
1850 address is not available on any local interface
1851 @throws randolf::rex::xEBADF The underlying socket is not open
1852 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1853 part of the user address space
1854 @throws randolf::rex::xEINVAL Socket is already bound
1855 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
1856 valid for this socket's family (a.k.a., communication domain)
1857 @throws randolf::rex::xENOMEM Insufficient memory
1858 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1859 doesn't refer to a socket
1861 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1862 @throws randolf::rex::xEACCES Write permission or search permission denied
1863 on a component of the path prefix (@c AF_UNIX family)
1864 @throws randolf::rex::xELOOP Too many symbolic links encountered while
1865 resolving address (@c AF_UNIX family)
1866 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
1867 @throws randolf::rex::xENOENT One of the path's directory components doesn't
1868 exist (@c AF_UNIX family)
1869 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
1870 directory (@c AF_UNIX family)
1871 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
1873 @returns The same rsocket object so as to facilitate stacking
1874 @see bind(const struct sockaddr*, const socklen_t)
1878 *///=========================================================================
1880 /// IPv4 address, IPv6 address, hostname, UNIX domain sockets path, or specialized variant (see notes, above)
1881 const std::string address,
1882 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
1883 const int port = 0) {
1884 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1886 + " port=" + std::to_string(port) + (port == 0 ? " (emphemeral or not-applicable)" : "")
1887 + ", size=" + std::to_string(__socket_addr_size)
1890//std::cout << " address: " << address << std::endl;
1891 if (address.starts_with("if=")) { // Bind to interface
1892// TODO: Use family() to simplify this section of code
1894 if (address.size() - 3 >= sizeof(ifr.ifr_name)) {} // Throw exception because ASCIIZ name will be too long
1895 address.copy(ifr.ifr_name, address.size() - 3, 3); // Copy address, excluding the "if=" portion
1896 __rc_check(::setsockopt(__socket_fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)));
1897 // TODO: Figure out how to use INADDR_ANY instead of 0 or :: (assuming this is even possible)
1898 // TODO: Test IPv6 more (telnet doesn't seem to be able to connect, and continues to work on IPv4 address)
1899 __socket_addr = *mk_sockaddr_storage(__socket_addr.ss_family == AF_INET6 ? "::" : "0", port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
1900 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
1902// } else if (address.starts_with("if_cidr=")) { // Bind to interface based on matching CIDR
1903// // REMINDER: Update static family() method as well
1904// getifaddrs: https://man7.org/linux/man-pages/man3/getifaddrs.3.html
1905// } else if (address.starts_with("if_mac=")) { // Bind to interface based on its physical/hardware machine address
1906// // REMINDER: Update static family() method as well
1907// // Resolve interface name, and then just do what if= does (above)
1910 __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
1911 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
1912// __rc_check(::bind(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
1915 } // -x- rsocket* bind -x-
1917 /*======================================================================*//**
1919 Find out what buffer size is used by the various recv() methods.
1920 @returns Buffer size (in bytes)
1921 @see buffer_size(const size_t nbytes)
1924 *///=========================================================================
1925 const size_t buffer_size() noexcept { return __buffer->get_size(); }; // -x- size_t buffer_size -x-
1927 /*======================================================================*//**
1929 Override the default buffer size (typically 8,192 bytes) used by the various
1932 If resetting to the compiled-in default, use the buffer_size_reset() method
1933 instead of setting the value directly. This ensures that future versions,
1934 with a different compiled-in default, will be reset to the compiled-in value.
1935 @exception std::overflow_error If the new size exceeds the amount of data
1936 that's already present in the ring buffer
1937 @returns The same rsocket object so as to facilitate stacking
1939 @see buffer_size_reset
1941 *///=========================================================================
1942 rsocket* buffer_size(
1943 /// Size of the new buffer (in bytes)
1944 const size_t nbytes) {
1945 __buffer_size = nbytes;
1946 if (__debug) debug("buffer_size(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1947 + " nbytes=" + std::to_string(nbytes)
1949 if (__buffer != nullptr) __buffer->set_size(nbytes);
1951 }; // -x- rsocket* buffer_size -x-
1953 /*======================================================================*//**
1955 Reset the default buffer size (typically 1024) used by the various recv()
1958 This method is preferred for resetting to the compiled-in default instead of
1959 setting the value directly. This ensures that future versions, with a
1960 different compiled-in default, will be reset to the compiled-in value.
1961 @exception std::overflow_error If the new size exceeds the amount of data
1962 that's already present in the ring buffer
1963 @returns The same rsocket object so as to facilitate stacking
1964 @see buffer_size(const size_t nbytes)
1966 *///=========================================================================
1967 rsocket* buffer_size_reset() {
1968 __buffer_size = RSOCKET_BUFFER_SIZE;
1969 if (__debug) debug("buffer_size_reset(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1970 + " nbytes=" + std::to_string(RSOCKET_BUFFER_SIZE)
1972 if (__buffer != nullptr) __buffer->set_size(__buffer_size);
1974 }; // -x- rsocket* buffer_size_reset -x-
1976 //===========================================================================
1978 // TODO: If family is AF_UNIX then also unlink the domain socket file.
1980 /*======================================================================*//**
1982 Close this rsocket. (If this rsocket was already closed, then calling this
1983 method additional times will have no effect, and will not cause exceptions to
1986 This method may throw exceptions in some circumstances, which is extremely
1987 rare. If you prefer to close without having to contend with any exceptions
1988 (e.g., while calling close() from within a destructor), the close_passive()
1989 method will return an integer indicating success/failure instead of throwing
1990 an exception, and then you'll have to handle errno manually (which is useful
1991 in destructors because any error can merely be handled procedurally).
1993 @throws randolf::rex::xEBADF The underlying socket is not open
1994 @throws randolf::rex::xEINTR Interrupted by a signal
1995 @throws randolf::rex::xEIO An I/O error occurred
1997 @returns The same rsocket object so as to facilitate stacking
2001 *///=========================================================================
2003 if (__socket_open) {
2004 __rc_check(::close(__socket_fd));
2005 __socket_open = false;
2006 } // -x- if __socket_open -x-
2008 }; // -x- rsocket* close -x-
2010 /*======================================================================*//**
2012 Close this rsocket without throwing any exceptions (an error code is returned
2013 instead, which is useful while calling close_passive() from within a
2015 @returns 0 = success
2016 @returns -1 = error (errno will be set accordingly)
2019 *///=========================================================================
2020 int close_passive() noexcept {
2021 if (__socket_open) {
2022 int rc = ::close(__socket_fd);
2023 if (rc == 0) __socket_open = false;
2025 } // -x- if __socket_open -x-
2026 return 0; // Indicate success (because the socket was previously closed successfully)
2027 }; // -x- int close_passive -x-
2029 /*======================================================================*//**
2031 Connect this socket to a specific endpoint (which may differ from this
2032 rsocket's address that was previously configured by the @ref bind() method).
2034 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
2035 @throws randolf::rex::xEADDRNOTAVAIL No ephemeral ports are available for
2036 assignment to unbound socket
2037 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
2038 @throws randolf::rex::xEAGAIN Insufficient entries in the routing cache
2039 @throws randolf::rex::xEALREADY Previous connection attempt not completed on
2041 @throws randolf::rex::xEBADF The underlying socket is not open
2042 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
2044 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2045 part of the user address space
2046 @throws randolf::rex::xEINPROGRESS Connection cannot be completed immediately
2047 on nonblocking socket (@c AF_UNIX family fails with xEAGAIN instead)
2048 @throws randolf::rex::xEINTR Interrupted by a signal
2049 @throws randolf::rex::xEISCONN Socket is already connected
2050 @throws randolf::rex::xENETUNREACH No route to network
2051 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2052 doesn't refer to a socket
2053 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
2054 @throws randolf::rex::xEPERM Can't connect to a broadcast address when the
2055 socket's broadcast flag isn't enabled
2056 @throws randolf::rex::xEPROTOTYPE Socket type doesn't support the requested
2057 communications protocol (e.g., connecting a UNIX domain socket of
2058 type @c SOCK_STREAM to an endpoint expecting type @c SOCK_DGRAM)
2059 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
2061 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
2062 @throws randolf::rex::xEACCES Write permission or search permission denied
2063 on a component of the path prefix (@c AF_UNIX family)
2064 @throws randolf::rex::xEAGAIN Connection cannot be completed immediately on
2065 nonblocking socket (@c AF_UNIX family)
2067 @returns The same rsocket object so as to facilitate stacking
2068 @see tls_do_handshake()
2071 *///=========================================================================
2073 /// IPv4 address, IPv6 address, hostname, or UNIX domain sockets path
2074 const std::string address,
2075 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
2076 const int port = 0) {
2077 if (__debug) debug("connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2079 + " (port=" + std::to_string(port) + ")"
2080 + ", " + std::to_string(__socket_addr_size)
2083 __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
2084 __rc_check(::connect(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size), 0, rex::rex::REX_FLAGS::REX_ALT_EAGAIN);
2086// __rc_check(::connect(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
2087 __socket_connected = true;
2089 // --------------------------------------------------------------------------
2091 // --------------------------------------------------------------------------
2093 if (__debug) debug("tls_connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2095 __rc_check_tls(SSL_connect(__tls_fd));
2096 } // -x- if __tls -x-
2099 }; // -x- rsocket* connect -x-
2101 /*======================================================================*//**
2103 Find out whether debug mode is enabled.
2106 This method is threadsafe.
2107 @returns TRUE = enabled
2108 @returns FALSE = not enabled
2110 *///=========================================================================
2111 const bool debug() noexcept { return __debug; };
2113 /*======================================================================*//**
2115 Debug mode. When debug mode is enabled, output is sent to stderr by default,
2116 unless a second parameter specifies a different file handle (e.g., stdout, or
2119 debug(...) returns -1 if fd can't be written to (errno will be set in
2120 accordance with std::fprintf's behaviour since std::fprintf is used for
2121 writing debug output). Normally we'd throw an exception for such errors, but
2122 with debug is a special case because debugging needs to be as quick and
2123 convenient as possible for developers.
2125 debug(...) returns -2 if writing to stderr failed when attempting to announce
2126 that fd can't be written to (as described above).
2128 debug(...) returns -3 if some other error occurs (e.g., internal formatting
2129 error {which should not happen} or memory allocation failed, in which case
2130 there's a more serious problem that will be causing other problems elsewhere
2133 Developers may add their own debug messages by using debug(std::string),
2134 which will only be written out if debug mode is enabled. This same method is
2135 used internally, so messages will be indistinguishable from developer's
2139 This method is thread-safe.
2140 @returns 0 = success
2141 @returns -1 = error writing to stream (errno will be set accordingly)
2142 @returns -2 = error writing to stderr (errno will be set accordingly)
2143 @returns -3 = error (errno will be set accordingly)
2145 *///=========================================================================
2147 /// TRUE = enable debug mode@n
2148 /// FALSE = disable debug mode (does not close any file handles)
2149 const bool debug_flag,
2150 /// File descriptor/handle to use for debug output
2151 std::FILE* fd = stderr) noexcept {
2153 int rc = debug_fd(fd); // Attempt to change debug handle
2154 if (rc != 0) return rc; // Return error id new debug handle failed
2155 time_t tm = std::time(nullptr);
2156 std::string dt(27, 0);
2157 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2159 if (debug_flag || __debug) std::fprintf(__debug_fd,
2160 "[%s] %s: Debug mode is %s\n",
2162 __debug_prefix.c_str(),
2163 debug_flag ? "ON" : "OFF");
2164 } catch (const std::system_error& e) { // Error writing to fd
2166 std::fprintf(stderr,
2167 "[%s] %s: error writing to stream\n",
2169 __debug_prefix.c_str());
2170 } catch (const std::exception& e) {
2174 } catch (const std::exception& e) {
2177 __debug = debug_flag; // Save debug flag
2179 return 0; // Indicate success (no errors)
2180 }; // -x- int debug -x-
2182 /*======================================================================*//**
2184 Send the specified message as debug output (as long as debug mode is enabled;
2185 if disabled, no debug output will be sent).
2186 @returns 0 = success
2187 @returns -1 = error writing to stream (errno will be set accordingly)
2188 @returns -2 = error writing to stderr (errno will be set accordingly)
2189 @returns -3 = error (errno will be set accordingly)
2191 *///=========================================================================
2193 /// Debug message as an ASCIIZ string
2194 const char* msg) noexcept {
2195 return __debug ? __debug_out(std::string(msg)) : 0;
2196 }; // -x- int debug -x-
2198 /*======================================================================*//**
2199 @copydoc debug(const char*)
2201 *///=========================================================================
2203 /// Debug message as an std::string object
2204 const std::string msg) noexcept {
2205 return __debug ? __debug_out(msg) : 0;
2206 }; // -x- int debug -x-
2208 /*======================================================================*//**
2210 Find out which file descriptor/handle is used for debug output.
2211 @returns file handle/descriptor currently used for debug output
2212 *///=========================================================================
2213 const std::FILE* debug_fd() noexcept { return __debug_fd; }; // Get debug-output file handle
2215 /*======================================================================*//**
2217 Specify a different file descriptor/handle to use for debug output
2218 @returns 0 = success
2219 @returns -1 = error writing to stream (errno will be set accordingly)
2220 @returns -2 = error writing to stderr (errno will be set accordingly)
2221 @returns -3 = error (errno will be set accordingly)
2222 *///=========================================================================
2224 /// File descriptor/handle to use for debug output
2225 std::FILE* fd) noexcept { // Set debug-output file handle
2226 if (__debug_fd != fd) {
2227 debug("Current debug-output file handle unset"); // For this message, we only want to send this out if debug mode is enabled
2228 __debug_fd = fd; // Save new debug-output file handle
2229 return debug("New debug-output file handle set"); // For this message, we only want to send this out if debug mode is enabled
2231 return 0; // Indicate success (no errors)
2232 }; // -x- int debug_fd -x-
2234 /*======================================================================*//**
2236 Find out what the current prefix is set to that's used in debug output.
2238 This may be set at any time, including before or after enabling or disabling
2240 @returns debug prefix string
2242 *///=========================================================================
2243 const std::string debug_prefix() noexcept { return __debug_prefix; }; // -x- std::string debug_prefix -x-
2245 /*======================================================================*//**
2247 Change the prefix used in debug output.
2248 @returns The same rsocket object so as to facilitate stacking
2250 *///=========================================================================
2251 rsocket* debug_prefix(
2252 /// New debug prefix string
2253 const std::string prefix) noexcept {
2254 __debug_prefix = prefix;
2256 }; // -x- std::string debug_prefix -x-
2259 /*----------------------------------------------------------------------*//**
2261 Internal debug-output function. This doesn't check __debug flag because it's
2262 expected that the normal debug() methods are taking care of this.
2263 @returns 0 = success
2264 @returns -1 = error writing to stream (errno will be set accordingly)
2265 @returns -2 = error writing to stderr (errno will be set accordingly)
2266 @returns -3 = error (errno will be set accordingly)
2267 *///-------------------------------------------------------------------------
2268 const int __debug_out(
2269 /// Debugging message
2270 const std::string msg) noexcept {
2272 time_t tm = std::time(nullptr);
2273 std::string dt(27, 0);
2274 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2276 std::fprintf(__debug_fd,
2279 __debug_prefix.c_str(),
2281 } catch (const std::system_error& e) { // Error writing to fd
2283 std::fprintf(stderr,
2284 "[%s] %s: error writing to stream\n",
2286 __debug_prefix.c_str());
2287 } catch (const std::exception& e) {
2291 } catch (const std::exception& e) {
2295 return 0; // Indicate success (no errors)
2296 }; // -x- int __debug_out -x-
2298 // --------------------------------------------------------------------------
2299 // These are specialized internal debug output methods for presenting socket
2300 // options when they are get or set. These methods are marked as "private"
2301 // because they are really aren't useful outside of internal-use contexts.
2304 // Some of these methods are thread-safe.
2306 // These methods are thread-safe when they don't reference structures (e.g.,
2307 // integer {int}, unsigned integer {u_int}, and unsigned character {u_char}).
2309 // These methods are not necessarily thread-safe if they reference structures
2310 // unless those structures are defined with std::atomic, or they were
2311 // constructed on-the-fly as anonymous parameters.
2312 // --------------------------------------------------------------------------
2313 const int __debug_sockopt(
2314 /// Name of function or method
2315 const std::string func,
2316 /// The level at which the option resides; typically @c SOL_SOCKET
2318 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2320 /// The structure that this socket option will be set to
2322 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2323 + ", " + std::to_string(level)
2324 + ", " + std::to_string(option)
2325 + ", " + std::to_string(value)
2326 + ", size=" + std::to_string(sizeof(value))
2328 }; // -x- int __debug_sockopt -x-
2329 const int __debug_sockopt(
2330 /// Name of function or method
2331 const std::string func,
2332 /// The level at which the option resides; typically @c SOL_SOCKET
2334 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2336 /// The structure that this socket option will be set to
2337 const u_int value) {
2338 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2339 + ", " + std::to_string(level)
2340 + ", " + std::to_string(option)
2341 + ", u_int{" + std::to_string(value) + "}"
2342 + ", size=" + std::to_string(sizeof(value))
2344 }; // -x- int __debug_sockopt -x-
2345 const int __debug_sockopt(
2346 /// Name of function or method
2347 const std::string func,
2348 /// The level at which the option resides; typically @c SOL_SOCKET
2350 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2352 /// The structure that this socket option will be set to
2353 const u_char value) {
2354 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2355 + ", " + std::to_string(level)
2356 + ", " + std::to_string(option)
2357 + ", u_char{" + std::to_string(value) + "}"
2358 + ", size=" + std::to_string(sizeof(value))
2360 }; // -x- int __debug_sockopt -x-
2361 const int __debug_sockopt(
2362 /// Name of function or method
2363 const std::string func,
2364 /// The level at which the option resides; typically @c SOL_SOCKET
2366 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2368 /// The structure that this socket option will be set to
2369 const linger* value) {
2370 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2371 + ", " + std::to_string(level)
2372 + ", " + std::to_string(option)
2373 + ", linger{l_onoff=" + std::to_string(value->l_onoff)
2374 + ", l_linger=" + std::to_string(value->l_linger) + "}"
2375 + ", " + std::to_string(sizeof(value))
2377 }; // -x- int __debug_sockopt -x-
2378 const int __debug_sockopt(
2379 /// Name of function or method
2380 const std::string func,
2381 /// The level at which the option resides; typically @c SOL_SOCKET
2383 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2385 /// The structure that this socket option will be set to
2386 const timeval* value) {
2387 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2388 + ", " + std::to_string(level)
2389 + ", " + std::to_string(option)
2390 + ", timeval{tv_sec=" + std::to_string(value->tv_sec)
2391 + ", tv_usec=" + std::to_string(value->tv_usec) + "}"
2392 + ", " + std::to_string(sizeof(value))
2394 }; // -x- int __debug_sockopt -x-
2395 const int __debug_sockopt(
2396 /// Name of function or method
2397 const std::string func,
2398 /// The level at which the option resides; typically @c SOL_SOCKET
2400 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2402 /// The structure that this socket option will be set to
2403 const in_addr* value) {
2404 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2405 + ", " + std::to_string(level)
2406 + ", " + std::to_string(option)
2407 + ", in_addr{" + std::to_string(value->s_addr) + "}"
2408 + ", " + std::to_string(sizeof(value))
2410 }; // -x- int __debug_sockopt -x-
2411 const int __debug_sockopt(
2412 /// Name of function or method
2413 const std::string func,
2414 /// The level at which the option resides; typically @c SOL_SOCKET
2416 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2418 /// The structure that this socket option will be set to
2419 const ip_mreq* value) {
2420 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2421 + ", " + std::to_string(level)
2422 + ", " + std::to_string(option)
2423 + ", ip_mreq{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2424 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2425 + ", " + std::to_string(sizeof(value))
2427 }; // -x- int __debug_sockopt -x-
2428 const int __debug_sockopt(
2429 /// Name of function or method
2430 const std::string func,
2431 /// The level at which the option resides; typically @c SOL_SOCKET
2433 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2435 /// The structure that this socket option will be set to
2436 const ip_mreq_source* value) {
2437 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2438 + ", " + std::to_string(level)
2439 + ", " + std::to_string(option)
2440 + ", ip_mreq_source{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2441 + ", source=" + std::to_string(value->imr_sourceaddr.s_addr)
2442 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2443 + ", " + std::to_string(sizeof(value))
2445 }; // -x- int __debug_sockopt -x-
2446 const int __debug_sockopt(
2447 /// Name of function or method
2448 const std::string func,
2449 /// The level at which the option resides; typically @c SOL_SOCKET
2451 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2453 /// The structure that this socket option will be set to
2454 const icmp6_filter* value) {
2455 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2456 + ", " + std::to_string(level)
2457 + ", " + std::to_string(option)
2458 + ", icmp6_filter{[0]=" + std::to_string(value->icmp6_filt[0])
2459 + ", [1]=" + std::to_string(value->icmp6_filt[1])
2460 + ", [2]=" + std::to_string(value->icmp6_filt[2])
2461 + ", [3]=" + std::to_string(value->icmp6_filt[3])
2462 + ", [4]=" + std::to_string(value->icmp6_filt[4])
2463 + ", [5]=" + std::to_string(value->icmp6_filt[5])
2464 + ", [6]=" + std::to_string(value->icmp6_filt[6])
2465 + ", [7]=" + std::to_string(value->icmp6_filt[7]) + "}"
2466 + ", " + std::to_string(sizeof(value))
2468 }; // -x- int __debug_sockopt -x-
2469 const int __debug_sockopt(
2470 /// Name of function or method
2471 const std::string func,
2472 /// The level at which the option resides; typically @c SOL_SOCKET
2474 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2476 /// The structure that this socket option will be set to
2477 const sockaddr_in6* value) {
2478 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2479 + ", " + std::to_string(level)
2480 + ", " + std::to_string(option)
2481 + ", sockaddr_in6{family=" + std::to_string(value->sin6_family)
2482 + ", port=" + std::to_string(value->sin6_port)
2483 + ", flowinfo=" + std::to_string(value->sin6_flowinfo)
2484 + ", addr=" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[0])
2485 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[1])
2486 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[2])
2487 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[3])
2488 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[4])
2489 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[5])
2490 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[6])
2491 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[7])
2492 + ", scope_id=" + std::to_string(value->sin6_scope_id) + "}"
2493 + ", " + std::to_string(sizeof(value))
2495 }; // -x- int __debug_sockopt -x-
2496 const int __debug_sockopt(
2497 /// Name of function or method
2498 const std::string func,
2499 /// The level at which the option resides; typically @c SOL_SOCKET
2501 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2503 /// The structure that this socket option will be set to
2504 const ip6_mtuinfo* value) {
2505 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2506 + ", " + std::to_string(level)
2507 + ", " + std::to_string(option)
2508 + ", ip6_mtuinfo{addr=" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[0])
2509 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[1])
2510 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[2])
2511 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[3])
2512 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[4])
2513 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[5])
2514 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[6])
2515 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[7])
2516 + ", mtu=" + std::to_string(value->ip6m_mtu) + "}"
2517 + ", " + std::to_string(sizeof(value))
2519 }; // -x- int __debug_sockopt -x-
2520 const int __debug_sockopt(
2521 /// Name of function or method
2522 const std::string func,
2523 /// The level at which the option resides; typically @c SOL_SOCKET
2525 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2527 /// The structure that this socket option will be set to
2528 const ipv6_mreq* value) {
2529 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2530 + ", " + std::to_string(level)
2531 + ", " + std::to_string(option)
2532 + ", ipv6_mreq{addr=" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[0])
2533 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[1])
2534 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[2])
2535 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[3])
2536 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[4])
2537 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[5])
2538 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[6])
2539 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[7])
2540 + ", iface=" + std::to_string(value->ipv6mr_interface) + "}"
2541 + ", " + std::to_string(sizeof(value))
2543 }; // -x- int __debug_sockopt -x-
2544 const int __debug_sockopt(
2545 /// Name of function or method
2546 const std::string func,
2547 /// The level at which the option resides; typically @c SOL_SOCKET
2549 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2551 /// The structure that this socket option will be set to
2552 const group_req* value) {
2553 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2554 + ", " + std::to_string(level)
2555 + ", " + std::to_string(option)
2556 + ", group_req{iface=" + std::to_string(value->gr_interface)
2558 + ", " + std::to_string(sizeof(value))
2560 }; // -x- int __debug_sockopt -x-
2561 const int __debug_sockopt(
2562 /// Name of function or method
2563 const std::string func,
2564 /// The level at which the option resides; typically @c SOL_SOCKET
2566 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2568 /// The structure that this socket option will be set to
2569 const group_source_req* value) {
2570 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2571 + ", " + std::to_string(level)
2572 + ", " + std::to_string(option)
2573 + ", group_source_req{iface=" + std::to_string(value->gsr_interface)
2574 + ", <group_source_req>}"
2575 + ", " + std::to_string(sizeof(value))
2577 }; // -x- int __debug_sockopt -x-
2578 const int __debug_sockopt_other(
2579 /// Name of function or method
2580 const std::string func,
2581 /// The level at which the option resides; typically @c SOL_SOCKET
2583 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2585 /// The structure that this socket option will be set to
2586 const socklen_t size) {
2587 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2588 + ", " + std::to_string(level)
2589 + ", " + std::to_string(option)
2591 + ", " + std::to_string(size)
2593 }; // -x- int __debug_sockopt_other -x-
2596 /*======================================================================*//**
2598 Discards the specified number of 8-bit bytes efficiently, and without closing
2599 the stream, and without consuming excessive quantities of memory.
2600 @exception randolf::rex::xERANGE An invalid value was specified for either
2601 the @c nbytes or @c memory_size parameter (e.g., a negative value,
2602 except for -1 with the @c nbytes parameter)
2603 @returns Number of bytes that were successfully discarded
2607 *///=========================================================================
2609 /// Number of bytes to discard@n
2610 /// 0 = use internal @ref buffer_size() @n
2611 /// -1 = all remaining data waiting to be received (in other words, this
2612 /// method will repeatedly consume all data until encountering @ref eos)
2615 /// MSG_PEEK (ignored, to prevent an endless loop)@n
2618 /// MSG_CMSG_CLOEXEC
2619 int posix_flags = 0,
2620 /// Maximum internal read size@n
2621 /// 0 = default to @ref buffer_size (when @c nbytes is larger than this size,
2622 /// multiple reads into this buffer will be utilized behind-the-scenes to
2623 /// consume the quantity of data specified by the @c nbytes parameter)
2624 const size_t memory_size = 0) {
2625 int buf_size = memory_size != 0 ? memory_size : __buffer_size;
2626 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2627 + ", " + std::to_string(nbytes)
2628 + ", " + std::to_string(posix_flags)
2629 + ", " + std::to_string(buf_size)
2632 // --------------------------------------------------------------------------
2634 // --------------------------------------------------------------------------
2635 if (nbytes == 0) nbytes = __buffer_size;
2636 else if (nbytes < -1) throw randolf::rex::xERANGE( "nbytes parameter of " + std::to_string(nbytes) + " is below -1");
2637 if (memory_size < 0) throw randolf::rex::xERANGE("memory_size parameter of " + std::to_string(memory_size) + " is below 0");
2639 // --------------------------------------------------------------------------
2640 // The MSG_PEEK flag is incompatible with this method, so we're turning it
2641 // off to prevent an endless loop. We're providing a debug message for this
2642 // in case someone's trying to track something down where they're using the
2643 // MSG_PEEK flag with a misunderstanding of the purpose of this method.
2644 // --------------------------------------------------------------------------
2645 if (posix_flags & MSG_PEEK) {
2646 posix_flags &= (~MSG_PEEK); // Remove MSG_PEEK flag if it was enabled
2647 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2648 + " ignoring incompatible MSG_PEEK flag"
2650 } // -x- if MSG_PEEK -x-
2652 // --------------------------------------------------------------------------
2653 // Internal variables.
2654 // --------------------------------------------------------------------------
2655 int discarded = 0; // Total number of bytes discarded
2656 std::vector<char> buf(buf_size);
2657 buf.reserve(buf_size); // Pre-allocate underlying array to prevent memory corruption when __recv writes directly into it
2659 // --------------------------------------------------------------------------
2660 // Discard loop. We are intentionally handling nbytes in two different ways
2661 // hear with a little bit more code to benefit from the trade-off of faster
2662 // overall performance.
2663 // --------------------------------------------------------------------------
2666 // --------------------------------------------------------------------------
2667 // Discard all remaining data waiting to be received.
2668 // --------------------------------------------------------------------------
2672 n = __recv(buf.data(), buf_size, posix_flags);
2674 }; // -x- while n -x-
2676 // --------------------------------------------------------------------------
2677 // Discard specific amount of data waiting to be received.
2678 // --------------------------------------------------------------------------
2681 int inbytes = nbytes;
2682 while (inbytes > 0 && !eos()) {
2683 int n = __recv(buf.data(), std::min(inbytes, buf_size), posix_flags);
2686 } // -x- while nbytes -x-
2687 } // -x- if nbytes -x-
2689 // --------------------------------------------------------------------------
2690 // Ignore exception so that we can simply indicate how many bytes were
2691 // successfully received and discarded.
2692 // --------------------------------------------------------------------------
2693 } catch(const rex::xALL e) {}
2695 // --------------------------------------------------------------------------
2696 // Erase all data to prevent data from being unexpectedly leaked to any other
2697 // memory allocations later that use some or all of the same memory.
2698 // --------------------------------------------------------------------------
2699 for (int i = nbytes <= 0 ? buf_size : std::min(nbytes, buf_size); i > 0; i--) {
2704 }; // -x- int discard -x-
2706 /*======================================================================*//**
2708 Discards the specified number of 8-bit bytes efficiently, but stops upon
2709 encountering an EoL sequence (which will also be discarded; to find out which
2710 EoL sequence was consumed, use the @ref eol_consumed_seq method which will
2711 also return an empty string if no EoL sequence was consumed), and without
2712 closing the stream, and without consuming excessive quantities of memory.
2714 This method is particularly useful for discarding lines that are too long,
2715 which, for example, makes it possible to ignore lines that are too long (and
2716 optionally present or log an error) and continue reading the next line(s)
2719 @exception randolf::rex::xERANGE An invalid value was specified for either
2720 the @c nbytes or @c memory_size parameter (e.g., a negative value,
2721 except for -1 with the @c nbytes parameter)
2722 @returns Number of bytes that were successfully discarded
2726 *///=========================================================================
2728 /// Number of bytes to discard@n
2729 /// 0 = use internal @ref buffer_size() @n
2730 /// -1 = all remaining data waiting to be received (in other words, this
2731 /// method will repeatedly consume all data until encountering @ref eos)
2734 /// MSG_PEEK (ignored, to prevent an endless loop)@n
2737 /// MSG_CMSG_CLOEXEC
2738 int posix_flags = 0,
2739 /// Line timeout (in seconds)@n
2740 /// 0 = no timeout (default), unless it was configured by way of the
2741 /// @ref timeout_recvline(long) method
2743 /// Maximum internal read size@n
2744 /// 0 = default to @ref buffer_size (when @c nbytes is larger than this size,
2745 /// multiple reads into this buffer will be utilized behind-the-scenes to
2746 /// consume the quantity of data specified by the @c nbytes parameter)
2747 const size_t memory_size = 0) {
2748 int buf_size = memory_size != 0 ? memory_size : __buffer_size;
2749 if (__debug) debug("discard_line(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2750 + ", " + std::to_string(nbytes)
2751 + ", " + std::to_string(posix_flags)
2752 + ", " + std::to_string(buf_size)
2755 // --------------------------------------------------------------------------
2757 // --------------------------------------------------------------------------
2758 bool infinite = nbytes == -1; // Keep track of whether nbytes was -1 before changing its value
2759 if (nbytes == 0) nbytes = __buffer_size;
2760 else if (nbytes == -1) nbytes = INT_MAX;
2761 else if (nbytes < -1) throw randolf::rex::xERANGE( "nbytes parameter of " + std::to_string(nbytes) + " is below -1");
2762 if (memory_size < 0) throw randolf::rex::xERANGE("memory_size parameter of " + std::to_string(memory_size) + " is below 0");
2764 // --------------------------------------------------------------------------
2765 // Syntax checks as part of preparation of timeout variable for faster
2766 // comparisons within line-reading loop.
2767 // --------------------------------------------------------------------------
2768 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
2769 if (timeout == 0) timeout = __recvline_timeout;
2770 timeout = timeout == 0 ? LONG_MAX : timeout + time(0);
2772 // --------------------------------------------------------------------------
2773 // The MSG_PEEK flag is incompatible with this method, so we're turning it
2774 // off to prevent an endless loop. We're providing a debug message for this
2775 // in case someone's trying to track something down where they're using the
2776 // MSG_PEEK flag with a misunderstanding of the purpose of this method.
2777 // --------------------------------------------------------------------------
2778 if (posix_flags & MSG_PEEK) {
2779 posix_flags &= (~MSG_PEEK); // Remove MSG_PEEK flag if it was enabled
2780 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2781 + " ignoring incompatible MSG_PEEK flag"
2783 } // -x- if MSG_PEEK -x-
2785 // --------------------------------------------------------------------------
2786 // Internal variables.
2787 // --------------------------------------------------------------------------
2788 int discarded = 0; // Total number of bytes discarded
2790 int buf_size_eol = buf_size + 1;
2791 buf.reserve(buf_size_eol); // Pre-allocate underlying array to prevent memory corruption when __recv writes directly into it, with +1 for straddled EoL
2792// char straddle_eol = 0;
2794 // --------------------------------------------------------------------------
2795 // Discard loop. We are intentionally handling nbytes in two different ways
2796 // hear with a little bit more code to benefit from the trade-off of faster
2797 // overall performance.
2798 // --------------------------------------------------------------------------
2801 // --------------------------------------------------------------------------
2802 // Discard all remaining data waiting to be received.
2803 // --------------------------------------------------------------------------
2807 n = __recv(buf.data(), buf_size_eol, posix_flags | MSG_PEEK);
2809 // --------------------------------------------------------------------------
2810 // Affect an actual discard of the correct amount of data. If "len" is -1,
2811 // then just discard everything because it means that we didn't encounter an
2812 // EoL sequence in this iteration.
2813 // --------------------------------------------------------------------------
2814 int len_with_eol = 0;
2815 int len = eol_index(buf, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
2816std::cout << "///// n=" << n << " len=" << len << " len_with_eol=" << len_with_eol << " buf_size_eol=" << buf_size_eol << std::endl;
2817 n = __recv(buf.data(), len > 0 ? len : n, posix_flags); // Consume data
2818std::cout << " n=" << n << std::endl;
2820 } // -x- while n -x-
2822 // --------------------------------------------------------------------------
2823 // Discard specific amount of data waiting to be received.
2824 // --------------------------------------------------------------------------
2827 int inbytes = nbytes;
2828 while (inbytes > 0 && !eos()) {
2829 n = __recv(buf.data(), std::min(inbytes + 1, buf_size_eol), posix_flags | MSG_PEEK);
2831 // --------------------------------------------------------------------------
2832 // Affect an actual discard of the correct amount of data. If "len" is -1,
2833 // then just discard everything because it means that we didn't encounter an
2834 // EoL sequence in this iteration.
2835 // --------------------------------------------------------------------------
2836 int len_with_eol = 0;
2837 int len = eol_index(buf, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
2838std::cout << "||||| n=" << n << " len=" << len << " len_with_eol=" << len_with_eol << " buf_size_eol=" << buf_size_eol << std::endl;
2839 n = __recv(buf.data(), len >= 0 ? len : n, posix_flags); // Consume data
2840std::cout << " n=" << n << std::endl;
2843 } // -x- while nbytes -x-
2844 } // -x- if nbytes -x-
2846 // --------------------------------------------------------------------------
2847 // Ignore exception so that we can simply indicate how many bytes were
2848 // successfully received and discarded.
2849 // --------------------------------------------------------------------------
2850 } catch(const rex::xALL e) {
2851std::cout << "Exception: " << e.what() << std::endl;
2854std::cout << "discarded=" << discarded << std::endl;
2856 // --------------------------------------------------------------------------
2857 // Erase all data to prevent data from being unexpectedly leaked to any other
2858 // memory allocations later that use some or all of the same memory.
2860 // Note: The +1 in the buffer size calcuation is to include a straddled EoL.
2861 // --------------------------------------------------------------------------
2862 for (int i = infinite ? buf_size : std::min(nbytes + 1, buf_size_eol); i > 0; i--) {
2867 }; // -x- int discard_line -x-
2869 /*======================================================================*//**
2871 Find out what the current EoL (End of Line) sequence is set to.
2873 To send an EoL sequence do not use `send(r.eol())` because it may not be
2874 initialized yet and the endpoint you're sending to may seem unresponsive or
2875 other unexpected behaviour may occur.@n
2877 To send an EoL sequence properly, use @ref sendline(); although specifying no
2878 parameters is more efficient than specifying an empty string (@c ""), the
2879 specialized @ref send_eol() method is the most efficient option for sending
2880 an EoL sequence separately.
2882 An empty EoL sequence means that CRLF will be sent by @ref sendline(), and
2883 that @ref recvline(), and @ref recv_rline() will automatically detect from
2884 one of @c CR, @c CRLF, @c LF, and @c LFCR.
2885 @returns Current EoL sequence
2895 *///=========================================================================
2896 const std::string eol() noexcept { return __eol; }; // -x- std::string eol -x-
2898 /*======================================================================*//**
2900 Set EoL (End of Line) sequence. This sequence is used by recvline(),
2901 recv_rline(), sendline(), and related functions, and it defaults to an empty
2902 string which results in the EoL sequence being detected automatically
2905 - @c "" (empty string) = automatically detect on-the-fly (default)
2906 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
2907 - @c \\r (CR) = Carriage Return (typical for MacOS)
2908 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
2911 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
2912 IMAP4, FINGER, NICNAME/WHOIS, etc.).
2913 @returns The same rsocket object so as to facilitate stacking
2923 *///=========================================================================
2925 /// EoL sequence as an ASCIIZ string
2926 const char* eol) noexcept {
2927 __eol = std::string(eol);
2928 __eol_out = __eol.empty() ? __CRLF : __eol;
2930 }; // -x- rsocket* eol -x-
2932 /*======================================================================*//**
2934 Set EoL (End of Line) sequence. This sequence is used by recvline(),
2935 recv_rline(), sendline(), and related functions, and it defaults to an empty
2936 string which results in the EoL sequence being detected automatically
2939 - @c "" (empty string) = automatically detect on-the-fly (default)
2940 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
2941 - @c \\r (CR) = Carriage Return (typical for MacOS)
2942 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
2945 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
2946 IMAP4, FINGER, NICNAME/WHOIS, etc.).
2947 @returns The same rsocket object so as to facilitate stacking
2956 *///=========================================================================
2958 /// EoL sequence as an std::string object
2959 const std::string eol) noexcept {
2962 }; // -x- rsocket* eol -x-
2964 /*======================================================================*//**
2966 Configure EoL adoption policy for the @ref recvline() and @ref recv_rline()
2967 methods. By default, @ref rsocket is configured with the EoL adoption policy
2968 enabled alongside an empty @ref eol() sequence, which results in the default
2969 operation being that the EoL sequence automatically gets detected and updated
2970 internally upon the first use of either the @ref recvline() or
2971 @ref recv_rline method.
2973 The EoL adoption policy is only effective when the @ref eol() sequence is not
2974 defined (which is indicated by an empty EoL sequence string).
2976 The EoL sequence is updated only when the EoL sequence string is empty, and
2977 when this EoL adoption policy is enabled.
2978 @returns The same rsocket object so as to facilitate stacking
2981 @see is_eol_adoption
2987 *///=========================================================================
2988 rsocket* eol_adoption(
2989 /// TRUE = enable EoL adoption (default)@n
2990 /// FALSE = disable EoL adoption
2991 const bool flag) noexcept {
2992 __eol_adoption = flag;
2994 }; // -x- rsocket* eol_adoption -x-
2996 /*======================================================================*//**
2998 Returns a String containing the EoL character sequence that was consumed by
2999 the most recent successful call to the @ref recvline() or @ref recv_rline
3000 method ("successful" in this context means that the received line was
3001 terminated by a valid EoL character sequence; otherwise the
3002 previous/unmodified value is returned).
3004 This method must not be used to determine whether the @ref recvline() method
3005 successfully consumed an EoL character sequence. The reason for this is
3006 that @ref recvline() doesn't update this string when it doesn't consume an
3007 EoL character sequence, hence interpreting its results can (and likely will)
3008 lead to false positives.
3009 @returns EoL character sequence
3015 *///=========================================================================
3016 const std::string eol_consumed_seq() noexcept { return __eol_consumed_seq; }; // -x- std::string eol_consumed_seq -x-
3018 /*======================================================================*//**
3020 Configure EoL substitution policy for the @ref printf(), @ref printfline(),
3021 @ref vprintf(), and @ref vprintfline() methods. By default, @ref rsocket
3022 substitutes printf's @c \\n sequence with the EoL sequence (if defined), but
3023 this method can be used to disable this behaviour.
3025 The @c \\n sequence used in the printf format string normally coincides with
3026 the local Operating System's newline standard, which is very likely different
3027 from the @ref rsocket endpoint's newline standard, and/or the newline
3028 standard of the protocol being implemented. This policy setting makes it
3029 possible to control whether to use the configured EoL sequence when sending a
3030 formatted string to the endpoint.
3031 @returns The same rsocket object so as to facilitate stacking
3033 @see is_eol_fix_printf
3039 *///=========================================================================
3040 rsocket* eol_fix_printf(
3041 /// TRUE = enable EoL substitution (default)@n
3042 /// FALSE = disable EoL substitution
3043 const bool flag) noexcept {
3044 __eol_fix_printf = flag;
3046 }; // -x- rsocket* eol_fix_printf -x-
3048 /*======================================================================*//**
3050 Finds the first instance of the EoL sequence and returns its offset (which is
3051 effectively the same as the size of the text, not including the characters
3052 that the EoL sequence is comprised of).
3055 This method is specialized primarily for internal use by the @ref recvline()
3056 and @ref recv_rline() methods, but is made available here in case there's a
3057 need to check in-memory text using this rsocket's EoL detection policy.
3058 @returns Size of EoL sequence
3059 @returns -1 if EoL sequence wasn't found
3063 *///=========================================================================
3064 const int eol_index(
3065 /// Buffer that probably contains at least one EoL sequence
3066 const std::string buffer,
3067 /// Size of string with EoL character sequence included (will be updated by this method)
3068 int* with_eol_size) noexcept {
3070 // --------------------------------------------------------------------------
3071 // An EoL character sequence was specified, a simple search will suffice.
3072 // --------------------------------------------------------------------------
3073 if (!__eol.empty()) {
3074//std::cout << "!__eol.empty() ------------------------------ " << randolf::rtools::to_hex(__eol) << std::endl;
3075 int pos = buffer.find(__eol);
3077 *with_eol_size = pos + __eol.size(); // Update size of EoL sequence
3078//std::cout << "------0------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3081//std::cout << "------1------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3082 if (!__eol_adoption) return pos; // Return the -1 error if we're not adopting different EoF sequences on-the-fly
3083 } // -x- if !__eol.empty() -x-
3084//std::cout << "__eol.empty() ------------------------------ String is empty" << std::endl;
3086 // --------------------------------------------------------------------------
3087 // Automatic detection of EoL sequence, so a more flexible approach will be
3090 // Search for all four possible EoL sequences (as indicated by an empty EoL
3091 // character sequence string):
3093 // CRLF: Common for text lines with internet protocols such as SMTP, POP3,
3094 // IMAP4, HTTP, FINGER, WHOIS, etc. Also: DOS and OS/2 EoL sequence.
3096 // CR: MacOS EoL character.
3098 // LF: UNIX/Linux EoL character.
3100 // LFCR: Extremely rare, but I've encountered multiple instances where the
3101 // intended CRLF was reversed to LFCR, possibly due to a programming
3102 // error or an artifact of inappropriate/unintended endian conversion.
3103 // --------------------------------------------------------------------------
3104 int pos = buffer.find(__CR); // CR or CRLF
3105//std::cout << "------2------ <CR> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3106 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)
3107 *with_eol_size = pos;// + 1;
3108// *with_eol_size = 1;
3109//std::cout << "------3------ <!CR> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3111 } else if (pos >= 0) { // EoL sequence found
3112 *with_eol_size = pos + (buffer[pos + 1] == __LF ? 2 : 1); // 2=CRLF, 1=CR
3113// *with_eol_size = buffer[pos + 1] == __LF ? 2 : 1; // 2=CRLF, 1=CR
3114 if (__eol_adoption) {
3115 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
3116 __eol.resize(*with_eol_size - pos); // Truncate extra characters
3117 } // -x- if __eol_adoption -x-
3118//std::cout << "------4------ <CR/CRLF-adopted> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3119 return pos; // Either way, we're done
3122 pos = buffer.find(__LF); // LF or LFCR
3123 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)
3124 *with_eol_size = pos;// + 1;
3125// *with_eol_size = 1;
3126//std::cout << "------5------ <LF> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3128 } else if (pos >= 0) { // EoL sequence found
3129 *with_eol_size = pos + (buffer[pos + 1] == __CR ? 2 : 1); // 2=LFCR, 1=LF
3130// *with_eol_size = buffer[pos + 1] == __CR ? 2 : 1; // 2=LFCR, 1=LF
3131 if (__eol_adoption) {
3132 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
3133 __eol.resize(*with_eol_size - pos); // Truncate extra characters
3134 } // -x- if __eol_adoption -x-
3135//std::cout << "------6------ <LF/LFCR-adopted> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3136 return pos; // Either way, we're done
3139//std::cout << "------7------ <LF> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3141 }; // -x- int eol_index -x-
3143 /*======================================================================*//**
3145 Find out if the stream is at its end and that this @ref rsocket's internal
3146 buffer (if one had been set up by the @ref recvline() method) is empty. This
3147 doesn't necessarily mean that the stream is closed; but rather that the
3148 endpoint just hasn't sent any more data (yet).
3150 If the stream isn't open, then this method will always return @c true to
3151 implicitly indicate that there's no data pending to be received.
3154 You can optionally specify a timeout with the timeout parameter, which will
3155 cause this method to wait for the specified period of time for pending data.
3157 It's better (more efficient) to use this method instead of the @c MSG_PEEK
3158 flag (with the various @c recv methods) to determine whether any data is
3159 waiting on the stream (e.g., data that's received by the sockets, but not by
3160 any @c recv methods yet) because this method is specialized in handling this
3161 particular condition and responds with an easy-to-use boolean flag.
3164 EoS is an acronym for: End of Stream
3166 @throws randolf::rex::xEBADF The underlying socket is not open
3167 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3168 part of the user address space
3169 @throws randolf::rex::xEINTR Interrupted by a signal
3170 @throws randolf::rex::xENOMEM Insufficient memory
3171 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3172 doesn't refer to a socket
3173 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
3174 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
3175 is a highly improbable chance that a timeout could still occur if the
3176 data is read by another thread before the `recv(..., MSG_PEEK)` call)
3177 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
3180 @returns TRUE = End of Stream (no buffered data, the stream @ref is_closed(),
3181 or there's no pending data, checked in this order)
3182 @returns FALSE = Pending data available
3187 *///=========================================================================
3189 /// Number of milliseconds to wait
3190 const int timeout = 0) {
3192 // --------------------------------------------------------------------------
3193 // Check internal buffer first, if there is one, and return immediately if
3194 // the buffer is not empty.
3195 // --------------------------------------------------------------------------
3196 if (__buffer != nullptr && !__buffer->empty()) return false;
3198 // --------------------------------------------------------------------------
3199 // If socket is closed, then just indicate EoS to be done with it. No need
3200 // to fall through to timeouts as this is not applicable.
3201 // --------------------------------------------------------------------------
3202 if (!__socket_open) return true;
3204 // --------------------------------------------------------------------------
3205 // Start by checking whether OpenSSL has pending data, and if it doesn't (or
3206 // there's an error, which is also indicated by 0) then fall through to
3207 // checking the underlying socket using ioctl's FIONREAD...
3209 // Use ioctl's FIONREAD to determine if any data is waiting to be read. If
3210 // ioctl returns non-zero, then an error occurred which always equates to
3211 // EoS (otherwise the FIONREAD operation would have been successful).
3212 // --------------------------------------------------------------------------
3213 if (__tls && SSL_has_pending(__tls_fd) == 1) return false;
3214 int n = 0; // Number of bytes waiting (default to 0 in case of ioctl error)
3215 ioctl(__socket_fd, FIONREAD, &n); // Ignore return code because "n" will be zero if there's an error
3216 if (n > 0) return false; // 0 bytes remaining means EoS
3218 // --------------------------------------------------------------------------
3219 // Timeout polling (if a timeout was specified).
3220 // --------------------------------------------------------------------------
3223 poll(POLLIN, timeout);
3224 return false; // EoS, because the "poll" method was triggered by new data
3225 } catch (const randolf::rex::xETIMEDOUT e) {
3226 return true; // EoS, because the "poll" method timed out while waiting for new data
3227 } // This is a specialized catch situation -- the ::recv function could still return an ETIMEDOUT error
3228 } // -x- if timeout -x-
3230 return true; // EoS, because new data was not detected
3231 }; // -x- bool eos -x-
3233 /*======================================================================*//**
3235 Find out what the specified IP address string's family (@c SO_DOMAIN) is.
3236 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
3237 address is not available on any local interface
3238 @throws randolf::rex::xEAI_NONAME If @c address is an empty string
3239 @returns @c AF_UNSPEC (if family couldn't be determined)
3240 @returns @c AF_INET (IPv4 address)
3241 @returns @c AF_INET6 (IPv6 address)
3242 @returns @c AF_UNIX (UNIX Domain address)
3243 @returns ...or other family as applicable
3245 *///=========================================================================
3247 /// Address, similar to @ref bind() addressing, including non-standard "if="
3248 /// variant that names a network interface
3249 const std::string address,
3250 /// Preferred family to return first (used only with interface mode where the
3251 /// network interface is specified after the "if=" prefix); the default value
3252 /// of @c AF_UNSPEC will return the first family interface found
3253 const int preferred_family = AF_UNSPEC) {
3255 // --------------------------------------------------------------------------
3256 // Simple checks first.
3257 // --------------------------------------------------------------------------
3258 if (address.size() == 0) randolf::rex::mk_exception("", EAI_NONAME);
3259 if (address.front() == '/') return AF_UNIX;
3261 // --------------------------------------------------------------------------
3262 // if=<interface-name>: Same "interface" option that we support in bind()
3263 // --------------------------------------------------------------------------
3264 if (address.starts_with("if=")) {
3265 //std::cout << "address=" << address.substr(3).c_str() << std::endl; // Debug
3266 struct ifaddrs *ifaddr; // Chained interface addresses structure
3267 if (::getifaddrs(&ifaddr) == -1) { // Error occurred, so we're done here
3268 freeifaddrs(ifaddr); // Memory management
3269 randolf::rex::mk_exception("getifaddrs() error (prequisite to searching for " + address + ")", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
3270 } // -x- if getifaddrs -x
3272 // --------------------------------------------------------------------------
3273 // Iterate through interface addresses. Each address should have a different
3274 // address family/domain, and is never AF_UNSPEC (this should never happen
3275 // because it wouldn't make sense, but if the network implementation was
3276 // broken and including an AF_UNSPEC family/domain, it would be useless for
3277 // the purposes of calling the ::socket() function so our code will just
3278 // ignore it anyway.
3279 // --------------------------------------------------------------------------
3280 int first_family = AF_UNSPEC; // We're using AF_UNSPEC (0) to indicate that there isn't a family/domain
3281 for (struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
3282 if (ifa->ifa_addr == nullptr) continue; // No address structure, so this isn't useful to us at all
3283 if (ifa->ifa_addr->sa_family == AF_UNSPEC) continue; // Skip AF_UNSPEC family/domain, as we can't use this
3284 if (address.compare(3, -1, ifa->ifa_name) != 0) continue; // Not the interface name we're looking for
3286 // --------------------------------------------------------------------------
3287 // Return current address's family if preferred_family is not specified (if
3288 // it's set to AF_UNSPEC {0}), or if it matches the current address.
3289 // --------------------------------------------------------------------------
3290 //std::cout << "if=" << ifa->ifa_name << " family=" << ifa->ifa_addr->sa_family << std::endl; // Debug
3291 if (preferred_family == AF_UNSPEC || preferred_family == ifa->ifa_addr->sa_family) {
3292 first_family = ifa->ifa_addr->sa_family; // Arbitrarily re-using first_family variable
3293 freeifaddrs(ifaddr); // Memory management
3294 return first_family; // Return current family/domain (re-used first_family variable)
3295 } // -x- if preferred_family -x-
3297 // --------------------------------------------------------------------------
3298 // Keep track of only the first_family/domain that we found; if we can't find
3299 // the preferred_family, then we'll be able to return the first one we found.
3300 // --------------------------------------------------------------------------
3301 if (first_family == AF_UNSPEC && ifa->ifa_addr->sa_family != AF_UNSPEC) first_family = ifa->ifa_addr->sa_family;
3302 //std::cout << " first_family=" << first_family << std::endl; // Debug
3303 } // -x- for *ifa -x-
3305 // --------------------------------------------------------------------------
3306 // Return first_family/domain, if there was one; otherwise, throw exception.
3307 // --------------------------------------------------------------------------
3308 freeifaddrs(ifaddr); // Memory management
3309 if (first_family != AF_UNSPEC) return first_family;
3310 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);
3311 } // -x- if /^if=/ -x-
3313 // --------------------------------------------------------------------------
3314 // Attempt to detect IPv4 or IPv6 using a simple shortcut with ::inet_pton(),
3315 // which requires some additional memory allocation (that the simple checks
3316 // from earlier don't need).
3317 // --------------------------------------------------------------------------
3318 char buf[sizeof(struct in6_addr)]; // Binary address storage
3319 if (::inet_pton(AF_INET, address.c_str(), buf) == 1) return AF_INET;
3320 if (::inet_pton(AF_INET6, address.c_str(), buf) == 1) return AF_INET6;
3322 // --------------------------------------------------------------------------
3323 // Throw xEAI_FAMILY exception because no family/domain was found.
3324 // --------------------------------------------------------------------------
3325 randolf::rex::mk_exception("No family/domain available for: " + address, EAI_FAMILY);
3326 }; // -x- int family -x-
3328 /*======================================================================*//**
3330 Get peer name returns the address of the socket as a sockaddr_storage
3333 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
3334 that aids in the prevention of resource leaks).
3336 @throws randolf::rex::xEBADF The underlying socket is not open
3337 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3338 part of the user address space
3339 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3340 valid for this socket's family (a.k.a., communication domain)
3341 @throws randolf::rex::xENOBUFS Insufficient memory
3342 @throws randolf::rex::xENOTCONN Underlying socket is not connected
3343 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3344 doesn't refer to a socket
3346 @returns sockaddr_storage structure
3349 *///=========================================================================
3350 const std::shared_ptr<sockaddr_storage> getpeername() {
3351 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3352 socklen_t len = sizeof(sockaddr_storage);
3353 __rc_check(::getpeername(__socket_fd, (struct sockaddr*)sa.get(), &len));
3355 }; // -x- std::shared_ptr<sockaddr_storage> getpeername -x-
3357 /*======================================================================*//**
3359 Get peer name returns the address of the socket as a std::string object.
3361 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3362 (e.g., because the @c family doesn't utilize or support an address
3363 {or the format isn't known}
3364 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3365 @throws randolf::rex::xEBADF The underlying socket is not open
3366 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3367 part of the user address space
3368 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3369 valid for this socket's family (a.k.a., communication domain)
3370 @throws randolf::rex::xENOBUFS Insufficient memory
3371 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3372 @throws randolf::rex::xENOTCONN Underlying socket is not connected
3373 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3374 doesn't refer to a socket
3376 @returns string representation of peer name
3378 *///=========================================================================
3379 const std::string getpeername_ntop() {
3380 sockaddr_storage sa;
3381 socklen_t len = sizeof(sockaddr_storage);
3382 __rc_check(::getpeername(__socket_fd, (sockaddr*)&sa, &len));
3383 return inet_ntop(&sa);
3384 }; // -x- std::string getpeername_ntop -x-
3386 /*======================================================================*//**
3388 Get specified "sockaddr_storage" structure's address as a "sockaddr"
3389 structure, for sockets in one of the supported families:
3393 - AF_UNIX (Domain socket path)
3394 - AF_PACKET (Ethernet node/mac. address)
3396 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3397 (e.g., because the @c family doesn't utilize or support an address
3398 {or the format isn't known}
3400 @returns pointer to sockaddr structure within provided sockaddr_storage
3402 @see mk_sockaddr_storage
3408 *///=========================================================================
3409 static sockaddr* getsockaddr(
3410 /// The @c sockaddr_storage structure wherein @c sockaddr will be referenced from
3411 const sockaddr_storage* sa) {
3412 switch (sa->ss_family) {
3413 case AF_INET: // IPv4 address
3414 return (sockaddr*)&(((struct sockaddr_in*)sa)->sin_addr);//->sin_addr);
3415 case AF_INET6: // IPv6 address
3416 return (sockaddr*)&(((struct sockaddr_in6*)sa)->sin6_addr);//->sin6_addr);
3417 case AF_UNIX: // UNIX (path) domain socket
3418 return (sockaddr*)&(((struct sockaddr_un*)sa)->sun_path);//->sun_path);
3419 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
3420 // case /*AF_LINK* /18: // Link layer interface (arp)
3422 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3423 return (sockaddr*)&(((struct sockaddr_ll*)sa)->sll_addr);//->sll_addr);
3424 } // -x- switch family -x-
3425 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY"); // Everything else
3426 }; // -x- sockaddr* getsockaddr -x-
3428 /*======================================================================*//**
3430 Get socket name returns the address of the socket as a "sockaddr_storage"
3433 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
3434 that aids in the prevention of resource leaks).
3436 @throws randolf::rex::xEBADF The underlying socket is not open
3437 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3438 part of the user address space
3439 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3440 valid for this socket's family (a.k.a., communication domain)
3441 @throws randolf::rex::xENOBUFS Insufficient memory
3442 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3443 doesn't refer to a socket
3445 @returns sockaddr_storage structure
3448 *///=========================================================================
3449 const std::shared_ptr<sockaddr_storage> getsockname() {
3450 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3451 socklen_t len = sizeof(sockaddr_storage);
3452 __rc_check(::getsockname(__socket_fd, (struct sockaddr*)sa.get(), &len));
3454 }; // -x- std::string getsockname -x-
3456 /*======================================================================*//**
3458 Get socket name returns the name of the socket as a std::string object.
3460 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3461 (e.g., because the @c family doesn't utilize or support an address
3462 {or the format isn't known}
3463 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3464 @throws randolf::rex::xEBADF The underlying socket is not open
3465 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3466 part of the user address space
3467 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3468 valid for this socket's family (a.k.a., communication domain)
3469 @throws randolf::rex::xENOBUFS Insufficient memory
3470 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3471 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3472 doesn't refer to a socket
3474 @returns string representation of socket name
3476 *///=========================================================================
3477 const std::string getsockname_ntop() {
3478 sockaddr_storage sa;
3479 socklen_t len = sizeof(sockaddr_storage);
3480 __rc_check(::getsockname(__socket_fd, (sockaddr*)&sa, &len));
3481 return inet_ntop(&sa);
3482 }; // -x- std::string getsockname_ntop -x-
3484 /*======================================================================*//**
3486 Get socket option details in the form of an integer.
3488 Most options return an integer, with the remaining options returning a
3489 pointer to a structure wrapped in a std::shared_ptr (a C++ smart pointer that
3490 aids in the prevention of resource leaks); the primitive types int, u_int,
3491 and u_char are not wrapped in C++ smart pointers because returning them by
3492 value is more efficient since allocating memory for an entire structure isn't
3496 It is up to the developer to know which return type is needed according to
3497 the socket option, otherwise an exception will likely be thrown -- in some
3498 cases where the wrong type will seem to work, this is due to the wrong type
3499 providing a minimally sufficient amount of memory for the storage of the
3500 resulting structure.
3503 The returned values/structures are not marked as "const" because they may
3504 need to be modified for unforseen purposes. Modifying the returend values or
3505 structures is fine because they are intended to be independent and are
3506 expected to have no direct impact on the rsocket's internal variables and
3509 Templates in C++ aren't used here because they don't work properly for our
3510 needs due to neccesity to handle both fundamental types and structures; it
3511 turns out that mixing these is impossible when using the same function name,
3512 so this just doesn't work as well as we'd like it to. (We may try to work on
3513 this again in the future as time permits to provide an additional method for
3514 obtaining socket options, but with the intention of never removing this
3515 current set of methods so as to ensure backward compatibility in the future.)
3517 @throws randolf::rex::xEBADF The underlying socket is not open
3518 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3519 part of the user address space
3520 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3521 valid for this socket's family (a.k.a., communication domain)
3522 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
3524 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3525 doesn't refer to a socket
3527 @returns socket option value
3529 *///=========================================================================
3531 /// The level at which the option resides; typically @c SOL_SOCKET
3533 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3536 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3537 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3539 }; // -x- int getsockopt_int -x-
3541 /*======================================================================*//**
3543 Get socket option details in the form of an unsigned integer.
3544 @copydetails getsockopt_int(const int, const int)
3545 @returns socket option value
3547 *///=========================================================================
3548 u_int getsockopt_u_int(
3549 /// The level at which the option resides; typically @c SOL_SOCKET
3551 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3554 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3555 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3557 }; // -x- u_int getsockopt_u_int -x-
3559 /*======================================================================*//**
3561 Get socket option details in the form of an unsigned character.
3562 @copydetails getsockopt_int(const int, const int)
3563 @returns socket option value
3565 *///=========================================================================
3566 u_char getsockopt_u_char(
3567 /// The level at which the option resides; typically @c SOL_SOCKET
3569 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3572 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3573 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3575 }; // -x- u_char getsockopt_u_char -x-
3577 /*======================================================================*//**
3579 Get socket option details in the form of a structure.
3580 @copydetails getsockopt_int(const int, const int);
3581 @returns socket option structure wrapped in std::shared_ptr
3583 *///=========================================================================
3584 std::shared_ptr<linger> getsockopt_linger(
3585 /// The level at which the option resides; typically @c SOL_SOCKET
3587 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3589 std::shared_ptr value = std::make_shared<linger>();
3590 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3591 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3593 }; // -x- linger getsockopt_linger -x-
3595 /*======================================================================*//**
3596 @copydoc getsockopt_linger(const int, const int)
3598 *///=========================================================================
3599 std::shared_ptr<timeval> getsockopt_timeval(
3600 /// The level at which the option resides; typically @c SOL_SOCKET
3602 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3604 std::shared_ptr value = std::make_shared<timeval>();
3605 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3606 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3608 }; // -x- timeval getsockopt_timeval -x-
3610 /*======================================================================*//**
3611 @copydoc getsockopt_linger(const int, const int)
3613 *///=========================================================================
3614 std::shared_ptr<in_addr> getsockopt_in_addr(
3615 /// The level at which the option resides; typically @c SOL_SOCKET
3617 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3619 std::shared_ptr value = std::make_shared<in_addr>();
3620 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3621 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3623 }; // -x- in_addr getsockopt_in_addr -x-
3625 /*======================================================================*//**
3626 @copydoc getsockopt_linger(const int, const int)
3628 *///=========================================================================
3629 std::shared_ptr<ip_mreq> getsockopt_ip_mreq(
3630 /// The level at which the option resides; typically @c SOL_SOCKET
3632 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3634 std::shared_ptr value = std::make_shared<ip_mreq>();
3635 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3636 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3638 }; // -x- ip_mreq getsockopt_ip_mreq -x-
3640 /*======================================================================*//**
3641 @copydoc getsockopt_linger(const int, const int)
3643 *///=========================================================================
3644 std::shared_ptr<ip_mreq_source> getsockopt_ip_mreq_source(
3645 /// The level at which the option resides; typically @c SOL_SOCKET
3647 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3649 std::shared_ptr value = std::make_shared<ip_mreq_source>();
3650 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3651 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3653 }; // -x- ip_mreq_source getsockopt_ip_mreq_source -x-
3655 /*======================================================================*//**
3656 @copydoc getsockopt_linger(const int, const int)
3658 *///=========================================================================
3659 std::shared_ptr<icmp6_filter> getsockopt_icmp6_filter(
3660 /// The level at which the option resides; typically @c SOL_SOCKET
3662 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3664 std::shared_ptr value = std::make_shared<icmp6_filter>();
3665 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3666 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3668 }; // -x- icmp6_filter getsockopt_icmp6_filter -x-
3670 /*======================================================================*//**
3671 @copydoc getsockopt_linger(const int, const int)
3673 *///=========================================================================
3674 std::shared_ptr<sockaddr_in6> getsockopt_sockaddr_in6(
3675 /// The level at which the option resides; typically @c SOL_SOCKET
3677 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3679 std::shared_ptr value = std::make_shared<sockaddr_in6>();
3680 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3681 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3683 }; // -x- sockaddr_in6 getsockopt_sockaddr_in6 -x-
3685 /*======================================================================*//**
3686 @copydoc getsockopt_linger(const int, const int)
3688 *///=========================================================================
3689 std::shared_ptr<ip6_mtuinfo> getsockopt_ip6_mtuinfo(
3690 /// The level at which the option resides; typically @c SOL_SOCKET
3692 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3694 std::shared_ptr value = std::make_shared<ip6_mtuinfo>();
3695 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3696 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3698 }; // -x- ip6_mtuinfo getsockopt_ip6_mtuinfo -x-
3700 /*======================================================================*//**
3701 @copydoc getsockopt_linger(const int, const int)
3703 *///=========================================================================
3704 std::shared_ptr<ipv6_mreq> getsockopt_ipv6_mreq(
3705 /// The level at which the option resides; typically @c SOL_SOCKET
3707 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3709 std::shared_ptr value = std::make_shared<ipv6_mreq>();
3710 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3711 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3713 }; // -x- ipv6_mreq getsockopt_ipv6_mreq -x-
3715 /*======================================================================*//**
3716 @copydoc getsockopt_linger(const int, const int)
3718 *///=========================================================================
3719 std::shared_ptr<group_req> getsockopt_group_req(
3720 /// The level at which the option resides; typically @c SOL_SOCKET
3722 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3724 std::shared_ptr value = std::make_shared<group_req>();
3725 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3726 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3728 }; // -x- group_req getsockopt_group_req -x-
3730 /*======================================================================*//**
3731 @copydoc getsockopt_linger(const int, const int)
3733 *///=========================================================================
3734 std::shared_ptr<group_source_req> getsockopt_group_source_req(
3735 /// The level at which the option resides; typically @c SOL_SOCKET
3737 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3739 std::shared_ptr value = std::make_shared<group_source_req>();
3740 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3741 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3743 }; // -x- group_source_req getsockopt_group_source_req -x-
3745 /*======================================================================*//**
3746 @copydoc getsockopt_linger(const int, const int)
3748 *///=========================================================================
3749 template<class T> std::shared_ptr<T> getsockopt_other(
3750 /// The level at which the option resides; typically @c SOL_SOCKET
3752 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3754 std::shared_ptr value = std::make_shared<T>();
3755 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3756 if (__debug) __debug_sockopt_other("getsockopt_other(socket{0x", level, option, socklen_t(sizeof(value)));
3758 }; // -x- T getsockopt_other -x-
3760 /*======================================================================*//**
3762 Get underlying socket's address as a std::string, for sockets in one of the
3767 - AF_UNIX (Domain socket path)
3768 - AF_PACKET (Ethernet node/mac. address)
3770 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3771 (e.g., because the @c family doesn't utilize or support an address
3772 {or the format isn't known}
3773 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3774 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3776 @returns string representation of underlying socket's address
3777 @see inet_ntop(sockaddr_storage*) static method
3780 *///=========================================================================
3781 const std::string inet_ntop() { return inet_ntop((sockaddr_storage*)&__socket_addr); } // -x- std::string inet_ntop -x-
3783 /*======================================================================*//**
3785 Get specified "sockaddr_storage" structure's address as a std::string, for
3786 sockets in one of the supported families:
3790 - AF_UNIX (Domain socket path)
3791 - AF_PACKET (Ethernet node/mac. address)
3793 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3794 (e.g., because the @c family doesn't utilize or support an address
3795 {or the format isn't known}
3796 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3797 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3799 @returns string representation of underlying socket's address
3800 @see inet_ntop() non-static method
3803 *///=========================================================================
3804 static const std::string inet_ntop(
3805 /// Source structure that [should] contain address data
3806 sockaddr_storage* sa) {
3808 switch (sa->ss_family) {
3809 case AF_INET: // IPv4 address
3811 char ntop[sizeof(sockaddr_in)]{0};
3812 const char* rc = ::inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), ntop, sizeof(ntop));
3813 str = std::string(ntop);
3816 case AF_INET6: // IPv6 address
3818 char ntop[sizeof(sockaddr_in6)]{0};
3819 const char* rc = ::inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), ntop, sizeof(ntop));
3820 str = std::string(ntop);
3823 case AF_UNIX: // UNIX (path) domain socket
3824 str = std::string(((struct sockaddr_un *)sa)->sun_path);
3826 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
3827 // case /*AF_LINK* /18: // Link layer interface (arp)
3829 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3830 str = to_mac(((struct sockaddr_ll *)sa)->sll_addr);
3832 default: // Everything else
3833 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY");
3834 } // -x- switch family -x-
3836 }; // -x- std::string inet_ntop -x-
3838 /*======================================================================*//**
3840 Find out whether an internal read buffer was allocated (this is most likely
3841 triggered by an attempt to read a line of text).
3843 The buffer_size() methods report on how much memory was allocated for the
3844 internal read buffer or to set its size (in bytes).
3845 @returns TRUE = an internal read buffer was allocated
3846 @returns FALSE = an internal read buffer was not allocated
3848 @see buffer_size(const size_t nbytes)
3850 *///=========================================================================
3851 const bool is_buffered() noexcept { return __buffer != nullptr; }; // -x- bool is_buffered -x-
3853 /*======================================================================*//**
3855 Find out whether the underlying socket is not open (which may not be the same
3856 as specifically "closed" since a newly instantiated empty socket begins in a
3857 "not open" state despite the underlying socket not explicitly having been
3859 @returns TRUE = not open
3860 @returns FALSE = open
3863 *///=========================================================================
3864 const bool is_closed() noexcept { return !__socket_open; }; // -x- bool is_closed -x-
3866 /*======================================================================*//**
3868 Find out whether the underlying socket is connected with/to an endpoint.
3869 @returns TRUE = open
3870 @returns FALSE = not open
3872 *///=========================================================================
3873 const bool is_connected() noexcept { return __socket_connected; }; // -x- bool is_connected -x-
3875 /*======================================================================*//**
3877 Find out whether the default byte order for this host is LSB (small endian).
3879 If you're trying to choose which endian type to use when designing a new
3880 internet protocol, then big endian is normally the better option. However,
3881 if your new protocol will only be used by hardware that all share the same
3882 endianness, then that endianness is probably the more optimal option since it
3883 will translate to an overall lesser consumption of CPU cycles by reducing or
3884 eliminating endianness conversions.
3885 @returns TRUE = LSB (little endian)
3886 @returns FALSE = MSB (big endian / network byte order)
3888 *///=========================================================================
3889 const bool is_endian_lsb() noexcept { return !__endian_is_msb; }; // -x- bool is_endian_lsb -x-
3891 /*======================================================================*//**
3893 Find out whether the default byte order for this host is MSB (big endian).
3895 Big endian is the standard known as "network byte order" that's also used in
3896 various header fields in internet packets.
3898 If you're trying to choose which endian type to use when designing a new
3899 internet protocol, then big endian is normally the better option. However,
3900 if your new protocol will only be used by hardware that all share the same
3901 endianness, then that endianness is probably the more optimal option since it
3902 will translate to an overall lesser consumption of CPU cycles by reducing or
3903 eliminating endianness conversions.
3904 @returns TRUE = MSB (big endian / network byte order)
3905 @returns FALSE = LSB (little endian)
3907 *///=========================================================================
3908 const bool is_endian_msb() noexcept { return __endian_is_msb; }; // -x- bool is_endian_msb -x-
3910 /*======================================================================*//**
3912 Find out if the EoL adoption policy is enabled for the @ref recvline() method
3913 (see the @ref eol_adoption method to find out how the dynamically-detected
3914 EoL sequence gets adopted, and under what conditions).
3915 @returns TRUE = EoL adoption is enabled
3916 @returns FALSE = EoL adoption is disabled
3924 *///=========================================================================
3925 const bool is_eol_adoption() noexcept {
3926 return __eol_adoption;
3927 }; // -x- bool is_eol_adoption -x-
3929 /*======================================================================*//**
3931 Find out if the EoL substitution policy is enabled for the @ref printf(),
3932 @ref printfline(), @ref vprintf(), and @ref vprintfline() methods.
3933 @returns TRUE = EoL substitution is enabled
3934 @returns FALSE = EoL substitution is disabled
3944 *///=========================================================================
3945 const bool is_eol_fix_printf() noexcept {
3946 return __eol_fix_printf;
3947 }; // -x- bool is_eol_fix_printf -x-
3949 /*======================================================================*//**
3951 Find out whether the underlying socket is open.
3952 @returns TRUE = open
3953 @returns FALSE = not open
3956 *///=========================================================================
3957 const bool is_open() noexcept { return __socket_open; }; // -x- bool is_open -x-
3959 /*======================================================================*//**
3961 Find out whether encrypted communications is enabled or disabled.
3962 @returns TRUE = encrypted communications is enabled
3963 @returns FALSE = encrypted communications is disabled
3964 @see tls(bool, TLS_FLAGS)
3966 *///=========================================================================
3967 const bool is_tls() noexcept { return __tls; }; // -x- bool is_tls -x-
3969 /*======================================================================*//**
3971 Find out whether TLS context is in TLS_CLIENT mode.
3972 @returns TRUE = TLS context is in TLS_CLIENT mode
3973 @returns FALSE = TLS context is in TLS_SERVER mode
3977 *///=========================================================================
3978 const bool is_tls_client_mode() noexcept { return !__tls_server_mode; }; // -x- bool is_tls_client_mode -x-
3980 /*======================================================================*//**
3982 Find out whether egress from encryption (to unencrypted mode) is allowed.
3983 @returns TRUE = egress from encrypted communications is allowed
3984 @returns FALSE = egress from encrypted communications is not allowed
3988 *///=========================================================================
3989 const bool is_tls_egress_okay() noexcept { return __tls_egress; }; // -x- bool is_tls_egress_okay -x-
3991 /*======================================================================*//**
3993 Find out whether encrypted communications is exclusive.
3994 @returns TRUE = encrypted communications is exclusive
3995 @returns FALSE = encrypted communications is not exclusive
3998 *///=========================================================================
3999 const bool is_tls_exclusive() noexcept { return __tls_exclusive; }; // -x- bool is_tls_exclusive -x-
4001 /*======================================================================*//**
4003 Find out whether ingress to encryption (from unencrypted mode) is allowed.
4004 @returns TRUE = ingress to encrypted communications is allowed
4005 @returns FALSE = ingress to encrypted communications is not allowed
4009 *///=========================================================================
4010 const bool is_tls_ingress_okay() noexcept { return __tls_ingress; }; // -x- bool is_tls_ingress_okay -x-
4012 /*======================================================================*//**
4014 Find out whether TLS context is in TLS_SERVER mode.
4015 @returns TRUE = TLS context is in TLS_SERVER mode
4016 @returns FALSE = TLS context is in TLS_CLIENT mode
4020 *///=========================================================================
4021 const bool is_tls_server_mode() noexcept { return __tls_server_mode; }; // -x- bool is_tls_server_mode -x-
4023 /*======================================================================*//**
4025 Find out whether SNI (Server Name Identifier) is enabled (configured, which
4026 implies that an internal callback function was also set up).
4027 @returns TRUE = SNI is enabled
4028 @returns FALSE = SNI is disabled
4031 *///=========================================================================
4032 const bool is_tls_sni() noexcept { return __tls_sni != nullptr; }; // -x- bool is_tls_sni -x-
4034 /*======================================================================*//**
4036 Find out whether SNI (Server Name Identifier) was matched, which means that
4037 we're using one of the supplementary TLS certificates that are included in
4038 the associated @ref rsocket_sni object as separate TLS contexts.
4040 When this method returns @c TRUE, it means the @c OpenSSL callback (which
4041 occurs during the TLS handshake process) completed the TLS handshake with one
4042 of the TLS certificates that's included in this @c rsocket's @ref rsocket_sni
4043 object instead of the primary TLS certificate assigned to this @c rsocket,
4044 and this @c rsocket is using the respective TLS context instead of the
4045 primary (default) one.
4046 @returns TRUE = SNI was matched
4047 @returns FALSE = SNI wasn't matched
4051 *///=========================================================================
4052 const bool is_tls_sni_match() noexcept { return __tls_sni_match; }; // -x- bool is_tls_sni_match -x-
4054 /*======================================================================*//**
4056 Enable listening mode for this rsocket to prepare it to accept() new inbound
4059 The backlog defaults to SOMAXCONN (4096 on Linux, and 128 on older systems),
4060 which is common on most systems. If a higher value is supplied that exceeds
4061 @c kern.somaxconn, the kernel will automatically override the backlog with
4062 the value stored in @c kern.somaxconn without generating any errors or
4065 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
4066 @throws randolf::rex::xEADDRINUSE No ephemeral ports are available for
4067 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
4068 but it really isn't)
4069 @throws randolf::rex::xEBADF The underlying socket is not open
4070 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4071 doesn't refer to a socket
4072 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
4073 @returns The same rsocket object so as to facilitate stacking
4082 *///=========================================================================
4084 /// Backlog queue size (0 = uses rsocket's default; see @ref backlog for more
4085 /// details about this); specifying a non-zero backlog also updates rocket's
4086 /// internal default (SOMAXCONN is 4096 on Linux, and 128 on older systems)
4088 if (__debug) debug("listen(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4089 + ", " + std::to_string(backlog)
4092 // --------------------------------------------------------------------------
4093 // Use rsocket's default backlog if not specified, otherwise update it
4094 // with the new value specified here.
4095 // --------------------------------------------------------------------------
4096 if (backlog = 0) backlog = __socket_backlog;
4097 else __socket_backlog = backlog;
4099 // --------------------------------------------------------------------------
4100 // Configure underlying socket to queue up to "backlog" inbound connections.
4101 // --------------------------------------------------------------------------
4102 __rc_check(::listen(__socket_fd, backlog));
4105 }; // -x- rsocket* listen -x-
4107 /*======================================================================*//**
4109 Convert an IPv4 address, IPv6 address, ethernet packet, or UNIX domain
4110 socket to a sockaddr_storage structure.
4112 If service_name is an absolute path (that begins with a "/" charcter) and the
4113 family is set to AF_UNSPEC (the default), then the resulting family will be
4117 This method utilizes the results of getaddrinfo().
4119 Other families like AF_LINK and AF_PACKET should work, but haven't been
4120 tested thoroughly. The additional support we provide for IPv4 and IPv6
4121 addresses is to copy the port number into the resulting structure (as a
4125 The resulting sockaddr_storage structure is wrapped in std::shared_ptr (a C++
4126 smart pointer that aids in the prevention of resource leaks).
4129 This method is thread-safe.
4131 @throws randolf::rex::xEAI_ADDRFAMILY If specified network host doesn't have
4132 any addresses in the specified address family
4133 @throws randolf::rex::xEAI_AGAIN Temporary failure code from DNS server (try
4135 @throws randolf::rex::xEAI_BADFLAGS Invalid flags in @c hints.ai_flags (or
4136 `hints.ai_flags` included @c AI_CANONNAME with @c nullptr as @c name)
4137 @throws randolf::rex::xEAI_FAIL Permanent failure code from DNS server
4138 @throws randolf::rex::xEAI_FAMILY The specified family is not supported
4139 @throws randolf::rex::xEAI_MEMORY Out of memory
4140 @throws randolf::rex::xEAI_NONAME If node_name is nullptr or an empty string
4141 @throws randolf::rex::xEAI_SERVICE The specified service is not available
4142 for the specified socket type
4143 @throws randolf::rex::xEAI_SOCKTYPE The specified socket type is not
4145 @throws randolf::rex::xEAI_SYSTEM Other system error (use errno to determine
4146 what the error is, then run use @ref randolf::rex::rex::mk_exception
4147 to throw the correct exception)
4149 @returns sockaddr_storage structure
4151 *///=========================================================================
4152 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
4153 /// IP address or UNIX domain socket address to convert
4154 const char* node_name,
4155 /// Port number (or service name used by some other families)
4156 const char* service_name = nullptr,
4157 /// Optional pointer to a helpful addrinfo structure
4158 const addrinfo* hints = nullptr) {
4160 // --------------------------------------------------------------------------
4161 // Initial sanity checks.
4162 // --------------------------------------------------------------------------
4163 if (node_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME); // if (node_name == nullptr && service_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME);
4165 // --------------------------------------------------------------------------
4166 // Handle any special cases.
4167 // --------------------------------------------------------------------------
4168 switch (hints == nullptr ? AF_UNSPEC : hints->ai_family) { // Default to AF_UNSPEC in the absence of hints
4170 if (node_name[0] != '/') break;
4171 // Next entry MUST be "case AF_UNIX" for this fall-through to work
4174 // For some unknown reason, clang++ can't compile this line that
4175 // g++ has absolutely no trouble with at all:
4176 // std::shared_ptr sa = std::make_shared<sockaddr_storage>(AF_UNIX);
4177 // So, after wasting more than a year trying to figure out what the
4178 // hundreds of lines of cryptic errors meant, I eventually gave up
4179 // on clang++ for being such a dumbfuck, and used this work-around:
4180 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
4181 sa->ss_family = AF_UNIX;
4182 std::strcpy(((struct sockaddr_un *)sa.get())->sun_path, node_name); // Copy path
4186 } // -x- switch hints->family -x-
4188 // --------------------------------------------------------------------------
4189 // Acquire addrinfo[] linked-list array.
4190 // --------------------------------------------------------------------------
4191 addrinfo* __addr_result; // This is temporary
4192//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;
4193 randolf::rex::mk_exception("", getaddrinfo(node_name,
4198 // --------------------------------------------------------------------------
4199 // Find first valid addrinfo[] array by trying to open a socket with it.
4200 // --------------------------------------------------------------------------
4201 for (addrinfo* ar = __addr_result; ar != nullptr; ar = ar->ai_next) {
4202//std::cout << " ... ai_family=" << ar->ai_family << " ai_socktype=" << ar->ai_socktype << " ai_flags=" << ar->ai_flags << std::endl; // *******************
4203// ***************************** TODO: Make sure this loop is working properly
4205//std::cout << " !!! ai_family=" << __addr_result->ai_family << std::endl; // *******************
4207 // --------------------------------------------------------------------------
4208 // Copy the address to "sa" and return it. The "addr" data for IPv4 and IPv6
4209 // addresses include the TCP/UDP port number (service) in first two bytes as
4210 // as an unsigned integer (u_int16), followed immediately by the IP address.
4212 // We're taking the first record only. We assume that hints{} is defined on
4213 // an as-needed basis.
4215 // Note: AF_LINK and AF_PACKET are not widely supported, but we've made an
4216 // effort to accomodate their usage.
4217 // --------------------------------------------------------------------------
4218 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
4219 switch (__addr_result->ai_family) {
4220 case AF_INET: // IPv4 address
4221 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_in));
4223 case AF_INET6: // IPv6 address
4224 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_in6));
4226 /* // We're handling this earlier as a special case: TODO: Move special handling to here
4227 case AF_UNIX: // UNIX (path) domain socket
4228 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_un));
4231 case /*AF_LINK*/18: // Link layer interface (arp)
4232 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_dl));
4234 case AF_PACKET: // Packet (ethernet) address (packet capturing)
4235 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_ll));
4237 default: // Everything else
4238 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_storage));
4239 } // -x- switch family -x-
4240 freeaddrinfo(__addr_result); // We don't need this anymore
4242 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
4244 /*======================================================================*//**
4245 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
4246 *///=========================================================================
4247 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
4248 /// IP address or UNIX domain socket address to convert
4249 const char* node_name,
4251 const u_int16_t service_name,
4252 /// Optional pointer to a helpful addrinfo structure
4253 const addrinfo* hints = nullptr) {
4254 std::string port = std::to_string(service_name);
4255 return mk_sockaddr_storage(node_name, port.c_str(), hints);
4256 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
4258 /*======================================================================*//**
4259 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
4260 *///=========================================================================
4261 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
4262 /// IP address or UNIX domain socket address to convert
4263 const std::string node_name,
4265 const u_int16_t service_name,
4266 /// Optional pointer to a helpful addrinfo structure
4267 const addrinfo* hints = nullptr) {
4268 std::string port = std::to_string(service_name);
4269 return mk_sockaddr_storage(node_name.c_str(), port.c_str(), hints);
4270 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
4272 /*======================================================================*//**
4273 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
4274 *///=========================================================================
4275 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
4276 /// IP address or UNIX domain socket address to convert
4277 const std::string node_name,
4278 /// Port number (or server name used by some other families)
4279 const std::string service_name,
4280 /// Optional pointer to a helpful addrinfo structure
4281 const addrinfo* hints = nullptr) {
4282 return mk_sockaddr_storage(node_name.c_str(), service_name.c_str(), hints);
4283 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
4285 /*======================================================================*//**
4287 Specify a name for this rsocket.
4289 This is an arbitrary name that is entirely optional, and should be regarded
4290 as similar to the naming of threads.
4291 @returns The same rsocket object so as to facilitate stacking
4292 *///=========================================================================
4294 /// Name to assign to this @c rsocket
4295 const std::string name) noexcept {
4296 // const std::lock_guard<std::mutex> lock(__name);
4299 }; // -x- rsocket* name -x-
4301 /*======================================================================*//**
4303 Find out what this rsocket's name is.
4305 The built-in SNI mechanism will overwrite this data to indicate the hostname
4306 that was specified by the TLS-encrypted endpoint that triggered an internal
4307 SNI callback -- use the @ref name_sni() method to find out which hostname is
4308 actually being used by TLS.
4309 @returns The name of this rsocket (or an empty @c std::string if this rsocket
4310 doesn't have a name)
4313 *///=========================================================================
4314 std::string name() noexcept {
4315 // const std::lock_guard<std::mutex> lock(__name);
4317 }; // -x- std::string name -x-
4319 /*======================================================================*//**
4321 Find out what this rsocket's actual TLS SNI hostname is.
4323 This is the exact - or closest-matching (in the case of wildcards) - hostname
4324 associated with an actual TLS certificate that was provided in the configured
4325 @ref rsocket_sni object.
4326 @returns The hostname associated with the TLS certificate selected by SNI
4330 *///=========================================================================
4331 std::string name_sni() noexcept {
4333 }; // -x- std::string name_sni -x-
4335 /*======================================================================*//**
4337 Get socket I/O statistics from internally-tracked socket I/O counters.
4339 The number of bytes transmitted and received is tracked internally, so that
4340 the information can be used later in logging. These statistics are available
4341 at all times, but for logging purposes it makes the most sense to copy this
4342 information after the rsocket is closed, at which time the final statistics
4343 will continue to be available until the rsocket's destructor takes over.
4346 This method is threadsafe.
4347 @returns rsocket_io wrapped in a std::shared_ptr object to help ease memory
4353 *///=========================================================================
4354 std::shared_ptr<rsocket_io> net_io() noexcept {
4355 std::shared_ptr stats = std::make_shared<rsocket_io>();
4356 stats->bytes_rx = __bytes_rx;
4357 stats->bytes_tx = __bytes_tx;
4358 stats->crypt_rx = __crypt_rx;
4359 stats->crypt_tx = __crypt_tx;
4360 stats->is_final = false;
4362 }; // -x- std::shared_ptr<rsocket_io> net_io -x-
4364 /*======================================================================*//**
4366 Get socket I/O statistics from internally-tracked socket I/O counters as a
4367 pre-formatted std::string object.
4369 The number of bytes transmitted and received is tracked internally, so that
4370 the information can be used later in logging. These statistics are available
4371 at all times, but for logging purposes it makes the most sense to copy this
4372 information after the rsocket is closed, at which time the final statistics
4373 will continue to be available until the rsocket's destructor takes over.
4376 The format string may contain any characters, with only instances of the
4377 following case-sensitive command sequences to be interpolated accordingly
4378 (invalid commands will be ignored and will remain in place, unmodified):
4380 <table cellpadding=8 cellspacing=0 border=1>
4381 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
4382 <tr><td>$$</td><td>One dollar sign ("$")</td><td>@e N/A</td></tr>
4383 <tr><td>$aR</td><td>Total (all) bytes received</td><td>bytes_rx @c + crypt_rx</td></tr>
4384 <tr><td>$aT</td><td>Total (all) bytes transmitted</td><td>bytes_tx @c + crypt_tx</td></tr>
4385 <tr><td>$bR</td><td>Unencrypted bytes received</td><td>bytes_rx</td></tr>
4386 <tr><td>$bT</td><td>Unencrypted bytes transmitted</td><td>bytes_tx</td></tr>
4387 <tr><td>$cR</td><td>Encrypted bytes recevied</td><td>crypt_rx</td></tr>
4388 <tr><td>$cT</td><td>Encrypted bytes transmitted</td><td>crypt_tx</td></tr>
4389 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
4392 Why do we use dollar signs instead of percent symbols, like other formatting
4393 functions like printf() does? So that the format string won't be in conflict
4394 with any percent-prefixed commands in printf() and similar funcions. This
4395 means that a printf() format string can be put through a first pass here to
4396 get the needed statistics interpolated into the needed positions.
4399 This method is threadsafe.
4400 @returns An interpolated format string as an std::string object.
4405 *///=========================================================================
4409 /// Length of format string (in bytes), or 0 to auto-detect length if format string is an ASCIIZ string
4411 /// Pointer to an @ref rsocket_io structure to read the statistics from (nullptr = use internal copy of current statistics)
4412 // 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)
4413 rsocket_io* addr = nullptr) noexcept {
4415 // --------------------------------------------------------------------------
4416 // Measure size of format string if an ASCIIZ string was indicated.
4417 // --------------------------------------------------------------------------
4418 if (len == 0) len = std::strlen(format);
4420 // --------------------------------------------------------------------------
4421 // Internal variables.
4422 // --------------------------------------------------------------------------
4423 rsocket_io* data = addr == nullptr ? net_io().get() : addr; // Copy current statistics so we have a snapshot instead of a set of possibly-changing statistics to work with
4424 std::string stats; // Formatted result
4425 ulong val; // Value (to be inserted)
4426 char c; // Current character
4427 std::string cmd; // Accumulated command (may be subsituted)
4428 bool flag_commas = false; // Thousands separators flag
4429 bool flag_right = false; // Flush-right flag
4431 // --------------------------------------------------------------------------
4432 // Process format string and build resulting formatted stats string.
4434 // This is designed to be fast, and I'm taking huge shortcuts here since the
4435 // commands are all the same size (except for the first one, which is only
4436 // two dollar sign characters). Processing in this loop should be quite fast
4437 // compared to using library functions to search for dollar sign characters
4438 // since data needs to be copied anyway -- why run a loop over the text twice
4439 // when can get away with doing it faster by doing it only once? This is an
4440 // optimized approach, although it probably could be even faster by not using
4441 // std::string for temporary command storage (there are other ways to do this
4442 // but I think this should suffice for now since it isn't expected to be used
4444 // --------------------------------------------------------------------------
4445 for (int i = 0; i < len; i++) {
4447 // --------------------------------------------------------------------------
4448 // First character (potentially).
4449 // --------------------------------------------------------------------------
4450 if ((c = format[i]) != '$') { // Not a dollar sign -- save it and move on
4454 cmd = c; // Start building up the command string
4456 // --------------------------------------------------------------------------
4457 // Second character: Part 1 of 2
4459 // TODO: Add support for "," commas, and "r" right-alignment.
4460 // --------------------------------------------------------------------------
4461 if (++i == len) continue; // End of format string, so we're done
4462 cmd.push_back(c = format[i]);
4463 if (c == '$') { // Consume the previous dollar sign so that $$ turns into $
4464 stats.push_back(c); // Add $ to stats string (we're only keeping one dollar sign)
4467 flag_commas = flag_right = false; // Reset these flags for a clean start
4469 // --------------------------------------------------------------------------
4471 // , = Include thousands separators (commas)
4472 // r = Flush right (leading spaces will be added)
4473 // --------------------------------------------------------------------------
4476 if (++i == len) continue; // End of format string, so we're done
4477 cmd.push_back(c = format[i]);
4479 goto net_io_flags_loop;
4480 } else if (c == 'r') {
4481 if (++i == len) continue; // End of format string, so we're done
4482 cmd.push_back(c = format[i]);
4484 goto net_io_flags_loop;
4485 } // -x- if [,r] -x-
4488 // --------------------------------------------------------------------------
4489 // Second character: Part 1 of 2
4490 // --------------------------------------------------------------------------
4491 if (c < 'a' || c > 'c') { // Not a, b, or c, so treat it as a regular character
4492 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4494 } // -x- if !~[abc] -x-
4496 // --------------------------------------------------------------------------
4498 // --------------------------------------------------------------------------
4499 if (++i == len) continue; // End of format string, so we're done
4500 cmd.push_back(c = format[i]);
4501 if (c != 'R' && c != 'T') { // Not R or T, so treat it as a regular character
4502 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4504 } // -x- if !~[RT] -x-
4506 // --------------------------------------------------------------------------
4507 // Command processing. If the command is valid, then it will be interpolated
4508 // with its expected result by replacing it with the resulting value.
4509 // --------------------------------------------------------------------------
4510 //std::cout << "[" << cmd << "]"; // Debug
4511 if (cmd.ends_with("aR")) val = data->bytes_rx + data->crypt_rx;
4512 else if (cmd.ends_with("aT")) val = data->bytes_tx + data->crypt_tx;
4513 else if (cmd.ends_with("bR")) val = data->bytes_rx ;
4514 else if (cmd.ends_with("bT")) val = data->bytes_tx ;
4515 else if (cmd.ends_with("cR")) val = data->crypt_rx;
4516 else if (cmd.ends_with("cT")) val = data->crypt_tx;
4517 else { stats.append(cmd); continue; } // This is wrong, so ignore it
4519 // --------------------------------------------------------------------------
4520 // Re-use cmd to generate formatted value.
4521 // --------------------------------------------------------------------------
4523 cmd.resize(21); // Maximum length without commas (plus character zero): 18446744073709551615
4524 cmd.resize(sprintf(cmd.data(), flag_right ? "%20lu" : "%lu", val)); // The ::sprintf function can't use cmd.c_str() here
4525 if (flag_commas) cmd = randolf::rtools::insert_commas(cmd.c_str()); // Insert commas (this makes the string longer)
4527 // --------------------------------------------------------------------------
4528 // Convert to std::string and add to the stats string.
4529 // --------------------------------------------------------------------------
4535 }; // -x- std::string net_io -x-
4537 /*======================================================================*//**
4539 Where the destructor should save final I/O statistics before this rsocket's
4540 resources are completely freed/deallocated.
4542 Developers should take care to check that the @ref rsocket_io::is_final flag
4543 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
4544 since there's no guarantee that the destructor will necessarily be executed
4545 in a timely manner (this flag is set last, after all other statistics are
4546 copied into the structure while in a mutex-locked state).
4547 @returns The same rsocket object so as to facilitate stacking
4553 *///=========================================================================
4554 rsocket* net_io_final(
4555 /// Pointer to @ref rsocket_io structure (nullptr = disabled)
4556 rsocket_io* addr) noexcept {
4557 __io_final_addr = addr;
4559 }; // -x- rsocket* net_io_final -x-
4561 /*======================================================================*//**
4563 Where the destructor should save current I/O statistics.
4565 Statistics are copied into the structure while in a mutex-locked state, but
4566 the copy itself is not an overall atomic snapshot of the I/O statistics even
4567 though each statistic is copied atomically. This means that the actual
4568 statistics could be slightly different if updates occur independently (e.g.,
4569 due to activities in a different thread), but this likely won't matter since
4570 the anticipated use for these statistics is to display or otherwise present
4571 general statistical information to a user at regular intervals.
4572 @returns The same rsocket object so as to facilitate stacking
4578 *///=========================================================================
4579 rsocket* net_io_update(
4580 /// Pointer to @ref rsocket_io structure (nullptr = do nothing)
4581 rsocket_io* addr) noexcept {
4583 // --------------------------------------------------------------------------
4584 // Copy statistics to final location. (We do this last in case there's an
4585 // issue with locking; there shouldn't be, but if there is then at least we
4586 // already we're not inadvertently hanging on to resources at this point that
4587 // need to be freed/closed anyway.)
4588 // --------------------------------------------------------------------------
4589 if (addr != nullptr) {
4591 addr->bytes_rx = __bytes_rx;
4592 addr->bytes_tx = __bytes_tx;
4593 addr->crypt_rx = __crypt_rx;
4594 addr->crypt_tx = __crypt_tx;
4595 addr->is_final = false;
4597 } // -x- if addr -x-
4600 }; // -x- rsocket* net_io_update -x-
4602 /*======================================================================*//**
4604 Return the number of bytes pending to be received, without actually receiving
4608 When using TLS, OpenSSL may not report all the pending data, but this is
4609 resolved here by also adding the total amount of pending raw data that is not
4610 currently being processed in the current record by OpenSSL, even when "read
4613 @throws randolf::rex::xEBADF The underlying socket is not open
4614 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4615 part of the user address space
4616 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
4617 @throws randolf::rex::xENOTTY The underlying socket is not associated with a
4618 character special device, or the specified operation does not apply
4619 to the kind of object that the file descriptor fd references (this
4620 exception will probably never occur unless the underlying socket
4621 handle was arbitrarily replaced with the type of handle that can
4622 cause this error to occur)
4624 @returns Total number of bytes pending (0 = none)
4628 *///=========================================================================
4630 if (__debug) debug("pending(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4633 // --------------------------------------------------------------------------
4634 // Get pending bytes from raw socket.
4635 // --------------------------------------------------------------------------
4636 ulong pending_bytes = 0; // BSD, glibc, etc., use ulong; older UNIX systems use int // TODO: Figure out how to detect what ioctl needs
4637 __rc_check(ioctl(__socket_fd, FIONREAD, &pending_bytes));
4639 // --------------------------------------------------------------------------
4640 // Add pending bytes from OpenSSL if TLS is active, because this total isn't
4641 // included in the raw socket count since OpenSSL received these bytes.
4642 // --------------------------------------------------------------------------
4643 return __tls ? pending_bytes + SSL_pending(__tls_fd) : pending_bytes;
4645 }; // -x- ulong pending -x-
4647 /*======================================================================*//**
4649 Poll the underlying socket using the poll() method for data that's ready for
4650 receiving (default), etc.
4653 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4654 select(), and accept()/accept4() when using multiple sockets.
4656 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4657 part of the user address space
4658 @throws randolf::rex::xEINTR Interrupted by a signal
4659 @throws randolf::rex::xENOMEM Insufficient memory
4660 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4661 doesn't refer to a socket
4662 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4663 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4664 is a highly improbable chance that a timeout could still occur if the
4665 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4666 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4669 @returns The resulting (short)pollfd.revents bitfield
4670 @returns 0 if @c timeout_behaviour is set to TIMEOUT_ZERO
4675 *///=========================================================================
4676 // TODO: Integrate SSL_poll (requires OpenSSL 3.3 or newer) with SSL_POLL_EVENT_R, SSL_POLL_EVENT_W, and SSL_POLL_EVENT_E conversion
4678 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4679 const short events = POLLIN,
4680 /// Number of milliseconds to wait
4681 const int timeout = 0,
4682 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4683 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4684 if (__debug) debug("poll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4685 + ", pollfd.events=" + std::to_string(events)
4686 + ", timeout=" + std::to_string(timeout)
4688 struct pollfd fds{__socket_fd, events, 0};
4689 if (__rc_check(::poll(&fds, 1, timeout)) == 0) {
4690 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4694 }; // -x- short poll -x-
4696 /*======================================================================*//**
4698 Get port number associated with underlying socket descriptor/handle.
4701 - an emphemeral port number assignment (dynamic) is intended
4702 - the underlying socket details are not defined (e.g., in the case of an
4703 empty rsocket instantiation)
4704 - port numbers are not supported/utilized by the current family (e.g., not
4705 AF_INET {IPv4} or AF_INET6 {IPv6})
4707 The port number can be set in most constructors, or via one of the socket()
4709 @returns Port number (typically TCP and UDP, although some other families
4710 such as SCTP and DCCP also utilize port numbers)
4711 @see socket_family()
4712 @see socket_protocol()
4715 *///=========================================================================
4716 const uint16_t port() noexcept {
4717 switch (__socket_addr.ss_family) {
4718 case AF_INET: // IPv4
4719 return ntohs(((sockaddr_in*)&__socket_addr)->sin_port);
4720 case AF_INET6: // IPv6
4721 return ntohs(((sockaddr_in6*)&__socket_addr)->sin6_port);
4722 default: // Everything else
4724 } // -x- switch __socket_addr.ss_family -x-
4725 }; // -x- uint16_t port -x-
4727 /*======================================================================*//**
4729 Poll the underlying socket using the ppoll() method for data that's ready for
4730 receiving (default), etc.
4733 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4734 select(), and accept()/accept4() when using multiple sockets.
4736 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4737 part of the user address space
4738 @throws randolf::rex::xEINTR Interrupted by a signal
4739 @throws randolf::rex::xENOMEM Insufficient memory
4740 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4741 doesn't refer to a socket
4742 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4743 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4744 is a highly improbable chance that a timeout could still occur if the
4745 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4746 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4749 @returns The resulting (short)pollfd.revents bitfield
4754 *///=========================================================================
4755 // TODO: Integrate SSL_poll (requires OpenSSL 3.3 or newer) with SSL_POLL_EVENT_R, SSL_POLL_EVENT_W, and SSL_POLL_EVENT_E conversion
4757 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4758 const short events = POLLIN,
4760 const struct timespec* tmo_p = nullptr,
4762 const sigset_t* sigmask = nullptr,
4763 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4764 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4765 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4766 + ", pollfd.events=" + std::to_string(events)
4767 + ", timespec{tv_sec=" + std::to_string(tmo_p->tv_sec)
4768 + ", tv_nsec=" + std::to_string(tmo_p->tv_nsec) + "}"
4771 struct pollfd fds{__socket_fd, events, 0};
4772 if (__rc_check(::ppoll(&fds, 1, tmo_p, sigmask)) == 0) {
4773 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4777 }; // -x- short ppoll -x-
4779 /*======================================================================*//**
4781 Poll the underlying socket using the ppoll() method for data that's ready for
4782 receiving (default), etc.
4785 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4786 select(), and accept()/accept4() when using multiple sockets.
4788 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4789 part of the user address space
4790 @throws randolf::rex::xEINTR Interrupted by a signal
4791 @throws randolf::rex::xENOMEM Insufficient memory
4792 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4793 doesn't refer to a socket
4794 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4795 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4796 is a highly improbable chance that a timeout could still occur if the
4797 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4798 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4801 @returns The resulting (short)pollfd.revents bitfield
4805 *///=========================================================================
4806 // TODO: Integrate SSL_poll (requires OpenSSL 3.3 or newer) with SSL_POLL_EVENT_R, SSL_POLL_EVENT_W, and SSL_POLL_EVENT_E conversion
4808 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4809 const short events = POLLIN,
4810 /// Timeout in seconds
4811 const int tv_sec = 0,
4812 /// Timeout in nanoseconds
4813 const long tv_nsec = 0,
4815 const sigset_t* sigmask = nullptr,
4816 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4817 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4818 struct timespec tmo_p{tv_sec, tv_nsec};
4819 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4820 + ", pollfd.events=" + std::to_string(events)
4821 + ", timespec{tv_sec=" + std::to_string(tmo_p.tv_sec)
4822 + ", tv_nsec=" + std::to_string(tmo_p.tv_nsec) + "}"
4825 struct pollfd fds{__socket_fd, events, 0};
4826 if (__rc_check(::ppoll(&fds, 1, &tmo_p, sigmask)) == 0) {
4827 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4831 }; // -x- short ppoll -x-
4833 /*======================================================================*//**
4835 Send a formatted string to the @ref rsocket endpoint.
4837 The @c format is described in the documentation for the POSIX or Standard C
4838 Library @c printf() function.
4839 @throws randolf::rex::xEBADF The underlying socket is not open
4840 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
4841 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
4842 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
4843 @throws randolf::rex::xENOMEM Insufficient memory
4844 @returns The same rsocket object so as to facilitate stacking
4846 @see is_eol_fix_printf
4853 *///=========================================================================
4855 /// Format string to use
4857 /// Variadic arguments
4861 ::va_start(args, format);
4862 int rc = ::vasprintf(&buf, format, args);
4863 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
4864 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
4865 if (__eol_fix_printf && !__eol.empty()) {
4866 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
4868 __send(str.c_str(), str.length());
4872 } catch (std::exception& e) { // Free buf then re-throw the exception
4873 ::free(buf); // Prevent memory leak when an exception is thrown
4876 } // -x- if __eol_fix_printf -x-
4878 }; // -x- rsocket* printf -x-
4880 /*======================================================================*//**
4882 Send a formatted string to the @ref rsocket endpoint, and append an EoL
4885 The @c format is described in the documentation for the POSIX or Standard C
4886 Library @c printf() function.
4887 @throws randolf::rex::xEBADF The underlying socket is not open
4888 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
4889 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
4890 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
4891 @throws randolf::rex::xENOMEM Insufficient memory
4892 @returns The same rsocket object so as to facilitate stacking
4895 @see is_eol_fix_printf
4902 *///=========================================================================
4903 rsocket* printfline(
4904 /// Format string to use
4906 /// Variadic arguments
4910 ::va_start(args, format);
4911 int rc = ::vasprintf(&buf, format, args);
4912 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
4913 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
4914 if (__eol_fix_printf && !__eol.empty()) {
4915 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
4918 __send(str.c_str(), str.length());
4921 __sendline(buf, rc);
4922 } catch (std::exception& e) { // Free buf then re-throw the exception
4923 ::free(buf); // Prevent memory leak when an exception is thrown
4926 } // -x- if __eol_fix_printf -x-
4928 }; // -x- rsocket* printfline -x-
4930 /*======================================================================*//**
4932 Receive data from the endpoint into a @c std::vector<char> that is allocated
4935 If nbytes is 0, then the internal @ref buffer_size() will be used as the
4936 default, which can also be changed from its compiled-in default of 1024 by
4937 using one of the buffer_size() methods.
4940 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4941 reception, but it's important to note that it does implement temporary
4942 blocking while waiting for data.
4944 @throws randolf::rex::xEBADF The underlying socket is not open
4945 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4947 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4948 part of the user address space
4949 @throws randolf::rex::xEINTR Interrupted by a signal
4950 @throws randolf::rex::xEINVAL Invalid argument passed
4951 @throws randolf::rex::xENOMEM Insufficient memory
4952 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4953 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4954 doesn't refer to a socket
4955 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4958 @returns appropriately-sized vector of characters
4959 @see recv(std::vector<char>, const int)
4960 @see recvz(const size_t, const int)
4962 *///=========================================================================
4963 std::vector<char> recv(
4964 /// Maximum number of bytes to receive
4965 const size_t nbytes = 0,
4970 /// MSG_CMSG_CLOEXEC
4971 const int posix_flags = 0) {
4972 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
4973 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4975 + ", " + std::to_string(buf_size)
4976 + ", " + std::to_string(posix_flags)
4978 std::vector<char> buf(buf_size);
4979 buf.resize(__recv(buf.data(), buf.size(), posix_flags));
4981 }; // -x- std::vector<char> recv -x-
4983 /*======================================================================*//**
4985 Receive data from the endpoint into the @c std::vector object supplied in the
4988 The maximum number of bytes that can be received won't exceed the number of
4989 bytes that the supplied @c std::vector<char> was initialized or resized to.
4992 For @c std::vector it's important that the @c resize() method is used to
4993 pre-allocate the underlying char[] array, instead of the unfortunately-named
4994 @c reserve() method that doesn't pre-allocate, to avoid causing segmentation
4995 faults or other undefined behaviours.
4998 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4999 reception, but it's important to note that it does implement temporary
5000 blocking while waiting for data.
5002 @throws randolf::rex::xEBADF The underlying socket is not open
5003 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5005 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5006 part of the user address space
5007 @throws randolf::rex::xEINTR Interrupted by a signal
5008 @throws randolf::rex::xEINVAL Invalid argument passed
5009 @throws randolf::rex::xENOMEM Insufficient memory
5010 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5011 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5012 doesn't refer to a socket
5013 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5016 @returns The same array that was specified in the @c buf parameter
5017 @see recv(const size_t, const int)
5018 @see recvz(const size_t, const int)
5021 *///=========================================================================
5022 std::vector<char> recv(
5023 /// Target std::vector<char> to receive data into
5024 std::vector<char> buf,
5029 /// MSG_CMSG_CLOEXEC
5030 const int posix_flags = 0) {
5031 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5033 + ", " + std::to_string(buf.size())
5034 + ", " + std::to_string(posix_flags)
5036 buf.resize(__recv(buf.data(), buf.size(), posix_flags));
5038 }; // -x- std::vector<char> recv -x-
5040 /*======================================================================*//**
5042 Receive data from the endpoint into a @c std::string object that is allocated
5045 If nbytes is 0, then the internal @ref buffer_size() will be used as the
5046 default, which can also be changed from its compiled-in default of 1024 by
5047 using one of the buffer_size() methods.
5050 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5051 reception, but it's important to note that it does implement temporary
5052 blocking while waiting for data.
5054 @throws randolf::rex::xEBADF The underlying socket is not open
5055 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5057 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5058 part of the user address space
5059 @throws randolf::rex::xEINTR Interrupted by a signal
5060 @throws randolf::rex::xEINVAL Invalid argument passed
5061 @throws randolf::rex::xENOMEM Insufficient memory
5062 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5063 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5064 doesn't refer to a socket
5065 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5068 @returns appropriately-sized vector of characters
5070 @see recv(std::vector<char>, const int)
5071 @see recvz(const size_t, const int)
5073 *///=========================================================================
5074 std::string recv_as_string(
5075 /// Maximum number of bytes to receive
5076 const size_t nbytes = 0,
5081 /// MSG_CMSG_CLOEXEC
5082 const int posix_flags = 0) {
5083 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5084 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5086 + ", " + std::to_string(buf_size)
5087 + ", " + std::to_string(posix_flags)
5090 buf.resize(buf_size); // Pre-fill anticipated string size
5091 buf.resize(__recv(buf.data(), buf.size(), posix_flags)); // Shorten string
5093 }; // -x- std::string recv -x-
5095 /*======================================================================*//**
5097 Receive an ASCIIZ string from the endpoint, including the NULL terminator.
5099 @throws randolf::rex::xEBADF The underlying socket is not open
5100 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5102 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5103 part of the user address space
5104 @throws randolf::rex::xEINTR Interrupted by a signal
5105 @throws randolf::rex::xEINVAL Invalid argument passed
5106 @throws randolf::rex::xENOMEM Insufficient memory
5107 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5108 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5109 doesn't refer to a socket
5110 @throws randolf::rex::xERANGE if no NULL terminator is detected (this may
5111 occur before the underlying ASCIIZ string char* array is allocated)
5112 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5115 @returns Pointer to ASCIIZ string (will need to be deleted by caller)
5116 @see send_asciiz(const char*, const int)
5118 *///=========================================================================
5120 /// Maximum number of bytes to receive
5121 const size_t nbytes = 0,
5126 /// MSG_CMSG_CLOEXEC
5127 const int posix_flags = 0) {
5128 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5129 if (__debug) debug("recv_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5130 + ", " + std::to_string(buf_size)
5131 + ", " + std::to_string(posix_flags)
5134 // --------------------------------------------------------------------------
5135 // Calculate size of buffer (includes NULL terminator since we'll also be
5136 // receiving this from the endpoint).
5137 // --------------------------------------------------------------------------
5140 // --------------------------------------------------------------------------
5141 // Reduce buffer size to what is actually read (remember: we don't actually
5142 // know where the EoL sequence is yet, or if there even is one).
5143 // --------------------------------------------------------------------------
5144 int max = __recv(&buf, buf_size, posix_flags | MSG_PEEK); // Look-ahead at socket stream (buffer was inflated to include EoL sequence)
5146 for (int i = 0; i < max; i++) {
5150 } // -x- if v[i] -x-
5152 if (len < 0) throw randolf::rex::xERANGE("ERANGE: NULL terminator not found");
5154 // --------------------------------------------------------------------------
5155 // I'd love to use std::shared_ptr<char*> for this next part, but it leads
5156 // to intermittent segmentation faults and/or "malloc(): corrupted top size
5157 // occurs" errors outside of this method. So, my conclusion is that char*
5158 // (and char[], as I've tried with this too) are not properly supported by:
5159 // std::shared_ptr<char*> v = std::make_shared<char*>(new char[len + 1]);
5161 // Using std::string() is really not what we want because there is confusion
5162 // concerning the NULL terminator -- with char* strings, it's crystal clear
5163 // that there must be a NULL terminator (since a length isn't tracked). Now,
5164 // if you want an std::string, just initialize as follows:
5166 // char* temp_string = r.recv_asciiz();
5167 // std::string mystr = std::string(temp_string);
5168 // delete temp_string;
5169 // --------------------------------------------------------------------------
5170 char* v = new char[len + 1];
5171 int rc = __recv(v, len + 1, posix_flags); // Consume with NULL terminator from socket stream
5172// TODO: Free "v" in a catch-and-rethrow block (check other calls to __recv and __send too)
5175 }; // -x- char* recv_asciiz -x-
5177 /*======================================================================*//**
5179 Receive one byte (unsigned 8-bit byte) of data from the endpoint.
5181 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5182 reception, but it's important to note that it does implement temporary
5183 blocking while waiting for data.
5185 @throws randolf::rex::xEBADF The underlying socket is not open
5186 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5188 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5189 part of the user address space
5190 @throws randolf::rex::xEINTR Interrupted by a signal
5191 @throws randolf::rex::xEINVAL Invalid argument passed
5192 @throws randolf::rex::xENOMEM Insufficient memory
5193 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5194 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5195 doesn't refer to a socket
5196 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5199 @returns one unsigned character
5200 @see recv(std::vector<char>, const int)
5201 @see recvz(const size_t, const int)
5206 *///=========================================================================
5212 /// MSG_CMSG_CLOEXEC
5213 const int posix_flags = 0) {
5215 if (__debug) debug("recv_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5217 + ", " + std::to_string(sizeof(buf))
5218 + ", " + std::to_string(posix_flags)
5220 int rc = __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5222 }; // -x- byte recv_byte -x-
5224 /*======================================================================*//**
5226 Receive one character (signed 8-bit byte) of data from the endpoint.
5228 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5229 reception, but it's important to note that it does implement temporary
5230 blocking while waiting for data.
5232 @throws randolf::rex::xEBADF The underlying socket is not open
5233 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5235 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5236 part of the user address space
5237 @throws randolf::rex::xEINTR Interrupted by a signal
5238 @throws randolf::rex::xEINVAL Invalid argument passed
5239 @throws randolf::rex::xENOMEM Insufficient memory
5240 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5241 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5242 doesn't refer to a socket
5243 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5246 @returns one signed character
5247 @see recv(std::vector<char>, const int)
5248 @see recvz(const size_t, const int)
5253 *///=========================================================================
5259 /// MSG_CMSG_CLOEXEC
5260 const int posix_flags = 0) {
5262 if (__debug) debug("recv_char(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5264 + ", " + std::to_string(sizeof(buf))
5265 + ", " + std::to_string(posix_flags)
5267 int rc = __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5268 // How to detect disconnected stream when telnet user presses CTRL-C?
5269 //std::cout << "rc=" << std::to_string(rc) << std::endl; // Debug (to be removed)
5270// TODO: Investigate checking platform's char size and moving signing bit accordingly
5272 }; // -x- char recv_char -x-
5274 /*======================================================================*//**
5276 Receive a line of data from the endpoint, into an @ref randolf::rline object
5277 with the EoL character(s) isolated. While this is meant for ASCII and UTF-8
5278 text, it will also work with binary data that doesn't include EoL character
5279 sequences as non-line-ending data (when receiving binary data,
5280 the @ref recv() and @ref recvz() methods tend to be better-suited).
5282 This is essentially a wrapper around what recvline() does, but returns both
5283 the line of text and the EoL sequence together in an @ref randolf::rline
5286 For additional details on the other parameters, please see the @ref recvline
5287 method's documentation for the remaining details.
5289 If you're using a customzied EoL sequence, then it's important to note that
5290 it probably won't be recognized by the @ref randolf::rline class, but do also
5291 check that documentation to confirm this.
5293 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
5294 @throws randolf::rex::xEBADF The underlying socket is not open
5295 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5297 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5298 part of the user address space
5299 @throws randolf::rex::xEINTR Interrupted by a signal
5300 @throws randolf::rex::xEINVAL Invalid argument passed
5301 @throws randolf::rex::xENOMEM Insufficient memory
5302 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5303 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5304 doesn't refer to a socket
5305 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
5306 no EoL character sequence is detected after recvline's buffer became
5307 full (whatever data is waiting may still be received using any of the
5308 recv_ methods {with or without the @c MSG_PEEK flag set}, including
5309 @c recvline() with a larger buffer {see the @c nbytes parameter})
5310 @throws randolf::rex::xERANGE if the timeout parameter is below 0
5311 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5314 @returns One line of text as a randolf::rline object.
5317 @see eol_consumed_seq()
5320 @see sendline(const std::string, const int)
5322 @see timeout_recvline
5323 @see timeout_recvline(long)
5325 *///=========================================================================
5326 randolf::rline recv_rline(
5327 /// Maximum number of bytes to receive (including EoL character sequence)
5328 const size_t nbytes = 0,
5333 /// MSG_CMSG_CLOEXEC
5334 const int posix_flags = 0,
5335 /// Line timeout (in seconds)@n
5336 /// 0 = no timeout (default), unless it was configured by way of the
5337 /// @ref timeout_recvline(long) method
5339 /// Configuration parameters
5340 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
5341 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
5342 // + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF; TODO: Add an option for this
5343 // Internally, the length of the @ref eol() sequence is added to the buffer size
5344 // so that the maximum line length without EoL characters matches the maximum
5345 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
5346 if (__debug) debug("recv_rline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5347 + ", " + std::to_string(buf_size)
5348 + ", " + std::to_string(posix_flags)
5350 std::string line = __recvline(buf_size, posix_flags, timeout, recvline_flags);
5351 return randolf::rline(line.append(eol_consumed_seq()));
5352 }; // -x- randolf::rline recv_rline -x-
5354 /*======================================================================*//**
5356 Receive a data structure from the endpoint.
5358 MSB/LSB considerations are important for any integers within your structure,
5359 so be sure to sanitize them with htons(), ntohs(), and related methods prior
5360 to sending, and then after receiving. This way, your data will be protected
5361 against corruption resulting from byte order differences when communicating
5362 between hardware architectures that differ in MSB and LSB byte ordering.
5364 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5365 reception, but it's important to note that it does implement temporary
5366 blocking while waiting for data.
5368 @throws randolf::rex::xEBADF The underlying socket is not open
5369 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5371 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5372 part of the user address space
5373 @throws randolf::rex::xEINTR Interrupted by a signal
5374 @throws randolf::rex::xEINVAL Invalid argument passed
5375 @throws randolf::rex::xENOMEM Insufficient memory
5376 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5377 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5378 doesn't refer to a socket
5379 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5382 @returns Data structure
5385 *///=========================================================================
5386 template <typename T> T recv_struct(
5391 /// MSG_CMSG_CLOEXEC
5392 const int posix_flags = 0) {
5394 if (__debug) debug("recv_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5396 + ", " + std::to_string(sizeof(buf))
5397 + ", " + std::to_string(posix_flags)
5399 __recv(&buf, sizeof(buf), posix_flags);
5401 }; // -x- T recv_struct -x-
5403 /*======================================================================*//**
5405 Receive one 16-bit unsigned integer of data in LSB (little endian) order from
5408 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5409 reception, which implements temporary blocking while waiting for data.
5411 @throws randolf::rex::xEBADF The underlying socket is not open
5412 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5414 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5415 part of the user address space
5416 @throws randolf::rex::xEINTR Interrupted by a signal
5417 @throws randolf::rex::xEINVAL Invalid argument passed
5418 @throws randolf::rex::xENOMEM Insufficient memory
5419 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5420 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5421 doesn't refer to a socket
5422 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5425 @returns uint16_t (converted to local endianness)
5427 *///=========================================================================
5428 uint16_t recv_uint16_lsb(
5433 /// MSG_CMSG_CLOEXEC
5434 const int posix_flags = 0) {
5436 if (__debug) debug("recv_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5438 + ", " + std::to_string(sizeof(buf))
5439 + ", " + std::to_string(posix_flags)
5441 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5442 return !__endian_is_msb ? buf : ntohs(buf);
5443 }; // -x- uint16_t recv_uint16_lsb -x-
5445 /*======================================================================*//**
5447 Receive one 16-bit unsigned integer of data in MSB (big endian) order from
5450 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5451 reception, which implements temporary blocking while waiting for data.
5453 @throws randolf::rex::xEBADF The underlying socket is not open
5454 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5456 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5457 part of the user address space
5458 @throws randolf::rex::xEINTR Interrupted by a signal
5459 @throws randolf::rex::xEINVAL Invalid argument passed
5460 @throws randolf::rex::xENOMEM Insufficient memory
5461 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5462 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5463 doesn't refer to a socket
5464 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5467 @returns uint16_t (converted to local endianness)
5469 *///=========================================================================
5470 uint16_t recv_uint16_msb(
5475 /// MSG_CMSG_CLOEXEC
5476 const int posix_flags = 0) {
5478 if (__debug) debug("recv_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5480 + ", " + std::to_string(sizeof(buf))
5481 + ", " + std::to_string(posix_flags)
5483 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5484 return __endian_is_msb ? buf : htons(buf);
5485 }; // -x- uint16_t recv_uint16_msb -x-
5487 /*======================================================================*//**
5489 Receive one 32-bit unsigned integer of data in LSB (little endian) order from
5492 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5493 reception, which implements temporary blocking while waiting for data.
5495 @throws randolf::rex::xEBADF The underlying socket is not open
5496 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5498 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5499 part of the user address space
5500 @throws randolf::rex::xEINTR Interrupted by a signal
5501 @throws randolf::rex::xEINVAL Invalid argument passed
5502 @throws randolf::rex::xENOMEM Insufficient memory
5503 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5504 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5505 doesn't refer to a socket
5506 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5509 @returns uint32_t (converted to local endianness)
5511 *///=========================================================================
5512 uint32_t recv_uint32_lsb(
5517 /// MSG_CMSG_CLOEXEC
5518 const int posix_flags = 0) {
5520 if (__debug) debug("recv_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5522 + ", " + std::to_string(sizeof(buf))
5523 + ", " + std::to_string(posix_flags)
5525 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5526 return !__endian_is_msb ? buf : htonl(buf);
5527 }; // -x- uint32_t recv_uint32_lsb -x-
5529 /*======================================================================*//**
5531 Receive one 32-bit unsigned integer of data in MSB (big endian) order from
5534 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5535 reception, which implements temporary blocking while waiting for data.
5537 @throws randolf::rex::xEBADF The underlying socket is not open
5538 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
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::xENOMEM Insufficient memory
5545 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5546 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5547 doesn't refer to a socket
5548 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5551 @returns uint32_t (converted to local endianness)
5553 *///=========================================================================
5554 uint32_t recv_uint32_msb(
5559 /// MSG_CMSG_CLOEXEC
5560 const int posix_flags = 0) {
5562 if (__debug) debug("recv_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5564 + ", " + std::to_string(sizeof(buf))
5565 + ", " + std::to_string(posix_flags)
5567 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5568 return __endian_is_msb ? buf : ntohl(buf);
5569 }; // -x- uint32_t recv_uint32_msb -x-
5571 /*======================================================================*//**
5573 Receive one 64-bit unsigned integer of data in LSB (little endian) order from
5576 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5577 reception, which implements temporary blocking while waiting for data.
5579 @throws randolf::rex::xEBADF The underlying socket is not open
5580 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5582 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5583 part of the user address space
5584 @throws randolf::rex::xEINTR Interrupted by a signal
5585 @throws randolf::rex::xEINVAL Invalid argument passed
5586 @throws randolf::rex::xENOMEM Insufficient memory
5587 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5588 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5589 doesn't refer to a socket
5590 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5593 @returns uint64_t (converted to local endianness)
5595 *///=========================================================================
5596 uint64_t recv_uint64_lsb(
5601 /// MSG_CMSG_CLOEXEC
5602 const int posix_flags = 0) {
5604 if (__debug) debug("recv_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5606 + ", " + std::to_string(sizeof(buf))
5607 + ", " + std::to_string(posix_flags)
5609 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5610 return !__endian_is_msb ? buf : ((((uint64_t)ntohl((buf) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((buf) >> 32)));
5611 }; // -x- uint64_t recv_uint64_lsb -x-
5613 /*======================================================================*//**
5615 Receive one 64-bit unsigned integer of data in MSB (big endian) order from
5618 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5619 reception, which implements temporary blocking while waiting for data.
5621 @throws randolf::rex::xEBADF The underlying socket is not open
5622 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5624 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5625 part of the user address space
5626 @throws randolf::rex::xEINTR Interrupted by a signal
5627 @throws randolf::rex::xEINVAL Invalid argument passed
5628 @throws randolf::rex::xENOMEM Insufficient memory
5629 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5630 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5631 doesn't refer to a socket
5632 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5635 @returns uint64_t (converted to local endianness)
5637 *///=========================================================================
5638 uint64_t recv_uint64_msb(
5643 /// MSG_CMSG_CLOEXEC
5644 const int posix_flags = 0) {
5646 if (__debug) debug("recv_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5648 + ", " + std::to_string(sizeof(buf))
5649 + ", " + std::to_string(posix_flags)
5651 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5652 return __endian_is_msb ? buf : ((((uint64_t)htonl((buf) & 0xffffffffUL)) << 32) | htonl((uint32_t)((buf) >> 32)));
5653 }; // -x- uint64_t recv_uint64_msb -x-
5655 /*======================================================================*//**
5657 Receive data from a specific endpoint.
5660 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5661 reception, but it's important to note that it does implement temporary
5662 blocking while waiting for data.
5665 This method is not compatible with TLS.
5667 @throws randolf::rex::xEBADF The underlying socket is not open
5668 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5670 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5671 part of the user address space
5672 @throws randolf::rex::xEINTR Interrupted by a signal
5673 @throws randolf::rex::xEINVAL Invalid argument passed
5674 @throws randolf::rex::xENOMEM Insufficient memory
5675 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5676 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5677 doesn't refer to a socket
5678 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5681 @returns Appropriately-sized vector of characters
5683 *///=========================================================================
5684 std::vector<char> recvfrom(
5685 /// Maximum number of bytes to receive
5686 const size_t nbytes,
5691 /// MSG_CMSG_CLOEXEC
5692 const int posix_flags,
5693 /// Target endpoint address structure
5694 struct sockaddr *from,
5695 /// Size of target endpoint structure
5696 socklen_t fromlen = sizeof(sockaddr)) {
5697 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5698 if (__debug) debug("recvfrom(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5700 + ", " + std::to_string(buf_size)
5701 + ", " + std::to_string(posix_flags)
5703 + ", " + std::to_string(fromlen)
5705 std::vector<char> v(buf_size);
5706 v.resize(__track_bytes_rx(__rc_check(::recvfrom(__socket_fd, v.data(), v.size(), posix_flags, from, &fromlen))));
5708 }; // -x- std::vector<char> recvfrom -x-
5711 /*======================================================================*//**
5713 This is an internal function that:
5714 1. receives data from the endpoint:
5715 - via underlying socket when TLS is not enabled
5716 - via the OpenSSL socket API when TLS is enabled
5717 2. checks for a socket I/O error (and throws an exception)
5718 3. tracks number of bytes received (if there were no errors)
5721 The ring buffer will only be used if there is one. The @ref __recvline
5722 method is one method that instantiates the internal ring buffer, and there
5723 could be others in the future (although this is not strongly anticipated
5724 aside from a method that allows the software developer to explicitly choose
5725 to have a buffer active for all socket-read operations).
5727 @throws randolf::rex::xEAGAIN The underlying socket timed out
5728 @throws randolf::rex::xEBADF The underlying socket is not open
5729 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5731 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5732 part of the user address space
5733 @throws randolf::rex::xEINTR Interrupted by a signal
5734 @throws randolf::rex::xEINVAL Invalid argument passed
5735 @throws randolf::rex::xENOMEM Insufficient memory
5736 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5737 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5738 doesn't refer to a socket
5739 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5742 @returns Number of bytes that were successfully received
5744 *///=========================================================================
5748 /// Length of message data
5750 /// Flags (ignored with encrypted streams, except for the MSG_PEEK flag)
5751 const int posix_flags = 0) {
5753 // --------------------------------------------------------------------------
5754 // When internal buffering is not set up (which is the default operation), we
5755 // simply pass data directly.
5756 // --------------------------------------------------------------------------
5757 if (__buffer == nullptr) {
5758 return __tls ? (posix_flags & MSG_PEEK ? __rc_check_tls(SSL_peek(__tls_fd, data, len))
5759 : __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, data, len))))
5760 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, data, len, posix_flags)));
5761 } // -x- if !__buffer -x-
5763 // --------------------------------------------------------------------------
5764 // If the amount of data contained in the internal buffer is sufficient to
5765 // satisfy the amount of data that "len" represents, then all that's needed
5766 // is to return "len" bytes from the ring buffer without having to receive
5767 // more data from the socket.
5768 // --------------------------------------------------------------------------
5769 if (len <= __buffer->get_utilized()) {
5770 if (posix_flags & MSG_PEEK) __buffer->peek_array( (size_t)len, (char*)data); // Copy character(s) from ring buffer because MSG_PEEK flag is set
5771 else __buffer->remove_array((size_t)len, (char*)data); // Consume character(s) from ring buffer
5775 // --------------------------------------------------------------------------
5776 // Consume data from socket to make up for OpenSSL's inability to read beyond
5777 // one packet of data in its SSL_peek() and SSL_read() methods, and also some
5778 // inconsistencies with certain raw (unencrypted) socket implementations that
5779 // occasionally refuse to read beyond one packet of data (at least on a first
5780 // attempt to read with or without the MSG_PEEK flag).
5782 // Fill internal buffer. It may be more than len is set to, but this is okay
5783 // because we're committed to using internal buffering now anyway (it's far
5784 // more likely that a protocol that requires the use of recvline() even once
5785 // is going to be using it repeatedly, but as long as the ring buffer is
5786 // large enough to cover the maximum length of a line of text, including its
5787 // EoL sequence, then this will always succeed).
5788 // --------------------------------------------------------------------------
5789 __buffer_bam = __buffer->data_bam(__buffer_bam); // Update ring buffer setup (will be allocated automatically if "nullptr" default)
5790 if (__buffer_bam->avail1_size == 0) return 0; // Buffer is full, so return 0
5791 int n = __tls ? __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, __buffer_bam->avail1, __buffer_bam->avail1_size))) // Not using SSL_peek() at all here because we need to consume this data as we stuff it into the internal buffer
5792 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, __buffer_bam->avail1, __buffer_bam->avail1_size, posix_flags & (~MSG_PEEK)))); // Mask the MSG_PEEK flag because we need to consume this data as we stuff it into the internal buffer
5793 if (n == 0) return 0;
5794 __buffer->set_head(n, false); // Add number of bytes received to the ring buffer after writing to the available portion of the ring buffer
5796 // --------------------------------------------------------------------------
5797 // Fill in the remaining (unused) portion of the internal buffer (if it's
5800 // Note: FIONREAD instructs ioctl() to acquire the number of bytes that are
5801 // immediately available for reading. Returns 0 when successful.
5802 // --------------------------------------------------------------------------
5803 if (n == __buffer_bam->avail1_size // First available block got filled
5804 && __buffer_bam->avail2_size > 0 // Second available block is not 0 (which means that more data can be stored there)
5805 && !eos()) { // More data is in stream, ready to be copied without any delay
5806 int n2 = __tls ? __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, __buffer_bam->avail2, __buffer_bam->avail2_size))) // Not using SSL_peek() at all here because we need to consume this data as we stuff it into the internal buffer
5807 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, __buffer_bam->avail2, __buffer_bam->avail2_size, posix_flags & (~MSG_PEEK)))); // Mask the MSG_PEEK flag because we need to consume this data as we stuff it into the internal buffer
5808 __buffer->set_head(n2, false); // Add number of bytes received to the ring buffer after writing to the available portion of the ring buffer
5809 n += n2; // Update n
5812 // --------------------------------------------------------------------------
5813 // Copy or move data into destination.
5814 // --------------------------------------------------------------------------
5815 size_t utilized = std::min((size_t)len, __buffer->get_utilized()); // Specified length or utilized data in ring buffer, whichever is less
5816 if (posix_flags & MSG_PEEK) __buffer->peek_array( utilized, (char*)data); // Copy character(s) from ring buffer because MSG_PEEK flag is set
5817 else __buffer->remove_array(utilized, (char*)data); // Consume character(s) from ring buffer
5820 }; // -x- int __recv -x-
5822 /*======================================================================*//**
5824 This is an internal function that:
5825 1. receives a line of data from the endpoint:
5826 - via underlying socket when TLS is not enabled
5827 - via the OpenSSL socket API when TLS is enabled
5828 2. checks for a socket I/O error (and throws an exception)
5829 3. tracks number of bytes received (if there were no errors)
5830 4. isolates the EoL sequence from the line of data, and records it for
5831 later reference (for those cases where it's needed)
5833 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
5834 @throws randolf::rex::xEBADF The underlying socket is not open
5835 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5837 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5838 part of the user address space
5839 @throws randolf::rex::xEINTR Interrupted by a signal
5840 @throws randolf::rex::xEINVAL Invalid argument passed
5841 @throws randolf::rex::xENOMEM Insufficient memory
5842 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5843 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5844 doesn't refer to a socket
5845 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
5846 no EoL character sequence is detected after recvline's buffer became
5847 full (whatever data is waiting may still be received using any of the
5848 recv_ methods {with or without the @c MSG_PEEK flag set}, including
5849 @c recvline() with a larger buffer {see the @c nbytes parameter})
5850 @throws randolf::rex::xERANGE if the timeout parameter is below 0
5851 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5855 *///=========================================================================
5856 std::string __recvline(
5857 /// Maximum number of bytes to receive (including EoL character sequence)
5860 /// MSG_PEEK (with some limitations)@n
5862 /// MSG_DONTWAIT (ineffective when @c timeout is not 0)@n
5863 /// MSG_CMSG_CLOEXEC
5864 const int posix_flags,
5865 /// Line timeout (in seconds)@n
5866 /// 0 = no timeout (default), unless it was configured by way of the
5867 /// @ref timeout_recvline(long) method
5868 const ulong timeout,
5869 /// Configuration parameters
5870 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
5872 // --------------------------------------------------------------------------
5873 // Syntax checks as part of preparation of timeout variable for faster
5874 // comparisons within line-reading loop. To avoid problems when wrapping
5875 // past ULONG_MAX, we mask out the most-significant bit by multiplying by two
5876 // and then dividing by two.
5877 // --------------------------------------------------------------------------
5878 //if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
5879 //if (timeout == 0) timeout = (__recvline_timeout * 2) / 2; // Convert 0 to
5880//std::cout << "timeout=" << timeout << std::endl;
5881 ulong timeout_target = timeout == 0 ? ULONG_MAX : timeout + time(0);
5882//std::cout << "timeout_target=" << timeout_target << std::endl;
5884 // --------------------------------------------------------------------------
5885 // Allocate line buffer.
5886 // --------------------------------------------------------------------------
5887 std::string v; v.resize(buf_size);
5889 // --------------------------------------------------------------------------
5890 // If an internal buffer, which is used by __recv, hasn't been instantiated,
5891 // then instantiate it and save the pointer to it for future use.
5893 // Buffering becomes necessary with TLS connection because SSL_peek fails to
5894 // return data that spans multiple packets. What triggers this behaviour is
5895 // the command "stty -icanon && openssl s_client host_name tcp_port" followed
5896 // by manual interactive typing some text then pressing the "[Enter]" key.
5898 // The ::malloc(__buffer_size) won't be leaked because it will be freed in
5899 // in this rsocket's destructor (if it isn't freed elsewhere before this).
5900 // --------------------------------------------------------------------------
5901 if (__buffer == nullptr) __buffer = new rring(__buffer_size);
5903 int br = 0; // Number of Bytes Received
5904 int len_with_eol = 0;
5907 // --------------------------------------------------------------------------
5908 // Line-reading loop.
5909 // --------------------------------------------------------------------------
5912 // --------------------------------------------------------------------------
5913 // Attempt to read from the stream.
5914 // --------------------------------------------------------------------------
5916 br = __recv(v.data(), buf_size, posix_flags | MSG_PEEK); // Look-ahead at socket stream (with buffer size that was inflated earlier to include EoL sequence)
5917 } catch (const randolf::rex::xEAGAIN e) {
5918 // Socket timeout occurred, but ignore it so we can handle it separately
5920std::cout << "br=" << br << std::endl;
5921 len = eol_index(v, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
5922 if (len >= 0) break;
5923 if (br == buf_size && len < 0) break;
5925//std::cout << "br=" << br << " buf_size=" << buf_size << " v.size()=" << v.size() << " len=" << len << " len_with_eol=" << len_with_eol << " v[]=" << randolf::rtools::to_hex(v.substr(0, len < 0 ? 0 : 20), ":") << " t=" << timeout_target - time(0) << std::endl;
5926 // --------------------------------------------------------------------------
5927 // Wait for pending data for a duration no longer than the remaining timeout.
5928 // --------------------------------------------------------------------------
5929 if (eos(timeout == 0 ? timeout_target : timeout_target - time(0))) break;
5931 // --------------------------------------------------------------------------
5932 // Keep going if we haven't timed out yet.
5933 // --------------------------------------------------------------------------
5934 } while (timeout == 0 || time(0) < timeout_target);
5936std::cout << "break -- br=" << br << std::endl;
5937//std::cout << "br=" << br << " buf_size=" << buf_size << " v.size()=" << v.size() << " len=" << len << " len_with_eol=" << len_with_eol << " v[]=" << randolf::rtools::to_hex(v.substr(0, len < 0 ? 0 : 20), ":") << std::endl;
5938 // --------------------------------------------------------------------------
5939 // EoL sequence not found. Either the buffer is exhausted, or we're at the
5940 // end of the stream, with no EoL anywhere in sight.
5941 // --------------------------------------------------------------------------
5943 //std::cout << "overflow br=" << br << " buf_size=" << buf_size << std::endl; // Debug
5945 // --------------------------------------------------------------------------
5946 // Caller wants partial data instead of throwing an exception, even if it has
5948 // --------------------------------------------------------------------------
5949 if (recvline_flags && RECVLINE_PARTIAL) {
5950 __eol_consumed_seq = ""; // Indicate that there's no EoL sequence
5951 if (!(posix_flags & MSG_PEEK)) __recv(v.data(), br, posix_flags & (~MSG_PEEK)); // Consume data if not in MSG_PEEK mode / __recv will throw exception if something goes wrong so there's no need for an additional __rc_check call from here
5952 v.resize(br); // Truncate string to "br" (bytes received)
5954 } // -x- if RECVLINE_PARTIAL -x-
5956 // --------------------------------------------------------------------------
5957 // Generate xEOVERFLOW exception.
5959 // TODO: Generate a different exception for no EoL when the buffer isn't
5961 // --------------------------------------------------------------------------
5962 if (!(recvline_flags && RECVLINE_KEEP_ON_OVERFLOW) && !(posix_flags & MSG_PEEK)) {
5963 discard_line(); // Consume all that remains, or stop after first EoL sequence encountered
5964std::cout << "discarded" << std::endl;
5966 throw randolf::rex::xEOVERFLOW("recvline overflow (line too long or missing EoL sequence)");
5968 } // -x- if !len -x-
5970 // --------------------------------------------------------------------------
5971 // Save the EoL sequence that was detected following a successful recvline()
5972 // (this must be completed prior to "final clean-up" {which is next}, because
5973 // we need to copy these characters before the truncation occurs).
5974 // --------------------------------------------------------------------------
5975//std::cout << "len=" << len << " len_with_eol=" << len_with_eol << " __buffer.get_utilized()=" << __buffer->get_utilized() << std::endl;
5976 __eol_consumed_seq = v.substr(len, len_with_eol - len);
5978 // --------------------------------------------------------------------------
5980 // --------------------------------------------------------------------------
5981 if (!(posix_flags & MSG_PEEK)) __recv(v.data(), len_with_eol, posix_flags & (~MSG_PEEK)); // Consume data if not in MSG_PEEK mode / __recv will throw exception if something goes wrong so there's no need for an additional __rc_check call from here
5982 //if ((posix_flags & MSG_PEEK) == 0) __recv(v.data(), len_with_eol, posix_flags); // Consume data if not in MSG_PEEK mode / __recv will throw exception if something goes wrong so there's no need for an additional __rc_check call from here
5983 v.resize(len); // Truncate string to exclude unused portion (and we're also not including the EoL sequence)
5986 }; // -x- std::string __recvline -x-
5989 /*======================================================================*//**
5991 Receive a line of data from the endpoint, with the EoL character(s) removed.
5992 While this is meant for ASCII and UTF-8 text, it will also work with binary
5993 data that doesn't include EoL character sequences as non-line-ending data
5994 (when receiving binary data, the @ref recv() and @ref recvz() methods tend to
5997 If @c nbytes is 0, then the internal buffer_size will be used as the default,
5998 which can also be changed from its compiled-in default of 1024 by using one
5999 of the buffer_size() methods. The total number of bytes received, including
6000 the EoL sequence, will not exceed the total number of byte specified with the
6001 @c nbytes parameter.
6003 For @c nbytes the EoL character sequence size should be included, especially
6004 for shorter lines -- if the EoL character sequence is detected automatically
6005 on-the-fly (which is the default), then provisioning 2 characters is needed
6006 since an EoL sequence could be 1 or 2 characters long (line lengths shorter
6007 than 3 characters could be particularly problematic when detecting on-the-fly
6008 EoL character sequences). In such a case, a better approach is to allow for
6009 2 additional characters and then test the length of the returned string to
6010 ensure it doesn't exceed whatever the required maximum is (and throw the same
6011 @ref randolf::rex::xEOVERFLOW exception if the length is exceeded).
6013 For more information about which characters an EoL sequence is comprised of,
6014 and how our specialized support for multiple EoL sequences works, see the
6015 documentation for the various @ref eol methods.
6018 When setting the recvline timeout (either with the @c timeout parameter with
6019 this method, or by using the @ref timeout_recvline(long) method), you may
6020 also need to set the socket timeout beforehand using the @ref timeout()
6021 method, because @c recvline doesn't do this...
6024 @ref timeout_recvline sets the overall total timeout for an entire line to
6027 @ref timeout sets the timeout between individual characters received (such
6028 as keystrokes from a live end-user)
6031 If your socket timeout is longer than then recvline timeout, this will have
6032 the effect of rendering the recvling timeout as ineffective.
6034 The @c timeout parameter can be used to override the total overall timeout
6035 for receiving a line, which is useful for specific situations where a
6036 specific timeout is desired for particular prompts, during certain data entry
6040 When a line is not found (@c randolf::rex::xEAGAIN), or a line is too long
6041 because there's too much data (longer than a line) without an EoL sequence
6042 (@c randolf::rex::xOVERFLOW), then that data will not be discarded or
6043 consumed, and will remain ready for receiving (@ref recv) or for discarding
6047 If you're searching for a readline() method for socket I/O, then this is most
6048 likely what you're wanting/needing.
6050 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
6051 @throws randolf::rex::xEBADF The underlying socket is not open
6052 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6054 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6055 part of the user address space
6056 @throws randolf::rex::xEINTR Interrupted by a signal
6057 @throws randolf::rex::xEINVAL Invalid argument passed
6058 @throws randolf::rex::xENOMEM Insufficient memory
6059 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6060 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6061 doesn't refer to a socket
6062 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
6063 no EoL character sequence is detected after recvline's buffer became
6064 full (whatever data is waiting may still be received using any of the
6065 recv_ methods {with or without the @c MSG_PEEK flag set}, including
6066 @c recvline() with a larger buffer {see the @c nbytes parameter})
6067 @throws randolf::rex::xERANGE if the timeout parameter is below 0
6068 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6071 @returns One line of text as a std::string object.
6074 @see eol_consumed_seq()
6076 @see sendline(const std::string, const int)
6078 @see timeout_recvline
6079 @see timeout_recvline(long)
6081 *///=========================================================================
6082 std::string recvline(
6083 /// Maximum number of bytes to receive (including EoL character sequence)@n
6084 /// 0 = use internal @ref buffer_size()
6085 const size_t nbytes = 0,
6090 /// MSG_CMSG_CLOEXEC
6091 const int posix_flags = 0,
6092 /// Line timeout (in seconds)@n
6093 /// 0 = no timeout (default), unless it was configured by way of the
6094 /// @ref timeout_recvline(long) method
6096 /// Configuration parameters
6097 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
6098 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
6099 // + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF; TODO: Add an option for this
6100 // Internally, the length of the @ref eol() sequence is added to the buffer size
6101 // so that the maximum line length without EoL characters matches the maximum
6102 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
6103 if (__debug) debug("recvline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6104 + ", " + std::to_string(buf_size)
6105 + ", " + std::to_string(posix_flags)
6107 return __recvline(buf_size, posix_flags, timeout, recvline_flags);
6108 }; // -x- std::string recvline -x-
6110 /*======================================================================*//**
6112 Receive data in the form of a "msghdr" structure.
6114 This method is not compatible with TLS.
6116 @throws randolf::rex::xEBADF The underlying socket is not open
6117 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6119 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6120 part of the user address space
6121 @throws randolf::rex::xEINTR Interrupted by a signal
6122 @throws randolf::rex::xEINVAL Invalid argument passed
6123 @throws randolf::rex::xENOMEM Insufficient memory
6124 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6125 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6126 doesn't refer to a socket
6127 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6130 @returns pointer to "msghdr" structure
6132 *///=========================================================================
6134 /// Pointer to "msghdr" structure (or nullptr for automatic allocation)
6140 /// MSG_CMSG_CLOEXEC
6141 const int posix_flags = 0) {
6142 if (__debug) debug("recvmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6144 + ", " + std::to_string(posix_flags)
6146 if (msg == nullptr) msg = new msghdr{0};
6147 __track_bytes_rx(__rc_check(::recvmsg(__socket_fd, msg, posix_flags)));
6149 }; // -x- msghdr* recvmsg -x-
6151 /*======================================================================*//**
6153 Receive data in the form of an "mmsghdr" structure.
6155 This method is not compatible with TLS.
6157 @throws randolf::rex::xEBADF The underlying socket is not open
6158 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6160 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6161 part of the user address space
6162 @throws randolf::rex::xEINTR Interrupted by a signal
6163 @throws randolf::rex::xEINVAL Invalid argument passed
6164 @throws randolf::rex::xENOMEM Insufficient memory
6165 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6166 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6167 doesn't refer to a socket
6168 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6171 @returns pointer to "mmsghdr" structure
6173 *///=========================================================================
6175 /// Pointer to "mmsghdr" structure (or nullptr for automatic allocation)
6176 struct mmsghdr* mmsg,
6177 /// Size of target endpoint structure
6178 const unsigned int vlen = sizeof(mmsghdr),
6184 const int posix_flags = 0,
6186 struct timespec* timeout = {0}) {
6187 if (__debug) debug("recvmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6189 + ", " + std::to_string(vlen)
6190 + ", " + std::to_string(posix_flags)
6191 + ", timeout{tv_sec=" + std::to_string(timeout->tv_sec)
6192 + ", tv_nsec=" + std::to_string(timeout->tv_nsec) + "}"
6194 __track_bytes_rx(__rc_check(::recvmmsg(__socket_fd, mmsg, vlen, posix_flags, timeout)));
6196 }; // -x- mmsghdr* recvmmsg -x-
6198 /*======================================================================*//**
6200 Receive data from the endpoint, and add a 0 (null) onto the end. This is
6201 useful when using the resulting std::vector<char> as an ASCIIZ string.
6203 If nbytes is 0, then the internal buffer_size will be used as the default,
6204 which can also be changed from its compiled-in default of 1024 by using one
6205 of the buffer_size() methods.
6208 The resulting std::vector size is always inflated by 1. This means that
6209 relying on a comparison against 0 will result in an infinite loop:
6211 for (std::vector<char> v = r.recvz(); v.size > 0; v = recvz()) { ... }
6213 So, you'll need to compare against 1 instead of 0 to compensate fot the
6214 inflated size due to the addition of null character 0:
6216 for (std::vector<char> v = r.recvz(); v.size > 1; v = recvz()) { ... }
6220 The resulting size of std::vector<char> will be <tt>nbytes + 1</tt> which
6221 ensures that any string processing functions or presentation libraries - such
6222 as @c printf() - expecting an ASCIIZ string buffer will stop at null (0), and
6223 will reliably output no more than the maximum size specified.
6225 The way to think of this is that @c nbytes specifies the maximum number of
6226 bytes (a.k.a., characters) to recieve over the underlying socket, and the
6227 final 0 (null) being added is not included in the maximum specified by the
6228 @c nbytes parameter.
6231 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6232 reception, but it's important to note that it does implement temporary
6233 blocking while waiting for data.
6235 @throws randolf::rex::xEBADF The underlying socket is not open
6236 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6238 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6239 part of the user address space
6240 @throws randolf::rex::xEINTR Interrupted by a signal
6241 @throws randolf::rex::xEINVAL Invalid argument passed
6242 @throws randolf::rex::xENOMEM Insufficient memory
6243 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6244 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6245 doesn't refer to a socket
6246 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6249 @returns appropriately-sized vector of characters + 1 (includes additional
6250 null terminator character 0)
6251 @see recv(const size_t, const int)
6252 @see recv(std::vector<char>, const int)
6254 *///=========================================================================
6255 std::vector<char> recvz(
6256 /// Maximum number of bytes to receive
6257 const size_t nbytes = 0,
6262 /// MSG_CMSG_CLOEXEC
6263 const int posix_flags = 0) {
6264 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6265 if (__debug) debug("recvz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6267 + ", " + std::to_string(buf_size)
6268 + ", " + std::to_string(posix_flags)
6270 std::vector<char> v(buf_size);
6271 v.resize(__recv(v.data(), v.size(), posix_flags) + 1); // Add 1 to include room for final null character 0
6272 v[v.size() - 1] = (char)0; // Set final element to 0 (null)
6274 }; // -x- std::vector<char> recvz -x-
6277 /*======================================================================*//**
6279 This is an internal function that:
6280 1. sends data to the endpoint:
6281 - via underlying socket when TLS is not enabled
6282 - via the OpenSSL socket API when TLS is enabled
6283 2. checks for a socket I/O error (and throws an exception)
6284 3. tracks number of bytes transmitted (if there were no errors)
6287 This method is threadsafe because the underlying POSIX send() and OpenSSL's
6288 SSL_write() functions are threadsafe.
6290 @throws randolf::rex::xEBADF The underlying socket is not open
6291 @throws randolf::rex::xECONNRESET Connect reset by peer
6292 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6293 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6294 part of the user address space
6295 @throws randolf::rex::xEINTR Interrupted by a signal
6296 @throws randolf::rex::xEINVAL Invalid argument passed
6297 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6298 occur, but the POSIX sockets documentation lists it as one of the
6299 errors that can be returned, perhaps because some incorrectly
6300 implemented TCP/IP stacks return this error?)
6301 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6302 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6303 and 65,527 bytes for IPv6)
6304 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6305 network congestion (or, less commonly, insufficient memory)
6306 @throws randolf::rex::xENOMEM Insufficient memory
6307 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6308 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6309 doesn't refer to a socket
6310 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6311 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6312 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6314 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6316 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6317 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6318 but it really isn't)
6320 @returns Number of bytes that were successfully transmitted
6322 *///=========================================================================
6326 /// Length of message data
6328 /// Flags (ignored with encrypted streams)
6329 const int posix_flags = 0) {
6330 if (__tls) return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data, len)));
6331 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data, len, posix_flags)));
6332 }; // -x- int __send -x-
6334 /*======================================================================*//**
6336 This is an internal function that:
6337 1. sends data to the endpoint:
6338 - via underlying socket when TLS is not enabled
6339 - via the OpenSSL socket API when TLS is enabled
6340 2. checks for a socket I/O error (and throws an exception)
6341 3. tracks number of bytes transmitted (if there were no errors)
6344 This method is threadsafe because the underlying POSIX send() and OpenSSL's
6345 SSL_write() functions are threadsafe.
6347 @throws randolf::rex::xEBADF The underlying socket is not open
6348 @throws randolf::rex::xECONNRESET Connect reset by peer
6349 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6350 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6351 part of the user address space
6352 @throws randolf::rex::xEINTR Interrupted by a signal
6353 @throws randolf::rex::xEINVAL Invalid argument passed
6354 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6355 occur, but the POSIX sockets documentation lists it as one of the
6356 errors that can be returned, perhaps because some incorrectly
6357 implemented TCP/IP stacks return this error?)
6358 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6359 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6360 and 65,527 bytes for IPv6)
6361 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6362 network congestion (or, less commonly, insufficient memory)
6363 @throws randolf::rex::xENOMEM Insufficient memory
6364 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6365 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6366 doesn't refer to a socket
6367 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6368 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6369 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6371 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6373 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6374 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6375 but it really isn't)
6377 @returns Number of bytes that were successfully transmitted
6379 *///=========================================================================
6383 /// Number of bytes to send
6385 /// Flags (ignored with encrypted streams)
6386 const int posix_flags = 0) {
6387 if (__tls) { // Encrypted
6388 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data , len )))
6389 + __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.c_str(), __eol_out.length())));
6390 } else { // Not encrypted
6391 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data , len , posix_flags | MSG_MORE))) // MSG_MORE prevents extra packets from being sent
6392 + __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.c_str(), __eol_out.length(), posix_flags )));
6393 } // -x- if __tls -x-
6394 }; // -x- int __sendline -x-
6396 /*======================================================================*//**
6398 This is an internal function that:
6399 1. sends data to the endpoint:
6400 - via underlying socket when TLS is not enabled
6401 - via the OpenSSL socket API when TLS is enabled
6402 2. checks for a socket I/O error (and throws an exception)
6403 3. tracks number of bytes transmitted (if there were no errors)
6406 This method is threadsafe because the underlying POSIX send() and OpenSSL's
6407 SSL_write() functions are threadsafe.
6409 @throws randolf::rex::xEBADF The underlying socket is not open
6410 @throws randolf::rex::xECONNRESET Connect reset by peer
6411 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6412 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6413 part of the user address space
6414 @throws randolf::rex::xEINTR Interrupted by a signal
6415 @throws randolf::rex::xEINVAL Invalid argument passed
6416 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6417 occur, but the POSIX sockets documentation lists it as one of the
6418 errors that can be returned, perhaps because some incorrectly
6419 implemented TCP/IP stacks return this error?)
6420 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6421 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6422 and 65,527 bytes for IPv6)
6423 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6424 network congestion (or, less commonly, insufficient memory)
6425 @throws randolf::rex::xENOMEM Insufficient memory
6426 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6427 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6428 doesn't refer to a socket
6429 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6430 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6431 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6433 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6435 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6436 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6437 but it really isn't)
6439 @returns Number of bytes that were successfully transmitted
6441 *///=========================================================================
6443 /// Flags (ignored with encrypted streams)
6444 const int posix_flags = 0) {
6445 if (__tls) { // Encrypted
6446 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.c_str(), __eol_out.length())));
6447 } else { // Not encrypted
6448 return __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.c_str(), __eol_out.length(), posix_flags)));
6449 } // -x- if __tls -x-
6450 }; // -x- int __send_eol -x-
6451 // TODO: Create a recv_eol() method that some people will probably consider to be evil (ha ha!)
6452 // recv_eol() will need to be able to auto-detect the CRLF sequence on-the-fly (if necessary) just like recvline() does
6455 /*======================================================================*//**
6457 Send data in the form of a std::string to the endpoint.
6459 This method is threadsafe.
6461 @throws randolf::rex::xEBADF The underlying socket is not open
6462 @throws randolf::rex::xECONNRESET Connect reset by peer
6463 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6464 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6465 part of the user address space
6466 @throws randolf::rex::xEINTR Interrupted by a signal
6467 @throws randolf::rex::xEINVAL Invalid argument passed
6468 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6469 occur, but the POSIX sockets documentation lists it as one of the
6470 errors that can be returned, perhaps because some incorrectly
6471 implemented TCP/IP stacks return this error?)
6472 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6473 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6474 and 65,527 bytes for IPv6)
6475 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6476 network congestion (or, less commonly, insufficient memory)
6477 @throws randolf::rex::xENOMEM Insufficient memory
6478 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6479 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6480 doesn't refer to a socket
6481 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6482 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6483 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6485 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6487 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6488 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6489 but it really isn't)
6491 @returns The same rsocket object so as to facilitate stacking
6493 *///=========================================================================
6496 const std::string msg,
6502 const int posix_flags = 0) {
6503 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6505 + ", " + std::to_string(msg.length())
6506 + ", " + std::to_string(posix_flags)
6508 __send(msg.c_str(), msg.length(), posix_flags);
6510 }; // -x- rsocket* send -x-
6512 /*======================================================================*//**
6514 Send data in the form of a std::vector<char> to the endpoint.
6516 This method is threadsafe.
6518 @throws randolf::rex::xEBADF The underlying socket is not open
6519 @throws randolf::rex::xECONNRESET Connect reset by peer
6520 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6521 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6522 part of the user address space
6523 @throws randolf::rex::xEINTR Interrupted by a signal
6524 @throws randolf::rex::xEINVAL Invalid argument passed
6525 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6526 occur, but the POSIX sockets documentation lists it as one of the
6527 errors that can be returned, perhaps because some incorrectly
6528 implemented TCP/IP stacks return this error?)
6529 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6530 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6531 and 65,527 bytes for IPv6)
6532 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6533 network congestion (or, less commonly, insufficient memory)
6534 @throws randolf::rex::xENOMEM Insufficient memory
6535 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6536 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6537 doesn't refer to a socket
6538 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6539 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6540 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6542 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6544 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6545 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6546 but it really isn't)
6548 @returns The same rsocket object so as to facilitate stacking
6550 *///=========================================================================
6553 const std::vector<char> msg,
6559 const int posix_flags = 0) {
6560 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6562 + ", " + std::to_string(msg.size())
6563 + ", " + std::to_string(posix_flags)
6565 __send(msg.data(), msg.size(), posix_flags);
6567 }; // -x- rsocket* send -x-
6569 /*======================================================================*//**
6571 Send data in the form of a C-string to the endpoint.
6573 This method is threadsafe.
6575 @throws randolf::rex::xEBADF The underlying socket is not open
6576 @throws randolf::rex::xECONNRESET Connect reset by peer
6577 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6578 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6579 part of the user address space
6580 @throws randolf::rex::xEINTR Interrupted by a signal
6581 @throws randolf::rex::xEINVAL Invalid argument passed
6582 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6583 occur, but the POSIX sockets documentation lists it as one of the
6584 errors that can be returned, perhaps because some incorrectly
6585 implemented TCP/IP stacks return this error?)
6586 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6587 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6588 and 65,527 bytes for IPv6)
6589 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6590 network congestion (or, less commonly, insufficient memory)
6591 @throws randolf::rex::xENOMEM Insufficient memory
6592 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6593 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6594 doesn't refer to a socket
6595 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6596 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6597 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6599 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6601 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6602 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6603 but it really isn't)
6605 @returns The same rsocket object so as to facilitate stacking
6608 *///=========================================================================
6610 /// Pointer to data to send
6612 /// Number of bytes to send, or 0 to auto-detect length if message is an ASCIIZ string
6619 const int posix_flags = 0) {
6620 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6622 + ", " + std::to_string(len)
6623 + ", " + std::to_string(posix_flags)
6626 // --------------------------------------------------------------------------
6627 // Measure size of format string if an ASCIIZ string was indicated.
6628 // --------------------------------------------------------------------------
6629 if (len == 0) len = std::strlen(msg);
6631 // --------------------------------------------------------------------------
6633 // --------------------------------------------------------------------------
6634 __send(msg, len, posix_flags);
6636 }; // -x- rsocket* send -x-
6638 /*======================================================================*//**
6640 Send data in the form of an ASCIIZ string to the endpoint, including the
6641 terminating NULL character.
6643 This method is threadsafe.
6645 @throws randolf::rex::xEBADF The underlying socket is not open
6646 @throws randolf::rex::xECONNRESET Connect reset by peer
6647 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6648 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6649 part of the user address space
6650 @throws randolf::rex::xEINTR Interrupted by a signal
6651 @throws randolf::rex::xEINVAL Invalid argument passed
6652 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6653 occur, but the POSIX sockets documentation lists it as one of the
6654 errors that can be returned, perhaps because some incorrectly
6655 implemented TCP/IP stacks return this error?)
6656 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6657 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6658 and 65,527 bytes for IPv6)
6659 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6660 network congestion (or, less commonly, insufficient memory)
6661 @throws randolf::rex::xENOMEM Insufficient memory
6662 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6663 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6664 doesn't refer to a socket
6665 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6666 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6667 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6669 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6671 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6672 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6673 but it really isn't)
6675 @returns The same rsocket object so as to facilitate stacking
6676 @see recv_asciiz(const size_t, const int)
6677 @see sendz(const char*, const int) which doesn't transmit the terminating
6680 *///=========================================================================
6681 rsocket* send_asciiz(
6682 /// Pointer to data to send
6689 const int posix_flags = 0) {
6690 if (__debug) debug("send_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6692 + ", " + std::to_string(std::strlen(msg) + 1)
6693 + ", " + std::to_string(posix_flags)
6695 __send(msg, std::strlen(msg) + 1, posix_flags);
6697 }; // -x- rsocket* send_asciiz -x-
6699 /*======================================================================*//**
6701 Send one 8-bit byte (one unsigned character) of data to the endpoint.
6703 This method is threadsafe.
6705 @throws randolf::rex::xEBADF The underlying socket is not open
6706 @throws randolf::rex::xECONNRESET Connect reset by peer
6707 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6708 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6709 part of the user address space
6710 @throws randolf::rex::xEINTR Interrupted by a signal
6711 @throws randolf::rex::xEINVAL Invalid argument passed
6712 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6713 occur, but the POSIX sockets documentation lists it as one of the
6714 errors that can be returned, perhaps because some incorrectly
6715 implemented TCP/IP stacks return this error?)
6716 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6717 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6718 and 65,527 bytes for IPv6)
6719 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6720 network congestion (or, less commonly, insufficient memory)
6721 @throws randolf::rex::xENOMEM Insufficient memory
6722 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6723 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6724 doesn't refer to a socket
6725 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6726 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6727 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6729 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6731 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6732 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6733 but it really isn't)
6735 @returns The same rsocket object so as to facilitate stacking
6740 *///=========================================================================
6749 const int posix_flags = 0) {
6750 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6752 + ", " + std::to_string(sizeof(value))
6753 + ", " + std::to_string(posix_flags)
6755 __send(&value, sizeof(value), posix_flags);
6757 }; // -x- rsocket* send_byte -x-
6759 /*======================================================================*//**
6761 Send one signed character (one 8-bit byte) of data to the endpoint.
6762 @copydoc send_byte(char, int)
6763 @returns The same rsocket object so as to facilitate stacking
6768 *///=========================================================================
6777 const int posix_flags = 0) {
6778 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6780 + ", " + std::to_string(sizeof(value))
6781 + ", " + std::to_string(posix_flags)
6783 __send(&value, sizeof(value), posix_flags);
6785 }; // -x- rsocket* send_char -x-
6787 /*======================================================================*//**
6789 Send the EoL sequence to the endpoint.
6791 This method is threadsafe.
6793 @throws randolf::rex::xEBADF The underlying socket is not open
6794 @throws randolf::rex::xECONNRESET Connect reset by peer
6795 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6796 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6797 part of the user address space
6798 @throws randolf::rex::xEINTR Interrupted by a signal
6799 @throws randolf::rex::xEINVAL Invalid argument passed
6800 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6801 occur, but the POSIX sockets documentation lists it as one of the
6802 errors that can be returned, perhaps because some incorrectly
6803 implemented TCP/IP stacks return this error?)
6804 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6805 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6806 and 65,527 bytes for IPv6)
6807 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6808 network congestion (or, less commonly, insufficient memory)
6809 @throws randolf::rex::xENOMEM Insufficient memory
6810 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6811 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6812 doesn't refer to a socket
6813 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6814 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6815 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6817 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6819 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6820 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6821 but it really isn't)
6823 @returns The same rsocket object so as to facilitate stacking
6825 *///=========================================================================
6832 const int posix_flags = 0) {
6833 if (__debug) debug("send_eol(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6834 + ", " + std::to_string(posix_flags)
6836 __send_eol(posix_flags);
6838 }; // -x- rsocket* send_eol -x-
6840 /*======================================================================*//**
6842 Send a data structure to the endpoint.
6844 MSB/LSB considerations are important for any integers within your structure,
6845 so be sure to sanitize them with htons(), ntohs(), and related methods prior
6846 to sending, and then after receiving. This way, your data will be protected
6847 against corruption resulting from byte order differences when communicating
6848 between hardware architectures that differ in MSB and LSB byte ordering.
6850 This method is threadsafe.
6852 @throws randolf::rex::xEBADF The underlying socket is not open
6853 @throws randolf::rex::xECONNRESET Connect reset by peer
6854 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6855 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6856 part of the user address space
6857 @throws randolf::rex::xEINTR Interrupted by a signal
6858 @throws randolf::rex::xEINVAL Invalid argument passed
6859 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6860 occur, but the POSIX sockets documentation lists it as one of the
6861 errors that can be returned, perhaps because some incorrectly
6862 implemented TCP/IP stacks return this error?)
6863 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6864 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6865 and 65,527 bytes for IPv6)
6866 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6867 network congestion (or, less commonly, insufficient memory)
6868 @throws randolf::rex::xENOMEM Insufficient memory
6869 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6870 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6871 doesn't refer to a socket
6872 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6873 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6874 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6876 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6878 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6879 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6880 but it really isn't)
6882 @returns The same rsocket object so as to facilitate stacking
6885 *///=========================================================================
6886 template <typename T> rsocket* send_struct(
6894 const int posix_flags = 0) {
6895 if (__debug) debug("send_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6897 + ", " + std::to_string(sizeof(value))
6898 + ", " + std::to_string(posix_flags)
6900 __send(&value, sizeof(value), posix_flags);
6902 }; // -x- rsocket* send_struct -x-
6904 /*======================================================================*//**
6906 Send one 16-bit unsigned integer of data in LSB (little endian) order to the
6909 This method is threadsafe.
6911 @throws randolf::rex::xEBADF The underlying socket is not open
6912 @throws randolf::rex::xECONNRESET Connect reset by peer
6913 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6914 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6915 part of the user address space
6916 @throws randolf::rex::xEINTR Interrupted by a signal
6917 @throws randolf::rex::xEINVAL Invalid argument passed
6918 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6919 occur, but the POSIX sockets documentation lists it as one of the
6920 errors that can be returned, perhaps because some incorrectly
6921 implemented TCP/IP stacks return this error?)
6922 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6923 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6924 and 65,527 bytes for IPv6)
6925 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6926 network congestion (or, less commonly, insufficient memory)
6927 @throws randolf::rex::xENOMEM Insufficient memory
6928 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6929 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6930 doesn't refer to a socket
6931 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6932 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6933 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6935 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6937 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6938 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6939 but it really isn't)
6941 @returns The same rsocket object so as to facilitate stacking
6943 *///=========================================================================
6944 rsocket* send_uint16_lsb(
6946 const uint16_t value,
6952 const int posix_flags = 0) {
6953 uint16_t buf = !__endian_is_msb ? value : ntohs(value);
6954 if (__debug) debug("send_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6955 + ", " + std::to_string(value)
6956 + ", " + std::to_string(sizeof(buf))
6957 + ", " + std::to_string(posix_flags)
6959 __send(&buf, sizeof(buf), posix_flags);
6961 }; // -x- rsocket* send_uint16_lsb -x-
6963 /*======================================================================*//**
6965 Send one 16-bit integer of data in MSB (big endian) order to the endpoint.
6967 This method is threadsafe.
6969 @throws randolf::rex::xEBADF The underlying socket is not open
6970 @throws randolf::rex::xECONNRESET Connect reset by peer
6971 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6972 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6973 part of the user address space
6974 @throws randolf::rex::xEINTR Interrupted by a signal
6975 @throws randolf::rex::xEINVAL Invalid argument passed
6976 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6977 occur, but the POSIX sockets documentation lists it as one of the
6978 errors that can be returned, perhaps because some incorrectly
6979 implemented TCP/IP stacks return this error?)
6980 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6981 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6982 and 65,527 bytes for IPv6)
6983 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6984 network congestion (or, less commonly, insufficient memory)
6985 @throws randolf::rex::xENOMEM Insufficient memory
6986 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6987 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6988 doesn't refer to a socket
6989 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6990 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6991 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6993 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6995 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6996 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6997 but it really isn't)
6999 @returns The same rsocket object so as to facilitate stacking
7001 *///=========================================================================
7002 rsocket* send_uint16_msb(
7004 const uint16_t value,
7010 const int posix_flags = 0) {
7011 int16_t buf = __endian_is_msb ? value : htons(value);
7012 if (__debug) debug("send_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7013 + ", " + std::to_string(value)
7014 + ", " + std::to_string(sizeof(buf))
7015 + ", " + std::to_string(posix_flags)
7017 __send(&buf, sizeof(buf), posix_flags);
7019 }; // -x- rsocket* send_int16_msb -x-
7021 /*======================================================================*//**
7023 Send one 32-bit unsigned integer of data in LSB (little endian) order to the
7026 This method is threadsafe.
7028 @throws randolf::rex::xEBADF The underlying socket is not open
7029 @throws randolf::rex::xECONNRESET Connect reset by peer
7030 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7031 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7032 part of the user address space
7033 @throws randolf::rex::xEINTR Interrupted by a signal
7034 @throws randolf::rex::xEINVAL Invalid argument passed
7035 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7036 occur, but the POSIX sockets documentation lists it as one of the
7037 errors that can be returned, perhaps because some incorrectly
7038 implemented TCP/IP stacks return this error?)
7039 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7040 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7041 and 65,527 bytes for IPv6)
7042 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7043 network congestion (or, less commonly, insufficient memory)
7044 @throws randolf::rex::xENOMEM Insufficient memory
7045 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7046 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7047 doesn't refer to a socket
7048 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7049 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7050 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7052 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7054 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7055 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7056 but it really isn't)
7058 @returns The same rsocket object so as to facilitate stacking
7060 *///=========================================================================
7061 rsocket* send_uint32_lsb(
7063 const uint32_t value,
7069 const int posix_flags = 0) {
7070 uint32_t buf = !__endian_is_msb ? value : ntohl(value);
7071 if (__debug) debug("send_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7072 + ", " + std::to_string(value)
7073 + ", " + std::to_string(sizeof(buf))
7074 + ", " + std::to_string(posix_flags)
7076 __send(&buf, sizeof(buf), posix_flags);
7078 }; // -x- rsocket* send_uint32_lsb -x-
7080 /*======================================================================*//**
7082 Send one 32-bit unsigned integer of data in MSB (big endian) order to the
7085 This method is threadsafe.
7087 @throws randolf::rex::xEBADF The underlying socket is not open
7088 @throws randolf::rex::xECONNRESET Connect reset by peer
7089 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7090 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7091 part of the user address space
7092 @throws randolf::rex::xEINTR Interrupted by a signal
7093 @throws randolf::rex::xEINVAL Invalid argument passed
7094 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7095 occur, but the POSIX sockets documentation lists it as one of the
7096 errors that can be returned, perhaps because some incorrectly
7097 implemented TCP/IP stacks return this error?)
7098 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7099 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7100 and 65,527 bytes for IPv6)
7101 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7102 network congestion (or, less commonly, insufficient memory)
7103 @throws randolf::rex::xENOMEM Insufficient memory
7104 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7105 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7106 doesn't refer to a socket
7107 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7108 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7109 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7111 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7113 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7114 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7115 but it really isn't)
7117 @returns The same rsocket object so as to facilitate stacking
7119 *///=========================================================================
7120 rsocket* send_uint32_msb(
7122 const uint32_t value,
7128 const int posix_flags = 0) {
7129 uint32_t buf = __endian_is_msb ? value : htonl(value);
7130 if (__debug) debug("send_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7131 + ", " + std::to_string(value)
7132 + ", " + std::to_string(sizeof(buf))
7133 + ", " + std::to_string(posix_flags)
7135 __send(&buf, sizeof(buf), posix_flags);
7137 }; // -x- rsocket* send_uint32_msb -x-
7139 /*======================================================================*//**
7141 Send one 64-bit unsigned integer of data in LSB (little endian) order to the
7144 This method is threadsafe.
7146 @throws randolf::rex::xEBADF The underlying socket is not open
7147 @throws randolf::rex::xECONNRESET Connect reset by peer
7148 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7149 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7150 part of the user address space
7151 @throws randolf::rex::xEINTR Interrupted by a signal
7152 @throws randolf::rex::xEINVAL Invalid argument passed
7153 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7154 occur, but the POSIX sockets documentation lists it as one of the
7155 errors that can be returned, perhaps because some incorrectly
7156 implemented TCP/IP stacks return this error?)
7157 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7158 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7159 and 65,527 bytes for IPv6)
7160 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7161 network congestion (or, less commonly, insufficient memory)
7162 @throws randolf::rex::xENOMEM Insufficient memory
7163 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7164 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7165 doesn't refer to a socket
7166 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7167 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7168 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7170 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7172 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7173 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7174 but it really isn't)
7176 @returns The same rsocket object so as to facilitate stacking
7178 *///=========================================================================
7179 rsocket* send_uint64_lsb(
7181 const uint64_t value,
7187 const int posix_flags = 0) {
7188 uint64_t buf = !__endian_is_msb ? value : ((((uint64_t)ntohl((value) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((value) >> 32)));
7189 if (__debug) debug("send_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7190 + ", " + std::to_string(value)
7191 + ", " + std::to_string(sizeof(buf))
7192 + ", " + std::to_string(posix_flags)
7194 __send(&buf, sizeof(buf), posix_flags);
7196 }; // -x- rsocket* send_uint64_lsb -x-
7198 /*======================================================================*//**
7200 Send one 64-bit unsigned integer of data in MSB (big endian) order to the
7203 This method is threadsafe.
7205 @throws randolf::rex::xEBADF The underlying socket is not open
7206 @throws randolf::rex::xECONNRESET Connect reset by peer
7207 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7208 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7209 part of the user address space
7210 @throws randolf::rex::xEINTR Interrupted by a signal
7211 @throws randolf::rex::xEINVAL Invalid argument passed
7212 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7213 occur, but the POSIX sockets documentation lists it as one of the
7214 errors that can be returned, perhaps because some incorrectly
7215 implemented TCP/IP stacks return this error?)
7216 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7217 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7218 and 65,527 bytes for IPv6)
7219 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7220 network congestion (or, less commonly, insufficient memory)
7221 @throws randolf::rex::xENOMEM Insufficient memory
7222 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7223 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7224 doesn't refer to a socket
7225 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7226 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7227 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7229 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7231 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7232 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7233 but it really isn't)
7235 @returns The same rsocket object so as to facilitate stacking
7237 *///=========================================================================
7238 rsocket* send_uint64_msb(
7240 const uint64_t value,
7246 const int posix_flags = 0) {
7247 uint64_t buf = __endian_is_msb ? value : ((((uint64_t)htonl((value) & 0xffffffffUL)) << 32) | htonl((uint32_t)((value) >> 32)));
7248 if (__debug) debug("send_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7249 + ", " + std::to_string(value)
7250 + ", " + std::to_string(sizeof(buf))
7251 + ", " + std::to_string(posix_flags)
7253 __send(&buf, sizeof(buf), posix_flags);
7255 }; // -x- rsocket* send_uint64_msb -x-
7257 /*======================================================================*//**
7259 Send data in the form of a std::string to the endpoint, with an EoL sequence
7262 This method is threadsafe.
7264 @throws randolf::rex::xEBADF The underlying socket is not open
7265 @throws randolf::rex::xECONNRESET Connect reset by peer
7266 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7267 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7268 part of the user address space
7269 @throws randolf::rex::xEINTR Interrupted by a signal
7270 @throws randolf::rex::xEINVAL Invalid argument passed
7271 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7272 occur, but the POSIX sockets documentation lists it as one of the
7273 errors that can be returned, perhaps because some incorrectly
7274 implemented TCP/IP stacks return this error?)
7275 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7276 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7277 and 65,527 bytes for IPv6)
7278 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7279 network congestion (or, less commonly, insufficient memory)
7280 @throws randolf::rex::xENOMEM Insufficient memory
7281 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7282 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7283 doesn't refer to a socket
7284 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7285 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7286 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7288 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7290 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7291 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7292 but it really isn't)
7294 @returns The same rsocket object so as to facilitate stacking
7297 @see recvline(const size_t, const int)
7300 *///=========================================================================
7303 const std::string msg = std::string(),
7309 const int posix_flags = 0) {
7310 if (__debug) debug("sendline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7312 + ", " + std::to_string(msg.length())
7313 + "+" + std::to_string(__eol_out.length())
7314 + ", " + std::to_string(posix_flags)
7316 __sendline(msg.c_str(), msg.length(), posix_flags);
7318 }; // -x- rsocket* sendline -x-
7320 /*======================================================================*//**
7322 Send data in the form of a "msghdr" structure to a specific endpoint.
7324 This method is not compatible with TLS.
7326 This method is threadsafe.
7328 @throws randolf::rex::xEBADF The underlying socket is not open
7329 @throws randolf::rex::xECONNRESET Connect reset by peer
7330 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7331 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7332 part of the user address space
7333 @throws randolf::rex::xEINTR Interrupted by a signal
7334 @throws randolf::rex::xEINVAL Invalid argument passed
7335 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7336 occur, but the POSIX sockets documentation lists it as one of the
7337 errors that can be returned, perhaps because some incorrectly
7338 implemented TCP/IP stacks return this error?)
7339 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7340 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7341 and 65,527 bytes for IPv6)
7342 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7343 network congestion (or, less commonly, insufficient memory)
7344 @throws randolf::rex::xENOMEM Insufficient memory
7345 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7346 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7347 doesn't refer to a socket
7348 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7349 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7350 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7352 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7354 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7355 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7356 but it really isn't)
7358 @returns The same rsocket object so as to facilitate stacking
7360 *///=========================================================================
7362 /// Pointer to data to send
7363 const struct msghdr* msg,
7369 const int posix_flags = 0) {
7370 if (__debug) debug("sendmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7372 + ", " + std::to_string(posix_flags)
7374 __track_bytes_tx(__rc_check(::sendmsg(__socket_fd, msg, posix_flags)));
7376 }; // -x- rsocket* sendmsg -x-
7378 /*======================================================================*//**
7380 Send data in the form of a "mmsghdr" structure to a specific endpoint.
7382 This method is not compatible with TLS.
7384 This method is threadsafe.
7386 @throws randolf::rex::xEBADF The underlying socket is not open
7387 @throws randolf::rex::xECONNRESET Connect reset by peer
7388 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7389 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7390 part of the user address space
7391 @throws randolf::rex::xEINTR Interrupted by a signal
7392 @throws randolf::rex::xEINVAL Invalid argument passed
7393 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7394 occur, but the POSIX sockets documentation lists it as one of the
7395 errors that can be returned, perhaps because some incorrectly
7396 implemented TCP/IP stacks return this error?)
7397 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7398 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7399 and 65,527 bytes for IPv6)
7400 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7401 network congestion (or, less commonly, insufficient memory)
7402 @throws randolf::rex::xENOMEM Insufficient memory
7403 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7404 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7405 doesn't refer to a socket
7406 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7407 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7408 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7410 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7412 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7413 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7414 but it really isn't)
7416 @returns The same rsocket object so as to facilitate stacking
7418 *///=========================================================================
7420 /// Pointer to data to send
7421 struct mmsghdr* mmsg,
7422 /// Size of target endpoint structure
7423 const unsigned int vlen = sizeof(mmsghdr),
7429 const int posix_flags = 0) {
7430 if (__debug) debug("sendmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7432 + ", " + std::to_string(vlen)
7433 + ", " + std::to_string(posix_flags)
7435 __track_bytes_tx(__rc_check(::sendmmsg(__socket_fd, mmsg, vlen, posix_flags)));
7437 }; // -x- rsocket* sendmsg -x-
7439 /*======================================================================*//**
7441 Send data in the form of a std::string to a specific endpoint.
7443 This method is not compatible with TLS.
7445 This method is threadsafe.
7447 @throws randolf::rex::xEBADF The underlying socket is not open
7448 @throws randolf::rex::xECONNRESET Connect reset by peer
7449 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7450 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7451 part of the user address space
7452 @throws randolf::rex::xEINTR Interrupted by a signal
7453 @throws randolf::rex::xEINVAL Invalid argument passed
7454 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7455 occur, but the POSIX sockets documentation lists it as one of the
7456 errors that can be returned, perhaps because some incorrectly
7457 implemented TCP/IP stacks return this error?)
7458 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7459 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7460 and 65,527 bytes for IPv6)
7461 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7462 network congestion (or, less commonly, insufficient memory)
7463 @throws randolf::rex::xENOMEM Insufficient memory
7464 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7465 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7466 doesn't refer to a socket
7467 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7468 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7469 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7471 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7473 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7474 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7475 but it really isn't)
7477 @returns The same rsocket object so as to facilitate stacking
7479 *///=========================================================================
7482 const std::string msg,
7488 const int posix_flags,
7489 /// Target endpoint address structure
7490 const struct sockaddr *to,
7491 /// Size of target endpoint structure
7492 socklen_t tolen = sizeof(sockaddr)) {
7493 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7495 + ", " + std::to_string(posix_flags)
7497 + ", " + std::to_string(tolen)
7499 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg.c_str(), msg.length(), posix_flags, to, tolen)));
7501 }; // -x- rsocket* sendto -x- // TODO: Create easier-to-use variants
7503 /*======================================================================*//**
7505 Send data in the form of a C-string to a specific endpoint.
7507 This method is not compatible with TLS.
7509 This method is threadsafe.
7511 @throws randolf::rex::xEBADF The underlying socket is not open
7512 @throws randolf::rex::xECONNRESET Connect reset by peer
7513 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7514 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7515 part of the user address space
7516 @throws randolf::rex::xEINTR Interrupted by a signal
7517 @throws randolf::rex::xEINVAL Invalid argument passed
7518 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7519 occur, but the POSIX sockets documentation lists it as one of the
7520 errors that can be returned, perhaps because some incorrectly
7521 implemented TCP/IP stacks return this error?)
7522 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7523 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7524 and 65,527 bytes for IPv6)
7525 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7526 network congestion (or, less commonly, insufficient memory)
7527 @throws randolf::rex::xENOMEM Insufficient memory
7528 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7529 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7530 doesn't refer to a socket
7531 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7532 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7533 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7535 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7537 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7538 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7539 but it really isn't)
7541 @returns The same rsocket object so as to facilitate stacking
7543 *///=========================================================================
7545 /// Pointer to data to send
7547 /// Number of bytes to send
7554 const int posix_flags,
7555 /// Target endpoint address structure
7556 const struct sockaddr *to,
7557 /// Size of target endpoint structure
7558 socklen_t tolen = sizeof(sockaddr)) {
7559 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7561 + ", " + std::to_string(std::strlen(msg))
7562 + ", " + std::to_string(posix_flags)
7564 + ", " + std::to_string(tolen)
7566 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, len, posix_flags, to, tolen)));
7568 }; // -x- rsocket* sendto -x-
7570 /*======================================================================*//**
7572 Send data in the form of an ASCIIZ string to the endpoint. The terminating
7573 NULL character won't be transmitted.
7575 This method is threadsafe.
7577 @throws randolf::rex::xEBADF The underlying socket is not open
7578 @throws randolf::rex::xECONNRESET Connect reset by peer
7579 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7580 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7581 part of the user address space
7582 @throws randolf::rex::xEINTR Interrupted by a signal
7583 @throws randolf::rex::xEINVAL Invalid argument passed
7584 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7585 occur, but the POSIX sockets documentation lists it as one of the
7586 errors that can be returned, perhaps because some incorrectly
7587 implemented TCP/IP stacks return this error?)
7588 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7589 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7590 and 65,527 bytes for IPv6)
7591 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7592 network congestion (or, less commonly, insufficient memory)
7593 @throws randolf::rex::xENOMEM Insufficient memory
7594 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7595 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7596 doesn't refer to a socket
7597 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7598 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7599 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7601 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7603 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7604 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7605 but it really isn't)
7607 @returns The same rsocket object so as to facilitate stacking
7608 @see recvz(const size_t, const int)
7609 @see send_asciiz(const char*, const int) which also transmits the terminating
7612 *///=========================================================================
7614 /// Pointer to data to send
7621 const int posix_flags = 0) {
7622 if (__debug) debug("sendz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7624 + ", " + std::to_string(std::strlen(msg))
7625 + ", " + std::to_string(posix_flags)
7627 __send(msg, std::strlen(msg), posix_flags);
7629 }; // -x- rsocket* sendz -x-
7631 /*======================================================================*//**
7633 Send data in the form of an ASCIIZ string to a specific endpoint. The
7634 terminating NULL character won't be transmitted.
7636 This method is not compatible with TLS.
7638 This method is threadsafe.
7640 @throws randolf::rex::xEBADF The underlying socket is not open
7641 @throws randolf::rex::xECONNRESET Connect reset by peer
7642 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7643 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7644 part of the user address space
7645 @throws randolf::rex::xEINTR Interrupted by a signal
7646 @throws randolf::rex::xEINVAL Invalid argument passed
7647 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7648 occur, but the POSIX sockets documentation lists it as one of the
7649 errors that can be returned, perhaps because some incorrectly
7650 implemented TCP/IP stacks return this error?)
7651 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7652 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7653 and 65,527 bytes for IPv6)
7654 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7655 network congestion (or, less commonly, insufficient memory)
7656 @throws randolf::rex::xENOMEM Insufficient memory
7657 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7658 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7659 doesn't refer to a socket
7660 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7661 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7662 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7664 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7666 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7667 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7668 but it really isn't)
7670 @returns The same rsocket object so as to facilitate stacking
7671 *///=========================================================================
7673 /// Pointer to data to send
7680 const int posix_flags,
7681 /// Target endpoint address structure
7682 const struct sockaddr *to,
7683 /// Size of target endpoint structure
7684 socklen_t tolen = sizeof(sockaddr)) {
7685 if (__debug) debug("sendzto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7687 + ", " + std::to_string(std::strlen(msg))
7688 + ", " + std::to_string(posix_flags)
7690 + ", " + std::to_string(tolen)
7692 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, std::strlen(msg), posix_flags, to, tolen)));
7694 }; // -x- rsocket* sendzto -x-
7696 /*======================================================================*//**
7698 Set socket option to the specific integer.
7701 These setsockopt() methods take an integer or character value directly, or a
7702 pointer to a structure, and then rsocket handles the remaining tedious
7703 technical details behind-the-scenes for you when calling the underlying
7704 socket's setsockopt() function.
7706 @throws randolf::rex::xEBADF The underlying socket is not open
7707 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7708 part of the user address space
7709 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7710 valid for this socket's family (a.k.a., communication domain)
7711 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7713 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7714 doesn't refer to a socket
7716 @returns The same rsocket object so as to facilitate stacking
7719 *///=========================================================================
7720 rsocket* setsockopt(
7721 /// The level at which the option resides; typically @c SOL_SOCKET
7723 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7725 /// The value that this socket option will be set to
7727 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
7728 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7730 }; // -x- rsocket* setsockopt -x-
7732 /*======================================================================*//**
7734 Set socket option to the specific unsigned integer.
7735 @copydetails setsockopt(const int, const int, const int)
7738 For any values that require a u_int, you'll need to explicitly cast this type
7739 when specifying the value directly; for example: (u_int)32768
7741 @throws randolf::rex::xEBADF The underlying socket is not open
7742 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7743 part of the user address space
7744 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7745 valid for this socket's family (a.k.a., communication domain)
7746 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7748 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7749 doesn't refer to a socket
7751 @returns The same rsocket object so as to facilitate stacking
7753 *///=========================================================================
7754 rsocket* setsockopt(
7755 /// The level at which the option resides; typically @c SOL_SOCKET
7757 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7759 /// The value that this socket option will be set to
7760 const u_int value) {
7761 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
7762 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7764 }; // -x- rsocket* setsockopt -x-
7766 /*======================================================================*//**
7768 Set socket option to the specific unsigned character.
7769 @copydetails setsockopt(const int, const int, const int)
7771 *///=========================================================================
7772 rsocket* setsockopt(
7773 /// The level at which the option resides; typically @c SOL_SOCKET
7775 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7777 /// The value that this socket option will be set to
7778 const u_char value) {
7779 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
7780 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7782 }; // -x- rsocket* setsockopt -x-
7784 /*======================================================================*//**
7786 Set socket option to the specific structure.
7787 @copydetails setsockopt(const int, const int, const int)
7789 *///=========================================================================
7790 rsocket* setsockopt(
7791 /// The level at which the option resides; typically @c SOL_SOCKET
7793 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7795 /// The structure that this socket option will be set to
7796 const linger& value) {
7797 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7798 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7800 }; // -x- rsocket* setsockopt -x-
7802 /*======================================================================*//**
7804 Set socket option to the specific structure.
7805 @copydetails setsockopt(const int, const int, const linger&)
7807 *///=========================================================================
7808 rsocket* setsockopt(
7809 /// The level at which the option resides; typically @c SOL_SOCKET
7811 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7813 /// The structure that this socket option will be set to
7814 const timeval& value) {
7815 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7816 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7818 }; // -x- rsocket* setsockopt -x-
7820 /*======================================================================*//**
7822 Set socket option to the specific structure.
7823 @copydetails setsockopt(const int, const int, const linger&)
7825 *///=========================================================================
7826 rsocket* setsockopt(
7827 /// The level at which the option resides; typically @c SOL_SOCKET
7829 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7831 /// The structure that this socket option will be set to
7832 const in_addr& value) {
7833 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7834 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7836 }; // -x- rsocket* setsockopt -x-
7838 /*======================================================================*//**
7840 Set socket option to the specific structure.
7841 @copydetails setsockopt(const int, const int, const linger&)
7843 *///=========================================================================
7844 rsocket* setsockopt(
7845 /// The level at which the option resides; typically @c SOL_SOCKET
7847 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7849 /// The structure that this socket option will be set to
7850 const ip_mreq& value) {
7851 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7852 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7854 }; // -x- rsocket* setsockopt -x-
7856 /*======================================================================*//**
7858 Set socket option to the specific structure.
7859 @copydetails setsockopt(const int, const int, const linger&)
7861 *///=========================================================================
7862 rsocket* setsockopt(
7863 /// The level at which the option resides; typically @c SOL_SOCKET
7865 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7867 /// The structure that this socket option will be set to
7868 const ip_mreq_source& value) {
7869 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7870 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7872 }; // -x- rsocket* setsockopt -x-
7874 /*======================================================================*//**
7876 Set socket option to the specific structure.
7877 @copydetails setsockopt(const int, const int, const linger&)
7879 *///=========================================================================
7880 rsocket* setsockopt(
7881 /// The level at which the option resides; typically @c SOL_SOCKET
7883 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7885 /// The structure that this socket option will be set to
7886 const icmp6_filter& value) {
7887 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7888 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7890 }; // -x- rsocket* setsockopt -x-
7892 /*======================================================================*//**
7894 Set socket option to the specific structure.
7895 @copydetails setsockopt(const int, const int, const linger&)
7897 *///=========================================================================
7898 rsocket* setsockopt(
7899 /// The level at which the option resides; typically @c SOL_SOCKET
7901 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7903 /// The structure that this socket option will be set to
7904 const sockaddr_in6& value) {
7905 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7906 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7908 }; // -x- rsocket* setsockopt -x-
7910 /*======================================================================*//**
7912 Set socket option to the specific structure.
7913 @copydetails setsockopt(const int, const int, const linger&)
7915 *///=========================================================================
7916 rsocket* setsockopt(
7917 /// The level at which the option resides; typically @c SOL_SOCKET
7919 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7921 /// The structure that this socket option will be set to
7922 const ip6_mtuinfo& value) {
7923 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7924 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7926 }; // -x- rsocket* setsockopt -x-
7928 /*======================================================================*//**
7930 Set socket option to the specific structure.
7931 @copydetails setsockopt(const int, const int, const linger&)
7933 *///=========================================================================
7934 rsocket* setsockopt(
7935 /// The level at which the option resides; typically @c SOL_SOCKET
7937 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7939 /// The structure that this socket option will be set to
7940 const ipv6_mreq& value) {
7941 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7942 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7944 }; // -x- rsocket* setsockopt -x-
7946 /*======================================================================*//**
7948 Set socket option to the specific structure.
7949 @copydetails setsockopt(const int, const int, const linger&)
7951 *///=========================================================================
7952 rsocket* setsockopt(
7953 /// The level at which the option resides; typically @c SOL_SOCKET
7955 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7957 /// The structure that this socket option will be set to
7958 const group_req& value) {
7959 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7960 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7962 }; // -x- rsocket* setsockopt -x-
7964 /*======================================================================*//**
7966 Set socket option to the specific structure.
7967 @copydetails setsockopt(const int, const int, const linger&)
7969 *///=========================================================================
7970 rsocket* setsockopt(
7971 /// The level at which the option resides; typically @c SOL_SOCKET
7973 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7975 /// The structure that this socket option will be set to
7976 const group_source_req& value) {
7977 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7978 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7980 }; // -x- rsocket* setsockopt -x-
7982 /*======================================================================*//**
7984 Shut down the underlying socket, partially or fully.
7986 <div style=padding-left:32px;>
7989 <td valign=top>SHUT_RD:</td>
7990 <td>Further receives will be disallowed.</td>
7993 <td valign=top>SHUT_WR:</td>
7994 <td>Further sends will be disallowed (this may cause actions specific
7995 to the protocol family of the socket to occur).</td>
7998 <td valign=top>SHUT_RDWR:</td>
7999 <td>Further sends and receives will be disallowed (default).</td>
8004 @throws randolf::rex::xEBADF The underlying socket is not open
8005 @throws randolf::rex::xEINVAL Invalid argument passed
8006 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8007 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8008 doesn't refer to a socket
8010 @returns The same rsocket object so as to facilitate stacking
8013 *///=========================================================================
8017 /// SHUT_RDWR (default)
8018 const int how = SHUT_RDWR,
8019 /// Shutdown the TLS connection too (but only if it's alrady enabled), which
8020 /// is the default (disabling this will usually result in errors for the
8021 /// endpoint upon raw socket shutdown)
8022 const bool tls_shutdown = true) {
8023 if (__debug) debug("shutdown(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8024 + ", " + std::to_string(how)
8027 // --------------------------------------------------------------------------
8028 // Shut down TLS connections first, if TLS is enabled.
8030 // If SSL_shutdown returns a 0, then it means a "bidirectional shutdown" is
8031 // needed which entails calling SSL_shutdown a second time. The error codes
8032 // returned by SSL_shutdown are as follows:
8034 // 1 = Shutdown completed normally
8036 // 0 = Shutdown is not yet finished, and a second SSL_shutdown() is needed
8038 // -1 = Critical error, probably due to a connection problem, or because
8039 // the endpoint already initiated a shutdown, etc.
8041 // We don't really care what the second SSL_shutdown's return code is because
8042 // there's nothing we can do about it whatever it is. This situation is rare
8043 // though, and what follows next closes the raw socket, which is OpenSSL's
8044 // lifeline to communicating with the endpoint.
8045 // --------------------------------------------------------------------------
8046 if (__tls && tls_shutdown) {
8047 if (SSL_shutdown(__tls_fd) == 0) {
8048 SSL_shutdown(__tls_fd);
8049 } // -x- if SSL_shutdown -x-
8050 } // -x- if __tls -x-
8052 // --------------------------------------------------------------------------
8053 // Shut down the raw socket.
8054 // --------------------------------------------------------------------------
8055 __rc_check(::shutdown(__socket_fd, how));
8056 //__socket_connected = false; // TODO: Figure out when to change this to false
8058 }; // -x- rsocket* shutdown -x-
8060 /*======================================================================*//**
8062 Complete the configuration of an rsocket that was previously initialized
8063 without any parameters (a.k.a., an "empty rsocket").
8064 @copydetails rsocket()
8065 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
8066 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
8067 @throws randolf::rex::xEALREADY If this socket() method was already used, or
8068 it was used after rsocket() initialized with at least one parameter
8069 @throws randolf::rex::xEINVAL Protocal family invalid or not available
8070 @throws randolf::rex::xEINVAL Invalid flags in type
8071 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
8072 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
8073 @throws randolf::rex::xENOBUFS Insufficient memory
8074 @throws randolf::rex::xENOMEM Insufficient memory
8075 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
8076 supported within the specified family (a.k.a., communication domain)
8077 @returns The same rsocket object so as to facilitate stacking
8079 @see socket_family()
8081 @see socket_protocol()
8085 *///=========================================================================
8087 /// Communication domain; usually one of:@n
8088 /// AF_INET (IPv4)@n
8089 /// AF_INET6 (IPv6)@n
8090 /// AF_UNIX (UNIX domain sockets)
8092 /// Communication semantics; usually one of:@n
8093 /// SOCK_STREAM (common for TCP)@n
8094 /// SOCK_DGRAM (common for UDP)
8095 const int type = SOCK_STREAM,
8096 /// Network protocol; usually one of:@n
8100 /// PF_UNSPEC (auto-detect)
8101 const int protocol = PF_UNSPEC) {
8102 __socket(family, type, protocol);
8104 }; // -x- rsocket* socket -x-
8106 /*======================================================================*//**
8108 Get underlying socket family/domain constant (SO_DOMAIN).
8109 @returns socket family/domain constant
8113 @see socket_protocol()
8116 *///=========================================================================
8117 const int socket_family() noexcept { return __socket_addr.ss_family; }; // -x- int socket_family -x-
8119 /*======================================================================*//**
8121 Get underlying socket descriptor/handle.
8122 @returns socket descriptor/handle
8123 @returns 0 = socket not yet allocated
8126 @see socket_family()
8127 @see socket_protocol()
8130 *///=========================================================================
8131 const int socket_fd() noexcept { return __socket_fd; }; // -x- int socket_fd -x-
8133 /*======================================================================*//**
8135 Set underlying socket descriptor/handle (to one that is presumed to be open).
8137 This method is only available while an underlying socket has not been created
8138 or previously assigned, such as after an empty @ref rsocket instantiation.
8139 @throws randolf::rex::xEALREADY If this socket_fd() method was already used,
8140 or it was used after socket() initialized it, or if rsocket() had
8141 initialized with at least one parameter that resulted in the creation
8142 of an underlying socket
8143 @returns The same rsocket object so as to facilitate stacking
8145 @see socket_family()
8146 @see socket_protocol()
8149 *///=========================================================================
8151 /// New socket descriptor/handle
8152 const int new_socket_fd) {
8153 if (__debug) debug("socket_fd(socket{0x" + randolf::rtools::to_hex(new_socket_fd) + "}"
8155 if (__socket_fd != 0) throw randolf::rex::xEALREADY("EALREADY");
8156 __socket_fd = new_socket_fd;
8157 __socket_addr.ss_family = getsockopt_int(SOL_SOCKET, SO_DOMAIN);
8158 __socket_type = getsockopt_int(SOL_SOCKET, SO_TYPE);
8159 __socket_protocol = getsockopt_int(SOL_SOCKET, SO_PROTOCOL);
8160 __socket_open = true;
8162 }; // -x- rsocket* socket_fd -x-
8164 /*======================================================================*//**
8166 Get underlying socket protocol constant (SO_PROTOCOL).
8167 @returns socket protocol constant
8170 @see socket_family()
8174 *///=========================================================================
8175 const int socket_protocol() noexcept { return __socket_protocol; }; // -x- int socket_protocol -x-
8177 /*======================================================================*//**
8179 Get underlying socket type constant (SO_TYPE).
8180 @returns socket type constant
8183 @see socket_family()
8185 @see socket_protocol()
8187 *///=========================================================================
8188 const int socket_type() noexcept { return __socket_type; }; // -x- int socket_type -x-
8190 /*======================================================================*//**
8192 Find out whether the underlying socket is at the out-of-band (OOB) mark.
8194 @throws randolf::rex::xEBADF The underlying socket is not open
8195 @throws randolf::rex::xEINVAL The underlying socket file descriptor is not a
8196 type to which @ref sockatmark() can be applied
8198 @returns TRUE = at OOB mark
8199 @returns FALSE = not at OOB mark
8202 *///=========================================================================
8203 const bool sockatmark() {
8204 return __rc_check(::sockatmark(__socket_fd)) ? true : false;
8205 }; // -x- bool sockatmark -x-
8207 /*======================================================================*//**
8209 Find out what the read timeout is set to on the current socket.
8211 Since getting the read timeout is such a common operation, this specialized
8212 method was created to ease software development efforts; internally we're
8213 just calling getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO).
8215 @throws randolf::rex::xEBADF The underlying socket is not open
8216 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8217 part of the user address space
8218 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
8219 valid for this socket's family (a.k.a., communication domain)
8220 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
8222 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8223 doesn't refer to a socket
8225 @returns @c timeval socket option structure wrapped in std::shared_ptr
8227 *///=========================================================================
8228 std::shared_ptr<timeval> timeout() {
8229 return getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO);
8230 }; // -x- std::shared_ptr<timeval> timeout -x-
8232 /*======================================================================*//**
8234 Set the recv timeout on the current socket.
8236 Since setting the read timeout is such a common operation, this specialized
8237 method was created to ease software development efforts; internally we're
8238 just calling setsockopt(SOL_SOCKET, SO_RCVTIMEO, timeval).
8240 Although a timeout of 100,000 microseconds (1/10 of one second) may suffice
8241 in healthy and efficient networks, a more conservative setting of 1 second
8242 tends to minimally yield more reliable results. Many end-user applications
8243 use defaults of 3 to 5 seconds to cover a wider variety of unpredictable
8244 network connections (such as over shared wireless connections that are slow),
8245 and this setting should ultimately be configurable by users/administrators.
8248 The default timeout for new sockets is normally 0 (no timeout).
8250 @throws randolf::rex::xEBADF The underlying socket is not open
8251 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8252 part of the user address space
8253 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
8254 valid for this socket's family (a.k.a., communication domain)
8255 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
8257 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8258 doesn't refer to a socket
8260 @returns The same rsocket object so as to facilitate stacking
8262 *///=========================================================================
8264 /// timeval structure
8265 const struct timeval tv) {
8266 return setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
8267 }; // -x- rsocket* timeout -x-
8269 /*======================================================================*//**
8270 @copydoc timeout(struct timeval)
8272 *///=========================================================================
8274 /// Timeout in seconds
8275 const int tv_sec = 0,
8276 /// Timeout in microseconds
8277 const long tv_usec = 0) {
8278 struct timeval tv{tv_sec, tv_usec};
8279 return setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
8280 }; // -x- rsocket* timeout -x-
8282 /*======================================================================*//**
8284 Find out what the read timeout is set to when using the @ref recvline()
8287 @returns @c long value (0 = no timeout)
8290 @see timeout_recvline(long)
8292 *///=========================================================================
8293 long timeout_recvline() {
8294 return __recvline_timeout;
8295 }; // -x- long timeout_recvline -x-
8297 /*======================================================================*//**
8299 Set the read timeout for the @ref recvline() method (the @ref recvline()
8300 method's @c timeout parameter can override this setting).
8303 The default timeout for this recvline_timeout setting is 0 (no timeout).
8305 @throws randolf::rex::xERANGE if the timeout parameter is below 0
8307 @returns The same rsocket object so as to facilitate stacking
8310 @see timeout_recvline
8312 *///=========================================================================
8313 rsocket* timeout_recvline(
8314 /// timeval structure
8315 const long timeout) {
8316 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
8317 __recvline_timeout = timeout;
8319 }; // -x- rsocket* timeout_recvline -x-
8321 /*======================================================================*//**
8323 Enable or disable encrypted communications (from the OpenSSL library).
8326 TLS cannot be enabled before a non-encrypted rsocket is open (an rsocket is
8327 typically opened with the @ref socket() method, the @ref connect() method, or
8328 one of the @ref accept() methods). If calling @c tls(true) on an rsocket
8329 that isn't open, an exception will be thrown.
8331 If needed, a new TLS context will be instantiated and TLS will be initialized
8332 (if this hasn't already been done). TLS instantiation can be done first by
8333 calling the @ref tls_ctx() method (regardless of whether encryption is being
8334 enabled or disabled). If the default @ref TLS_FLAGS aren't sufficient for
8335 the needs of your application, then the @ref tls_ctx() method facilitates
8336 this regardless of wehther rsocket is open.
8339 The reason a TLS context is instantiated and TLS is initialized even when the
8340 status is being set to FALSE is to facilitate TLS ingress from an unencrypted
8341 connection later in the session (see the @ref TLS_NO_INGRESS flag for more
8345 If a client attempts to upgrade a TLS connection to TLS (e.g., by using a
8346 command such as @c STARTTLS, which is commonly transmitted in unencrypted
8347 form, like @c telnet-ssl does -- using `telnet-ssl -z ssl` prevents this
8348 condition), the following error that's difficult to track down may be
8349 triggered when calling any of the @c recv methods (I hope that including this
8350 information here in this documentation will be helpful):
8352 SSL_ERROR_UNKNOWN: error:0A00010B:SSL routines::wrong version number
8354 This is most likely not a programming error, but rather a problem with how
8355 users may attempt to mis-use a connection based on a misunderstanding of the
8356 communications requirements (e.g., connecting unencrypted and attempting to
8357 upgrade to TLS over a connection that's expecting TLS encrypted data from the
8358 very beginning, without involving any ingress).
8360 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8361 OpenSSL library doesn't document which errors may be returned)
8363 @returns The same rsocket object so as to facilitate stacking
8367 *///=========================================================================
8369 /// TRUE = Enable encrypted communications@n FALSE = Disable encrypted communications
8370 const bool status = true,
8371 /// Configuration parameters
8372 const int flags = TLS_FLAGS::TLS_DEFAULT) { // | TLS_FLAGS::TLS_CLIENT
8373 if (__debug) debug("tls(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8374 + ", " + (status ? "true" : "false")
8377 // --------------------------------------------------------------------------
8378 // Create default context (with "flags" passthrough), unless it was already
8379 // created (usually by one of the tls_ctx() methods).
8380 // --------------------------------------------------------------------------
8381 if (__tls_ctx == nullptr) tls_ctx((SSL_CTX*)nullptr, flags);
8383 // --------------------------------------------------------------------------
8384 // Make sure tls_fd (ssl_fd) is allocated. If not, then it needs to be
8385 // allocated and configured.
8386 // --------------------------------------------------------------------------
8387 if (status == true && __tls_fd == nullptr) {
8388 __tls_fd = SSL_new(__tls_ctx);
8389 if (__debug) debug("SSL_set_fd(<tls_fd>, socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8392 // --------------------------------------------------------------------------
8393 // Associate OpenSSL file descriptor with underlying socket file descriptor.
8394 // --------------------------------------------------------------------------
8395 __rc_check_tls(SSL_set_fd(__tls_fd, __socket_fd));
8397 // --------------------------------------------------------------------------
8398 // Enable read-ahead so that SSL_peek will work properly.
8399 // --------------------------------------------------------------------------
8400 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
8401 SSL_set_read_ahead(__tls_fd, 1); // 0=disable / 1=enable
8403// SSL_CTX_set_max_pipelines(__tls_ctx, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
8404// SSL_set_max_pipelines(__tls_fd, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
8406// SSL_CTX_set_mode(__tls_ctx, SSL_MODE_AUTO_RETRY);
8407// SSL_set_mode(__tls_fd, SSL_MODE_AUTO_RETRY);
8409 // --------------------------------------------------------------------------
8410 // We're probably not going to use BIO because we don't need it, and it adds
8411 // unnecessary overhead for what we're doing. Also, BIO doesn't provide an
8412 // alternative to the SSL_peek function, and also doesn't resolve the need
8413 // for the MSG_WAITALL flag that the ::recv() function supports.
8415 // TODO: Remove this completely, unless is can solve the problem of reading
8416 // all incoming data (needed for readline, primarily)
8417 // --------------------------------------------------------------------------
8418 __tls_rbio = SSL_get_rbio(__tls_fd);
8419 __tls_wbio = SSL_get_wbio(__tls_fd);
8421 } // -x- if !__tls_fd -x-
8424 }; // -x- rsocket* tls -x-
8426 /*======================================================================*//**
8428 Return the current TLS context (multiple TLS contexts are supported, although
8429 typically needed to support SNI with inbound connections).
8430 @returns Pointer to OpenSSL's context (normally labelled @c SSL_CTX* in the
8431 documentation for OpenSSL), or nullptr if this context was never
8432 assigned to (or created by) this rsocket
8435 *///=========================================================================
8436 SSL_CTX* tls_ctx() noexcept {
8438 }; // -x- SSL_CTX* tls_ctx -x-
8440 /*======================================================================*//**
8442 Copy the source rsocket's TLS context map and add it to this rsocket's
8443 collection; or, if the source doesn't have any TLS contexts and this rsocket
8444 doesn't have any TLS contexts in its collection, then initialize TLS and
8445 instantiate a new TLS context. In either scenario, the source rsocket will
8446 be treated as a template as all TLS flags duplicated to enable encrypted
8447 socket I/O for use in this rsocket().
8450 At least one TLS context is needed to enable encrypted socket I/O for use in
8454 Encrypted socket I/O is only possible after a TLS context has been
8455 initialized (this is not a global setting as it has per-rsocket specificity).
8458 The only @ref TLS_FLAGS flag that doesn't get transferred is @ref TLS_SERVER
8459 when no flags are specified. Specifying any flag(s) will cause this method
8460 to ignore the source rsocket's TLS flags so as to defer to this override.
8462 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8463 OpenSSL library doesn't document which errors may be returned)
8465 @returns The same rsocket object so as to facilitate stacking
8468 *///=========================================================================
8470 /// OpenSSL's TLS context to use (if not provided, a new context will be
8471 /// created automatically using OpenSSL's defaults)
8473 /// Configuration parameters
8474 const int flags = TLS_FLAGS::TLS_DEFAULT) {
8475 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8477 + ", " + std::to_string(flags)
8480 // --------------------------------------------------------------------------
8481 // TLS-related variables (OpenSSL).
8483 // Note: The __tls_fd variable cannot be allocated until after __socket_fd
8484 // has been, hence the "post" note in the documentation.
8485 // --------------------------------------------------------------------------
8486 __tls = (bool)rtemplate->__tls; // Preserve TLS mode setting
8487 if (rtemplate->__tls_ctx != nullptr) { // If TLS context is defined, then do these important things:
8488 __tls_ctx = rtemplate->__tls_ctx; // 1. copy the pointer to SSL_CTX
8489 SSL_CTX_up_ref(__tls_ctx); // 2. increment SSL_CTX's internal reference count
8490 } // -x- if __tlx_ctx -x-
8492 // --------------------------------------------------------------------------
8493 // Copy or override TLS flags.
8494 // --------------------------------------------------------------------------
8495 if (flags == TLS_FLAGS::TLS_DEFAULT) {
8496 __tls_exclusive = rtemplate->__tls_exclusive; // TLS policy
8497 __tls_egress = rtemplate->__tls_egress; // TLS policy
8498 __tls_ingress = rtemplate->__tls_ingress; // TLS policy
8499 } else { // Save flags
8500 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
8501 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
8502 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
8503 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
8504 } // -x- if flags -x-
8507 }; // -x- rsocket* tls_ctx -x-
8509 /*======================================================================*//**
8511 Initialize TLS and instantiate a TLS context, and add it to this rsocket's
8512 current collection of TLS contexts, and set it as the currently active TLS
8513 context (so that a certificate chain and private key may be added to it).
8515 At least one TLS context is needed to enable encrypted socket I/O for use in
8518 Encrypted socket I/O is only possible after a TLS context has been
8519 initialized (this is not a global setting as it has per-rsocket specificity).
8521 This is the default TLS context for this @c rsocket, which will also be used
8522 for non-SNI handshakes.
8524 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8525 OpenSSL library doesn't document which errors may be returned)
8527 @returns The same rsocket object so as to facilitate stacking
8530 *///=========================================================================
8532 /// OpenSSL's TLS context to use (if not provided, a new context will be
8533 /// created using OpenSSL's defaults)
8535 /// Configuration parameters
8536 const int flags = TLS_FLAGS::TLS_DEFAULT) {
8537 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8539 + ", " + std::to_string(flags)
8542 // --------------------------------------------------------------------------
8543 // Ignore repeated calls to this method.
8544 // --------------------------------------------------------------------------
8545 if (__tls_ctx != nullptr) return this;
8547 // --------------------------------------------------------------------------
8548 // Fire up OpenSSL's algorithms and pre-load its error strings.
8550 // These two functions have been deprecated since OpenSSL v1.1.0, so we don't
8551 // need to call them. If someone needs them, then they can always call them
8552 // in their own code anyway.
8553 // --------------------------------------------------------------------------
8554 //OpenSSL_add_all_algorithms(); // Load and register cryptography algorithms
8555 //SSL_load_error_strings(); // Load all error messages into memory
8557 // --------------------------------------------------------------------------
8558 // Save (ctx != nullptr) or create (ctx == nullptr) OpenSSL context.
8559 // --------------------------------------------------------------------------
8560 __tls_ctx = ctx == nullptr ? SSL_CTX_new(flags & TLS_FLAGS::TLS_SERVER ? TLS_server_method() : TLS_client_method()) // Create anew (default)
8561 : ctx; // Use OpenSSL context that was provided
8562 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);
8564 // --------------------------------------------------------------------------
8565 // Enable read-ahead so that SSL_peek will work properly.
8566 // --------------------------------------------------------------------------
8567 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
8569 // --------------------------------------------------------------------------
8571 // --------------------------------------------------------------------------
8572 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
8573 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
8574 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
8575 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
8578 }; // -x- rsocket* tls_ctx -x-
8580 /*======================================================================*//**
8582 Check the private key it to ensure it's consistent with the corresponding TLS
8585 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8586 OpenSSL library doesn't document which errors may be returned)
8588 @returns The same rsocket object so as to facilitate stacking
8589 @see tls_ctx_use_privatekey_file
8590 @see tls_ctx_use_privatekey_pem
8592 *///=========================================================================
8593 rsocket* tls_ctx_check_privatekey() {
8594 if (__debug) debug("tls_ctx_check_privatekey();");
8595 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);
8597 }; // -x- rsocket* tls_ctx_check_privatekey -x-
8599 /*======================================================================*//**
8601 Load a TLS certificate chain and private key in PEM format from text files
8602 and use them in the TLS context.
8604 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8605 OpenSSL library doesn't document which errors may be returned)
8607 @returns The same rsocket object so as to facilitate stacking
8608 @see tls_ctx_use_certificate_chain_and_privatekey_pems
8609 @see tls_ctx_use_certificate_chain_file
8610 @see tls_ctx_use_certificate_chain_pem
8611 @see tls_ctx_use_privatekey_file
8612 @see tls_ctx_use_privatekey_pem
8613 @see tls_ctx_check_privatekey
8615 *///=========================================================================
8616 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
8617 /// Pointer to ASCIIZ path and filename to certificate chain file (@c nullptr
8618 /// will simply be ignored)
8619 const char* chain_file,
8620 /// Pointer to ASCIIZ path and filename to private key file (@c nullptr will
8621 /// simply be ignored)
8622 const char* key_file) {
8623 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
8624 + std::string(chain_file)
8625 + ", " + std::string( key_file)
8627 if (chain_file != nullptr) tls_ctx_use_certificate_chain_file(chain_file);
8628 if ( key_file != nullptr) tls_ctx_use_privatekey_file( key_file);
8630 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
8632 /*======================================================================*//**
8634 Load a TLS certificate chain and private key in PEM format from text files
8635 and use them in the TLS context.
8637 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8638 OpenSSL library doesn't document which errors may be returned)
8640 @returns The same rsocket object so as to facilitate stacking
8641 @see tls_ctx_use_certificate_chain_and_privatekey_pems
8642 @see tls_ctx_use_certificate_chain_file
8643 @see tls_ctx_use_certificate_chain_pem
8644 @see tls_ctx_use_privatekey_file
8645 @see tls_ctx_use_privatekey_pem
8646 @see tls_ctx_check_privatekey
8648 *///=========================================================================
8649 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
8650 /// Pointer to ASCIIZ path and filename to certificate chain file (an empty
8651 /// string will simply be ignored)
8652 const std::string chain_file,
8653 /// Pointer to ASCIIZ path and filename to private key file (an empty string
8654 /// will simply be ignored)
8655 const std::string key_file) {
8656 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
8660 if (!chain_file.empty()) tls_ctx_use_certificate_chain_file(chain_file);
8661 if ( !key_file.empty()) tls_ctx_use_privatekey_file( key_file);
8663 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
8665 /*======================================================================*//**
8667 Load a TLS certificate chain and a TLS private key in PEM format from memory
8668 and use them in the TLS context.
8670 Although this functionality doesn't exist in OpenSSL (at the time of writing
8671 this method), it's provided here in a manner that has exactly the same effect
8672 as the @ref tls_ctx_use_certificate_chain_and_privatekey_files() methods, but
8673 without needing the PEM-formatted certificate chain stored in files
8677 The @c cert_pem_data and key_pem_data parameters are pointers to the memory
8678 locations that holds the PEM formatted certificate chain data and private key
8679 data, respectively. If the corresponding lengths of each of these data aren't
8680 specified or are set to zero (default), then they will be treated as multiline
8683 Behind the scenes, we're just writing the cert_pem_data and key_pem_data
8684 memory to temporary files with severely-limited permissions (), then
8685 optionally overwriting those temporary files with random data prior to
8686 deleting them (this is the default, since better security practices should be
8687 the default, but on a secured system it may not be necessary and so this
8688 option can also be disabled to save CPU cycles and reduce overall disk-write
8691 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8692 OpenSSL library doesn't document which errors may be returned)
8694 @returns The same rsocket object so as to facilitate stacking
8695 @see tls_ctx_use_certificate_chain_and_privatekey_files
8696 @see tls_ctx_use_certificate_chain_file
8697 @see tls_ctx_use_certificate_chain_pem
8698 @see tls_ctx_use_privatekey_file
8699 @see tls_ctx_use_privatekey_pem
8700 @see tls_ctx_check_privatekey
8702 *///=========================================================================
8703 rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems(
8704 /// Pointer to certificate chain data in PEM format
8705 const char* cert_pem_data,
8706 /// Pointer to private key data in PEM format
8707 const char* key_pem_data,
8708 /// Length of cert_pem_data (in bytes), or 0 to auto-detect length if cert_pem_data is an ASCIIZ string
8709 size_t cert_len = 0,
8710 /// Length of key_pem_data (in bytes), or 0 to auto-detect length if key_pem_data is an ASCIIZ string
8712 /// Whether to overwrite the temporary files with random data before deleting them
8713 const bool random_fill = true) {
8714 tls_ctx_use_certificate_chain_pem(cert_pem_data, cert_len, random_fill);
8715 tls_ctx_use_privatekey_pem( key_pem_data, key_len, random_fill);
8717 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems -x-
8719 /*======================================================================*//**
8721 Load a TLS certificate chain in PEM format from a text file and use it in the
8724 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8725 OpenSSL library doesn't document which errors may be returned)
8727 @returns The same rsocket object so as to facilitate stacking
8728 @see tls_ctx_use_certificate_chain_file
8729 @see tls_ctx_use_certificate_chain_pem
8730 @see tls_ctx_check_privatekey
8732 *///=========================================================================
8733 rsocket* tls_ctx_use_certificate_chain_file(
8734 /// Pointer to ASCIIZ path and filename to certificate chain file
8736 if (__debug) debug("tls_ctx_use_certificate_chain_file("
8739 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);
8741 }; // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
8743 /*======================================================================*//**
8745 Load a TLS certificate chain in PEM format from a text file and use it in the
8748 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8749 OpenSSL library doesn't document which errors may be returned)
8751 @returns The same rsocket object so as to facilitate stacking
8752 @see tls_ctx_use_certificate_chain_file
8753 @see tls_ctx_use_certificate_chain_pem
8754 @see tls_ctx_check_privatekey
8756 *///=========================================================================
8757 rsocket* tls_ctx_use_certificate_chain_file(
8758 /// Path and filename to certificate chain file
8759 const std::string file) {
8760 if (__debug) debug("tls_ctx_use_certificate_chain_file("
8763 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);
8765 }; // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
8767 /*======================================================================*//**
8769 Load a TLS certificate chain in PEM format from memory and use it in the TLS
8772 Although this functionality doesn't exist in OpenSSL (at the time of writing
8773 this method), it's provided here in a manner that has exactly the same effect
8774 as the @ref tls_ctx_use_certificate_chain_file() methods, but without needing
8775 the PEM-formatted certificate chain stored in a file beforehand.
8778 The @c pem_data parameter is a pointer to the memory location that holds
8779 the PEM formatted certificate chain data. If the length of this data isn't
8780 specified or is set to zero (default), then it will be treated as a multiline
8783 Behind the scenes, we're just writing the pem_data memory to a temporary
8784 file with severely-limited permissions (), then optionally overwriting that
8785 temporary file with random data prior to deleting it (this is the default,
8786 since better security practices should be the default, but on a secured
8787 system it may not be necessary and so this option can also be disabled to
8788 save CPU cycles and reduce overall disk-write I/O operations).
8790 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8791 OpenSSL library doesn't document which errors may be returned)
8793 @returns The same rsocket object so as to facilitate stacking
8794 @see tls_ctx_use_certificate_chain_file
8795 @see tls_ctx_check_privatekey
8797 *///=========================================================================
8798 rsocket* tls_ctx_use_certificate_chain_pem(
8799 /// Pointer to certificate chain data in PEM format
8800 const char* pem_data,
8801 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
8803 /// Whether to overwrite the temporary file with random data before deleting it
8804 const bool random_fill = true) {
8805 if (__debug) debug("tls_ctx_use_certificate_chain_pem(<buf>, " + std::to_string(len)
8806 + ", " + (random_fill ? "true" : "false")
8809 // --------------------------------------------------------------------------
8810 // Measure size of certificate chain if an ASCIIZ string was indicated.
8811 // --------------------------------------------------------------------------
8812 if (len == 0) len = std::strlen(pem_data);
8814 // --------------------------------------------------------------------------
8815 // Generate filename for temporary use.
8816 // --------------------------------------------------------------------------
8817 std::string file = std::filesystem::temp_directory_path();
8818 file.append("/rsocket.")
8819 .append(std::to_string(::getpid()))
8821 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
8823 // --------------------------------------------------------------------------
8824 // Open temporary file.
8825 // --------------------------------------------------------------------------
8826 FILE* fp = fopen(file.c_str(), "w+");
8827 if (fp == nullptr) randolf::rex::mk_exception("Cannot open temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8829 ioctl(fileno(fp), FS_IOC_GETFLAGS, &attr);
8830 attr |= FS_NOATIME_FL // Don't update access time attribute
8831 | FS_NODUMP_FL // Don't include in filesystem backup dumps
8832 | FS_SECRM_FL; // Mark file for secure deletion (where supported)
8833 ioctl(fileno(fp), FS_IOC_SETFLAGS, &attr);
8835 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
8836 if (fputs(pem_data, fp) == EOF) {
8838 randolf::rex::mk_exception("Cannot write to temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8839 } // -x- if !fputs -x-
8842 // --------------------------------------------------------------------------
8843 // Attempt to load certificate chain file, but save the error code for later
8844 // because we need to clean up the temporary file before possibly throwing an
8846 // --------------------------------------------------------------------------
8847 int rc = SSL_CTX_use_certificate_chain_file(__tls_ctx, file.c_str());
8849 // --------------------------------------------------------------------------
8850 // Overwrite the contenst of the temporary file before deleting it so as to
8851 // sabotage a simple attempt to undelete the file and access the certificate.
8853 // We're also re-using the "len" local variable because it's not needed once
8854 // we get the loop started, and it's more optimal to not allocate yet another
8855 // local variable while "len" goes to waste. :D
8856 // --------------------------------------------------------------------------
8857 if (random_fill) { // This option is configurable
8858 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
8859 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
8860 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
8861 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
8862 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
8864 } // -x- if randfill -x-
8865 fchmod(fileno(fp), 0); // Remove all permissions
8866 fclose(fp); // Close file handle
8867 unlink(file.c_str()); // Delete temporary file
8869 // --------------------------------------------------------------------------
8870 // Error check ... was delayed here until after temporary file cleanup.
8871 // --------------------------------------------------------------------------
8872 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);
8875 }; // -x- rsocket* tls_ctx_use_certificate_chain_pem -x-
8877 /*======================================================================*//**
8879 Load a TLS private key in PEM format from a text file and use it in the TLS
8882 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8883 OpenSSL library doesn't document which errors may be returned)
8885 @returns The same rsocket object so as to facilitate stacking
8886 @see tls_ctx_use_privatekey_file
8887 @see tls_ctx_use_privatekey_pem
8888 @see tls_ctx_check_privatekey
8890 *///=========================================================================
8891 rsocket* tls_ctx_use_privatekey_file(
8892 /// Pointer to ASCIIZ path-and-filename of private key file
8894 if (__debug) debug("tls_ctx_use_privatekey_file("
8897 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);
8900 }; // -x- rsocket* tls_ctx_use_privatekey_file -x-
8902 /*======================================================================*//**
8904 Load a TLS private key in PEM format from a text file and use it in the TLS
8907 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8908 OpenSSL library doesn't document which errors may be returned)
8910 @returns The same rsocket object so as to facilitate stacking
8911 @see tls_ctx_use_privatekey_file
8912 @see tls_ctx_use_privatekey_pem
8913 @see tls_ctx_check_privatekey
8915 *///=========================================================================
8916 rsocket* tls_ctx_use_privatekey_file(
8917 /// Path and filename to private key file
8918 const std::string file) {
8919 if (__debug) debug("tls_ctx_use_privatekey_file("
8922 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);
8925 }; // -x- rsocket* tls_ctx_use_privatekey_file -x-
8927 /*======================================================================*//**
8929 Load a TLS private key in PEM format from memory and use it in the TLS
8932 Although this functionality doesn't exist in OpenSSL (at the time of writing
8933 this method), it's provided here in a manner that has exactly the same effect
8934 as the @ref tls_ctx_use_privatekey_file() methods, but without needing the
8935 PEM-formatted private key stored in a file beforehand.
8938 The @c pem_data parameter is a pointer to the memory location that holds the
8939 PEM formatted private key data. If the length of this data isn't specified
8940 or is set to zero (default), then it will be treated as a multiline ASCIIZ
8943 Behind the scenes, we're just writing the pem_data memory to a temporary
8944 file (with severely-limited permissions), then optionally overwriting that
8945 temporary file with random data prior to deleting it (this is the default,
8946 since better security practices should be the default, but on a secured
8947 system it may not be necessary and so this option can also be disabled to
8948 save CPU cycles and reduce overall disk-write I/O operations).
8950 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8951 OpenSSL library doesn't document which errors may be returned)
8953 @returns The same rsocket object so as to facilitate stacking
8954 @see tls_ctx_use_privatekey_file
8955 @see tls_ctx_check_privatekey
8957 *///=========================================================================
8958 rsocket* tls_ctx_use_privatekey_pem(
8959 /// Pointer to private key data in PEM format
8960 const char* pem_data,
8961 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
8963 /// Whether to overwrite the temporary file with random data before deleting it
8964 const bool random_fill = true) {
8965 if (__debug) debug("tls_ctx_use_privatekey_pem(<buf>, " + std::to_string(len)
8966 + ", " + (random_fill ? "true" : "false")
8969 // --------------------------------------------------------------------------
8970 // Measure size of private key if an ASCIIZ string was indicated.
8971 // --------------------------------------------------------------------------
8972 if (len == 0) len = std::strlen(pem_data);
8974 // --------------------------------------------------------------------------
8975 // Generate filename for temporary use.
8976 // --------------------------------------------------------------------------
8977 std::string file = std::filesystem::temp_directory_path();
8978 file.append("/rsocket.")
8979 .append(std::to_string(::getpid()))
8981 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
8983 // --------------------------------------------------------------------------
8984 // Open temporary file.
8985 // --------------------------------------------------------------------------
8986 FILE* fp = fopen(file.c_str(), "w+");
8987 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);
8988 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
8989 if (fputs(pem_data, fp) == EOF) {
8991 randolf::rex::mk_exception("Cannot cannot write to temporary file (to use private key) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8992 } // -x- if !fputs -x-
8995 // --------------------------------------------------------------------------
8996 // Attempt to load private key file, but save the error code for later
8997 // because we need to clean up the temporary file before possibly throwing an
8999 // --------------------------------------------------------------------------
9000 int rc = SSL_CTX_use_PrivateKey_file(__tls_ctx, file.c_str(), SSL_FILETYPE_PEM);
9002 // --------------------------------------------------------------------------
9003 // Overwrite the contenst of the temporary file before deleting it so as to
9004 // sabotage a simple attempt to undelete the file and access the certificate.
9006 // We're also re-using the "len" local variable because it's not needed once
9007 // we get the loop started, and it's more optimal to not allocate yet another
9008 // local variable while "len" goes to waste. :D
9009 // --------------------------------------------------------------------------
9010 if (random_fill) { // This option is configurable
9011 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
9012 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
9013 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
9014 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
9015 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
9017 } // -x- if randfill -x-
9018 fchmod(fileno(fp), 0); // Remove all permissions
9019 fclose(fp); // Close file handle
9020 unlink(file.c_str()); // Delete temporary file
9022 // --------------------------------------------------------------------------
9023 // Error check ... was delayed here until after temporary file cleanup.
9024 // --------------------------------------------------------------------------
9025 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);
9028 }; // -x- rsocket* tls_ctx_use_privatekey_pem -x-
9030 /*======================================================================*//**
9032 Initiate the TLS handshake with the endpoint (which is presumed to be a
9034 This method makes it easier to support application-level commands such as @c
9035 STARTTLS (which are implemented in protocols like SMTP, POP3, IMAP4, MEOW,
9036 FTP, NNTP, LDAP, XMPP, etc.).
9038 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9039 OpenSSL library doesn't document which errors may be returned)
9041 @returns The same rsocket object so as to facilitate stacking
9043 @see connect(std::string, int)
9046 *///=========================================================================
9047 rsocket* tls_do_handshake() {
9048 if (__debug) debug("tls_handshake(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9050 __rc_check_tls(SSL_do_handshake(__tls_fd));
9052 }; // -x- rsocket* tls_do_handshake -x-
9054 /*======================================================================*//**
9056 Get OpenSSL's TLS structure.
9057 @returns TLS structure
9058 @returns nullptr = TLS structure not yet allocated
9060 *///=========================================================================
9061 const SSL* tls_fd() noexcept {
9063 }; // -x- int tls_fd -x-
9065 /*======================================================================*//**
9067 Return the current @ref rsocket_sni object that this @c rsocket will use when
9068 accepting incoming encrypted connections.
9069 @returns Pointer to @c rsocket_sni object
9070 @returns nullptr = SNI is not assigned
9072 @see tls_sni(rsocket_sni*)
9074 *///=========================================================================
9075 rsocket_sni* tls_sni() noexcept {
9077 }; // -x- rsocket_sni* tls_sni -x-
9079 /*======================================================================*//**
9081 Set the current @ref rsocket_sni object that this @c rsocket will use when
9082 accepting incoming encrypted connections.
9084 Use the @ref name() method to find out which server name was supplied by the
9085 endpoint that triggered the SNI callback, regardless of whether it matches
9086 any of the TLS certificates used with this rsocket object or the rsocket_sni
9087 object that's associated with this rsocket object. If an SNI callback wasn't
9088 triggered, or if the endpoint didn't provide a server name, then it will
9089 remain unaffected (and the default {empty string} will remain unchanged).
9090 @returns The same rsocket object so as to facilitate stacking
9093 @see is_tls_sni_match
9095 *///=========================================================================
9097 /// Pointer to the @ref rsocket_sni object to use, or specify @c nullptr to
9098 /// remove SNI support from this rsocket object
9099 rsocket_sni* sni) noexcept {
9101 // --------------------------------------------------------------------------
9102 // Remove SNI support.
9103 // --------------------------------------------------------------------------
9104 if (sni == nullptr) {
9105 if (__tls_sni != nullptr) SSL_CTX_set_client_hello_cb(__tls_ctx, nullptr, this);
9107 // --------------------------------------------------------------------------
9108 // Add or set SNI support.
9109 // --------------------------------------------------------------------------
9111 SSL_CTX_set_client_hello_cb(__tls_ctx, tls_sni_callback, this); // Configure SNI callbacks for TLS.
9112 //SSL_CTX_set_tlsext_servername_callback(__tls_ctx, tls_sni_callback); // Don't use this anymore; it's outdated
9113 //SSL_CTX_set_tlsext_servername_arg(__tls_ctx, this); // Don't use this anymore; it's outdated
9115 } // -x- if !sni -x-
9117 // --------------------------------------------------------------------------
9118 // Update internal pointer to the SNI map.
9119 // --------------------------------------------------------------------------
9123 }; // -x- rsocket_sni* tls_sni -x-
9126 /*======================================================================*//**
9128 Get OpenSSL's TLS structure.
9129 @returns TLS structure
9130 @returns nullptr = TLS structure not yet allocated
9132 *///=========================================================================
9133 int static tls_sni_callback(
9134 /// OpenSSL's socket descriptor/handle
9136 /// Where to store the @c alert value
9138 /// Context-specific argument
9139 void* arg) noexcept {
9141 // --------------------------------------------------------------------------
9142 // Internal variables.
9143 // --------------------------------------------------------------------------
9144 rsocket* r = (rsocket*)arg; // We may be updating the TLS context (this is normally the new client's rsocket object)
9145 const unsigned char* out = nullptr;
9146 size_t out_size = 0;
9148 // --------------------------------------------------------------------------
9149 // Obtain a pointer to newly-allocated ClientHello fields data. If *out is
9150 // nullptr, it means that no TLSEXT_TYPE_server_name was received from the
9151 // client, and so the default TLS context will suffice. If out_size is less
9152 // than or equal to 2, then it also won't have what we need.
9153 // --------------------------------------------------------------------------
9154 if (!SSL_client_hello_get0_ext(tls_fd, TLSEXT_TYPE_server_name, &out, &out_size)
9155 || out == nullptr || out_size <= 2) return SSL_CLIENT_HELLO_SUCCESS; // 1
9157 // --------------------------------------------------------------------------
9158 // Scan for TLSEXT_NAMETYPE_host_name, but if we can't find it then we'll
9159 // just leave the default TLS context as is.
9160 // --------------------------------------------------------------------------
9161 unsigned char* p = (unsigned char*)out;
9162 size_t len = (*(p++) << 8);
9164 if (len + 2 != out_size) goto finish;
9166 // --------------------------------------------------------------------------
9167 // We're taking a shortcut by examining only the first element in the list,
9168 // but in the future we need to make this more robust in case other types of
9169 // elements precede what we're looking for.
9171 // Unfortunately, there's no documentation that properly-explains the format
9172 // of the list, so some deeper research into OpenSSL's source code will be
9173 // needed (a cursory look so far has not yielded the necessary insight).
9175 // TODO: Turn this into a loop that supports future clients that provide
9176 // multiple SNI server names in their requests. (Although this isn't
9177 // occuring at present with common end-user tools such as web browsers
9178 // and eMail software, it may happen in the future as client/server
9179 // software becomes more savvy.)
9180 // --------------------------------------------------------------------------
9181 if (out_size == 0 || *p++ != TLSEXT_NAMETYPE_host_name) goto finish;
9183 // --------------------------------------------------------------------------
9184 // Avoid buffer overrun caused by corrupt or prematurely-truncated data.
9185 // --------------------------------------------------------------------------
9186 if (--out_size <= 2) goto finish;
9188 // --------------------------------------------------------------------------
9189 // Extract and use the hostname (SNI server name) that was supplied by the
9190 // endpoint so that the correct TLS certificate can be selected and assigned.
9191 // --------------------------------------------------------------------------
9192 len = (*(p++) << 8);
9194 if (!(len + 2 > out_size)) { // Only process SNI server name string if it's at least 2 bytes long
9195 // Debug: std::cout << "Server name: " << (const char*)p << std::endl; // Debug
9197 // --------------------------------------------------------------------------
9198 // Obtain the correct TLS context (wildcards supported) that is associated
9199 // with the hostname (SNI server name) that was supplied by the endpoint.
9200 // --------------------------------------------------------------------------
9201 const char* sni_name = (const char*)p;
9202 SSL_CTX* new_ctx = r->__tls_sni->get_ctx(sni_name, true, r->tls_ctx());
9204 // --------------------------------------------------------------------------
9205 // Change TLS context so that encryption with the endpoint uses the correct
9206 // TLS certificate (otherwise the endpoint will indicate security risk errors
9207 // to end users, log files, etc., and may {should} reject the connection).
9208 // --------------------------------------------------------------------------
9209//std::cout << "SNI setup... " << sni_name << std::endl;
9210 SSL_set_SSL_CTX(tls_fd, new_ctx);
9211//std::cout << "---1--- " << r->__tls_new_endpoint << std::endl;
9212 r->__tls_new_endpoint->tls_ctx(new_ctx);
9213//std::cout << "---2---" << std::endl;
9214 r->__tls_new_endpoint->__name_sni.assign(sni_name);
9215//std::cout << "SNI name() = " << r->__tls_new_endpoint->name() << std::endl;
9216 r->__tls_new_endpoint->__tls_sni_match = true;
9218 } // -x- if len+2 -x-
9221 // --------------------------------------------------------------------------
9222 // Free the ClientHello fields data, then return SSL_CLIENT_HELLO_SUCCESS.
9223 // Even if we encountered a problem with the ClientHello fields data, we
9224 // still return SSL_CLIENT_HELLO_SUCCESS so that the TLS context will be
9225 // accepted as valid.
9227 // If we return SSL_CLIENT_HELLO_ERROR the connection will fail unnecessarily
9228 // for valid TLS certificates. If the ClientHello fields data is malformed
9229 // to a degree that doesn't satisfy OpenSSL, then OpenSSL will reject it, so
9230 // there's really no point in duplicating what OpenSSL already does properly,
9231 // which OpenSSL will pass through the standard error channels with normal
9232 // error details-and-diagnostics anyway.
9233 // --------------------------------------------------------------------------
9234 //OPENSSL_free((char*)out); // OpenSSL documentation says to do this, but it fails and crashes the program
9236 return SSL_CLIENT_HELLO_SUCCESS; // 1
9237 }; // -x- int tls_sni_callback -x-
9240 /*======================================================================*//**
9242 Convert a 48-bit integer to a machine address in the form of @c
9243 xx:xx:xx:xx:xx:xx where every instance of @c xx is a hexadecimal
9244 representation of each respective 8-bit byte portion.
9246 This method is needed because we don't want to bring in the heavy fmt::format
9247 class as a dependency.
9248 @returns Mac address as 17-character in the typical format expected by system
9251 *///=========================================================================
9252 static std::string to_mac(
9253 /// Pointer to 48-bit integer
9254 const void* addr) noexcept {
9256 h.resize(18); // 48-bit mac address needs 6 hexadecimal pairs of nybbles delimited by colons plus a NULL terminator
9257 h.resize(snprintf(h.data(),
9259 "%02x:%02x:%02x:%02x:%02x:%02x",
9265 ((u_char*)addr)[5])); // Convert, and truncate NULL terminator
9267 }; // -x- std::string to_mac -x-
9269 /*======================================================================*//**
9271 Convert a 48-bit integer to a node address in the form of @c xxxx:xxxx:xxxx
9272 where every instance of @c xxxx is a hexadecimal representation of each
9273 respective 16-bit word portion.
9275 This method is needed because we don't want to bring in the heavy fmt::format
9276 class as a dependency.
9277 @returns Node address as 14-character in the typical format expected by
9278 network administrators
9280 *///=========================================================================
9281 static std::string to_node(
9282 /// Pointer to 48-bit integer
9283 const void* addr) noexcept {
9285 h.resize(15); // 48-bit node address needs 3 hexadecimal sets of words delimited by colons plus a NULL terminator
9286 h.resize(snprintf(h.data(),
9289 ((u_int16_t*)addr)[0],
9290 ((u_int16_t*)addr)[1],
9291 ((u_int16_t*)addr)[2])); // Convert, and truncate NULL terminator
9293 }; // -x- std::string to_node -x-
9295 /*======================================================================*//**
9297 Send a formatted string to the @ref rsocket endpoint.
9299 The @c format is described in the documentation for the POSIX or Standard C
9300 Library @c printf() function.
9301 @throws randolf::rex::xEBADF The underlying socket is not open
9302 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
9303 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
9304 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
9305 @throws randolf::rex::xENOMEM Insufficient memory
9306 @returns The same rsocket object so as to facilitate stacking
9308 @see is_eol_fix_printf
9315 *///=========================================================================
9317 /// Format string to use
9319 /// Variadic arguments in @c va_list format
9322 int rc = ::vasprintf(&buf, format, args);
9323 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
9324 if (__eol_fix_printf && !__eol.empty()) {
9325 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
9327 __send(str.c_str(), str.length());
9331 } catch (std::exception& e) { // Free buf then re-throw the exception
9332 ::free(buf); // Prevent memory leak when an exception is thrown
9335 } // -x- if __eol_fix_printf -x-
9337 }; // -x- rsocket* vprintf -x-
9339 /*======================================================================*//**
9341 Send a formatted string to the @ref rsocket endpoint, and append an EoL
9344 The @c format is described in the documentation for the POSIX or Standard C
9345 Library @c printf() function.
9346 @throws randolf::rex::xEBADF The underlying socket is not open
9347 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
9348 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
9349 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
9350 @throws randolf::rex::xENOMEM Insufficient memory
9351 @returns The same rsocket object so as to facilitate stacking
9354 @see is_eol_fix_printf
9361 *///=========================================================================
9362 rsocket* vprintfline(
9363 /// Format string to use
9365 /// Variadic arguments in @c va_list format
9368 int rc = ::vasprintf(&buf, format, args);
9369 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
9370 if (__eol_fix_printf && !__eol.empty()) {
9371 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
9374 __send(str.c_str(), str.length());
9377 __sendline(buf, rc);
9378 } catch (std::exception& e) { // Free buf then re-throw the exception
9379 ::free(buf); // Prevent memory leak when an exception is thrown
9382 } // -x- if __eol_fix_printf -x-
9384 }; // -x- rsocket* vprintfline -x-
9387 /*======================================================================*//**
9388 Track unencrypted bytes received. When the number of bytes is negative or
9389 zero, it isn't recorded.
9390 This is an internal function.
9391 @returns same value provided in @ref n
9393 *///=========================================================================
9394 int __track_bytes_rx(
9395 /// Number of bytes transferred
9397 if (n > 0) __bytes_rx.fetch_add(n, std::memory_order_relaxed);
9399 }; // -x- __track_bytes_rx -x-
9401 /*======================================================================*//**
9402 Track unencrypted bytes transmitted. When the number of bytes is negative or
9403 zero, it isn't recorded.
9404 This is an internal function.
9405 @returns same value provided in @ref n
9407 *///=========================================================================
9408 int __track_bytes_tx(
9409 /// Number of bytes transferred
9411 if (n > 0) __bytes_tx.fetch_add(n, std::memory_order_relaxed);
9413 }; // -x- __track_bytes_tx -x-
9415 /*======================================================================*//**
9416 Track encrypted bytes received. When the number of bytes is negative or
9417 zero, it isn't recorded.
9418 This is an internal function.
9419 @returns same value provided in @ref n
9421 *///=========================================================================
9422 int __track_crypt_rx(
9423 /// Number of bytes transferred
9425 if (n > 0) __crypt_rx.fetch_add(n, std::memory_order_relaxed);
9427 }; // -x- __track_crypt_rx -x-
9429 /*======================================================================*//**
9430 Track encrypted bytes transmitted. When the number of bytes is negative or
9431 zero, it isn't recorded.
9432 This is an internal function.
9433 @returns same value provided in @ref n
9435 *///=========================================================================
9436 int __track_crypt_tx(
9437 /// Number of bytes transferred
9439 if (n > 0) __crypt_tx.fetch_add(n, std::memory_order_relaxed);
9441 }; // -x- __track_crypt_tx -x-
9443 }; // -x- class rsocket -x-
9445}; // -x- namespace randolf -x-
9447// Save this for a future sendlines() methods.
9448// const void* msg_ptr = msg.c_str(); // Prevent repeated calls to c_str() method
9449// const int len = msg.length(); // Prevent repeated calls to length() method