4#include <randolf/rline>
5#include <randolf/rring>
6#include <randolf/rring_bam>
7#include <randolf/rsocket_io>
8#include <randolf/rsocket_sni>
9#include <randolf/rtools>
10#include <randolf/sockaddr_dl.h>
12#include <algorithm> // std::min and std::max
14#include <bit> // std::endian
15#include <cstdarg> // std::va_list
16#include <cstring> // std::strlen
17#include <ctime> // std::strftime
18#include <exception> // std::exception
19#include <filesystem> // std::filesystem::temp_directory_path
20#include <initializer_list>
21#include <iostream> // std::put_date
22#include <map> // std::map
23#include <memory> // std::shared_ptr
24#include <mutex> // std::mutex
27//#include <sstream> // std::ostringstream
28#include <unordered_map> // std::unordered_map
34#include <string.h> // strerror()
39#include <linux/fs.h> // Flags for ioctl()
41#include <net/if.h> // ifreq structure used by bind()
43#include <netinet/icmp6.h>
44#include <netinet/in.h>
46#include <netpacket/packet.h> // struct sockaddr_ll
48#include <openssl/err.h>
49#include <openssl/ossl_typ.h>
50#include <openssl/ssl.h>
53#include <sys/socket.h>
54#include <sys/stat.h> // fchmod()
55#include <sys/time.h> // TIMEVAL_TO_TIMESPEC macro
59static_assert((sizeof(__time_t) * CHAR_BIT) >= 64, "__time_t of at least 64-bit is required");
63 /*======================================================================*//**
65 This @ref rsocket class provides an easy-to-use and thoroughy-implemented
66 object-oriented socket I/O interface for C++, intended to make socket I/O
67 programming (with or without TLS encryption) easier and more enjoyable.
69 Here's a short list of benefits that are helpful in developing high quality
70 code that's consistent-and-reliable, and improves overall productivity:
72 - eliminating the need to repeatedly write blocks of code that check for
73 errors, by throwing exceptions instead (see @ref randolf::rex::rex class
74 for details and the long list of exceptions that are supported)
75 - eliminating the need to track socket descriptors
76 - eliminating the need to handle encrypted I/O separately (most functions)
77 - eliminating the need to manage memory for many common structures used to
78 interface with socket options, etc.
79 - eliminating the need to record socket I/O statistics with every call to
80 underlying socket I/O functions (see @ref randolf::rsocket_io for
82 - text-line reading/writing with an adapative approach (invented by
83 Randolf Richardson in the 1980s for a custom BBS software project) to
84 automatically detect EoL (End-of-Line) character sequences (unless the
85 developer provides a specific sequence via an @ref eol method) that can
86 determine whether an endpoint is sending Linux/UNIX (standard), MacOS,
87 DOS CR/LF, or even broken LF/CR (reverse of DOS) EoL sequences
88 - transparent support for encryption with many additional features,
89 including STARTTLS, ingress/egress policy enforcement, and SNI
90 - eliminating the complexity of handling events with poll(), select(), and
91 related functions (see the @ref randolf::rsocket_mux class for details)
92 - providing a variety of other useful features that make it easier to
93 communicate with socket endpoints, such as receiving/sending an entire
94 structure via a single call to the new-and-specialized @ref recv_struct
95 or @ref send_struct methods, respectively
97 An rsocket is either the endpoint that our underlying socket will connect to,
98 or it's the server daemon that our underying socket will listen() to and
99 accept() [inbound] connections from.
103 Using the C interface, the programming must check for errors by testing the
104 response codes (most of which are consistent, with a few subtle outliers),
105 which leads to a lot of additional error-checking code with the potential for
106 unintended errors (a.k.a., bugs). This style is necessary in C, but with C++
107 the way to handle errors is with exceptions, so I created this rsocket class
108 to handle all these tedious details behind-the-scenes and, for any socket
109 errors, to generate exceptions so that source code can be greatly simplified
110 (and, as a result, also easier to read and review).
112 Pre-allocating buffers is also handled internally, which is particularly
113 useful when making repeated calls to recv() and related methods. These
114 methods return `std::shared_ptr<structure>` (a C++ smart pointer that aids
115 in the prevention of resource leaks) or `std::vector<char>` (resized to the
116 actual number of bytes received) or `std::string` as appropriate, which
117 eliminates the need to track @c size_t separately.
120 Lower-case letter "r" is regularly used in partial example code to represent
121 an instantiated rsocket object.
123 An ASCIIZ string is a C-string (char* array) that includes a terminating null
124 (0) character at the end.
126 The following custom qualifiers are incorporated into headings by Doxygen
127 alongside method titles throughout the documentation:
128 - @c POSIX denotes a method that is based on POSIX functions by the same
129 name and don't deviate significantly from the POSIX function arguments
130 (intended to be helpful to developers transitioning to/from rsocket or
131 working on source code that utilizes @ref rsocket and POSIX functions)
132 - @c TLS denotes that a method works properly with TLS-encrypted sockets
133 (most of the POSIX functions have been made to work properly with TLS,
134 but for the few rare cases of functions that can't be made to work with
135 TLS an effort has also been made to mention this using Doxygen's
136 "warning" sections in addition to omitting the TLS qualifier)
138 @par Getting started with a few simple examples
140 This is an example of connecting to an HTTP server, using the "GET" command
141 to request the home page (using HTTP/1.0), then receiving-and-displaying the
142 resulting web page's contents via STDOUT (or sending an error message to
143 STDERR). Finally, we exit with an EXIT_SUCCESS (or EXIT_FAILURE) code.
146 #include <iostream> // std::cout, std::cerr, std::endl, etc.
147 #include <randolf/rex>
148 #include <randolf/rsocket>
150 int main(int argc, char *argv[]) {
152 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
153 r.connect("www.example.com", 80);
154 r.sendline("GET / HTTP/1.0");
155 r.sendline("Host: www.example.com");
156 r.sendline("Connection: close");
158 while (r.is_open()) {
159 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
160 } // -x- while data -x-
162 } catch (const randolf::rex::xALL e) {
163 std::cerr << "Socket exception: " << e.what() << std::endl;
165 } catch (const std::exception e) {
166 std::cerr << "Other exception: " << e.what() << std::endl;
170 } // -x- int main -x-
173 Parameter stacking is supported (with methods that return @c rsocket*); in
174 this example, notice that semicolons (";") and "r." references are omittted
175 (when compared with the above):
178 #include <iostream> // std::cout, std::cerr, std::endl, etc.
179 #include <randolf/rex>
180 #include <randolf/rsocket>
182 int main(int argc, char *argv[]) {
184 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
185 r.connect("www.example.com", 80)
186 ->sendline("GET / HTTP/1.0")
187 ->sendline("Host: www.example.com")
188 ->sendline("Connection: close")
190 while (r.is_open()) {
191 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
192 } // -x- while data -x-
194 } catch (const randolf::rex::xALL e) {
195 std::cerr << "Socket exception: " << e.what() << std::endl;
197 } catch (const std::exception e) {
198 std::cerr << "Other exception: " << e.what() << std::endl;
202 } // -x- int main -x-
207 This is meant to be a comprehensive socket class for C++, which is intended
208 to make socket I/O coding easier for developers with some key features:
210 - easy conversion from C-style POSIX sockets due to API consistency
211 - transparent TLS support (OpenSSL dependency)
212 - keys and certificate chains can also be loaded from memory
213 - SNI support (see the @ref rsocket_sni class for more information)
214 - underlying socket handle is accessible (via the @ref socket_fd() method)
215 - socket options are easier to get and to set
216 - sensible support for future or unknown socket options
217 - errors are presented as ~100 separate exception classes: @ref rex::rex
218 - one parent exception class makes it easier to catch all socket errors
219 - a few exceptions groups are also provided to catch groups of errors
220 - new buffers are returned as std::shared_ptr's to eliminate memory leaks
221 - constructors with sensible defaults help to simplify coding
222 - documentation includes code samples (with @c \#include lines as needed)
223 - debug output with helpful output (and option to set output file handle)
224 - low-overhead is considered (this is why there's a bit more overloading)
225 - thread-safety is noted where it is absolutely available (if any caution
226 is warranted, it will also be noted)
227 - can send ASCIIZ strings (a.k.a., C-strings) without needing to specify
229 - can send @c std::string (which tracks its own string length)
230 - each socket can optionally have a @ref name arbitrarily assigned (e.g.,
231 by an algorithm, or even populated with the connecting user's name, or
232 whatever purpose the development goals find it useful for)
234 Additional features that are not part of the typical POSIX standard, but
235 deserve special mention because they are needed so often:
237 - easy access to internal I/O counters (see @ref rsocket_io for details)
238 - your rsocket_io structure can be updated automatically by rsocket's
239 destructor after underlying socket is closed (see @c net_io_final()
241 - printf(), printfline(), vprintf(), vprintfline() // Formatted strings
242 - with automatic EoL sequence substitution (and/or the addition of)
243 - recv_asciiz(), send_asciiz() // ASCIIZ string I/O operations
244 - recv_byte() , send_byte() // 8-bit byte operations (LSB/MSB is N/A)
245 - recv_struct(), send_struct() // Multi-byte operations
246 - recv_uint16(), send_uint16() // 16-bit operations with LSB/MSB variants
247 - recv_uint32(), send_uint32() // 32-bit operations with LSB/MSB variants
248 - recv_uint64(), send_uint64() // 64-bit operations with LSB/MSB variants
249 - recvline(), recv_rline(), sendline() // ASCII text line I/O operations
250 - class-wide configurable newline sequence (defaults to @e autodetect)
251 - @ref rsocket_group class for grouping rsockets (and automatic inclusion
252 by accept() and accept4() methods; this may be a different group from
253 whichever group the parent rsocket is in, if it's even in one)
254 - option to send data to all rsocket objects in the rsocket_group
255 - with support from the rsocket_mux class (for multiplexing operations)
256 - automatic naming policies (possibly like net_io() formatting style)
258 Some advanced features are planned that exceed what the basic socket I/O
259 functions provide, but are also needed:
261 - recv_uint128()/send_uint128() // 128-bit operations w/ LSB/MSB variants
262 - recv_uint(n)/send_uint(n) where "n" specifies the number of bits (which
263 must be a multiple of 8), with LSB/MSB variants
264 - auto-detection of inbound TLS connection (this is turned off by default)
265 - This is not the same as STARTTLS (an application-level command, for
266 which the @ref tls_do_handshake() method will likely be used)
267 - simple timing tracking options using timing_start() and timing_stop()
268 methods, the results of which can be retrieved with timing_get() or a
269 similarly-named group of methods
271 Other features that are not a high priority:
273 - internal support for portability to Microsoft Windows, which is a major
274 undertaking that I know will be time-consuming since Windows Sockets
275 exhibit some nuanced behaviours and are not consistent with POSIX
276 sockets APIs that are used by Linux, UNIX, MacOS, and pretty much all
277 other Operating Systems. Because of this, MS-Windows portability just
278 isn't a high priority for me (without sufficient demand and sufficient
279 funding so I can commit my time without missing mortgage payments,
280 student loan payments {for my kids}, various living expenses, etc.).
283 The @c __time_t structure must be 64-bit (or larger). This becomes espcially
284 important for the @ref recvline method, in addition to Year 2038 compliance.
288 I use the term "ASCIIZ string" to indicate an array of characters that's
289 terminated by a 0 (a.k.a., null). Although this is very much the same as a
290 C-string, the difference is that in many API functions a C-string must often
291 be accompanied by its length value. When referring to an ASCIIZ string, I'm
292 intentionally indicating that the length of the string is not needed because
293 the string is null-terminated. (This term was also commonly used in assembly
294 language programming in the 1970s, 1980s, and 1990s, and as far as I know is
295 still used by machine language programmers today.)
297 UTF-8 works without any problems as it is backward-compatible to 8-bit ASCII,
298 and because @c std::string uses 8-bit bytes to store strings internally. Do
299 keep in mind that the manipulation of UTF-8 substrings will require working
300 with UTF-8 codepoints that may consume one or more bytes, which is beyond the
301 scope of the impartial (to UTF-8 and ASCII) functionality that this rsocket
304 UTF-8 newline codepoints NEL (c2 85), LS (e2 80 a8), and PS (e2 80 a9) garner
305 no special handling at this time - unlike CR/LF (0d/0a) - and are merely
306 treated as non-newline codepoints. There is a possibility of adding support
307 for this in the future, but additional research and planning is required to
308 make sure this works properly. What is most likely is that some UTF-8 flags
309 will be added to support each of these (which will probably be disabled by
310 default) that will be integrated into the readline() methods. This also
311 depends on how widely used these particular codepoints are, and pending
312 further research to determine whether these really are supposed to be used
313 functionally as newlines...
315 So far, there are two UTF-8 codepoints that absolutely are not functional,
316 yet which a small number of people have mistakenly assumed are:
317 - <sup>C</sup><sub>R</sub> = superscript "C" with subscript "R" (e2 90 9d)
318 - <sup>N</sup><sub>L</sub> = superscript "N" with subscript "L" (e2 90 a4)
320 The special characters above are intended to represent the Carriage-Return
321 and New-Line respectively in documentation such as ASCII character reference
322 charts, which were used mostly by IBM in the 1970s and 1980s for instruction
323 manuals (and in other documentation), and also on a few keyboard overlays.
327 I created this class to make it easier to write internet server daemons. I
328 started out using C-style socket functions (because C++ doesn't come with a
329 socket class), but I found that I didn't enjoy mixing something as important
330 and detailed as socket I/O in a procedural way into the object-oriented
331 paradigm that C++ provides.
333 After looking for existing solutions (none of which served as comprehensive
334 replacements for socket I/O), I embarked on creating the rsocket class, and
335 then I began to understand why this probably hadn't been done -- it's a
336 massive undertaking, primarily because there are a lot of functions that are
337 needed to handle socket I/O. Further, [at the time of this writing] the @c
338 sockaddr_storage structure wasn't as widely used as it should be, and so
339 information about it tended to be scarce, incomplete, or incorrect (further
340 research, and diving down into some pretty deep "rabbit holes," was required
341 to understand this properly, which was worthwhile because it resulted in
342 having transparent support for IPv4 and IPv6 without breaking backward
343 compatibility for code expecting specific structures).
345 Moving error codes into exceptions is also a major effort because they are
346 diverse and plentiful, and there are so many errors that can occur at various
347 stages for many different reasons. There are also a few outlier functions
348 that require slightly different approaches to error handling due to subtly
349 different rules for handling their errors, and so the exception-generation
350 wasn't as straight-forward as one might optimistically expect, but this is
351 one of the many benefits of the object-oriented programming pardigm because
352 handling edge cases internally results in a consistent error-handling
353 interface using exceptions that also simplifies the source code. (Need to
354 handle a specific set of conditions? Catch the relevant exceptions for those
355 cases in an inner set of exceptions, and just catch all the others in a more
356 general way without the added complexity of repeatedly checking for errors
357 every step along the way.)
359 So, I dedicated time to make this work, and with the intention of making it
360 an open source project once I got it into a state that's ready for the
361 general public. This required putting my other C++ projects on hold, which
362 was fine because they didn't have strict deadlines and using this socket
363 class in them will speed up development in the long-term anyway, so it's
364 clearly worth the effort to me ... and I sincerely hope that my efforts will
365 be helpful to others too.
367 My background in programming began when I was a young child, teaching myself
368 BASIC and then machine language (when I found BASIC to be too limited) before
369 moving on to other languages like Perl and Java many years later. Eventually
370 I circled around to C (which I chose to learn the hard way by writing some
371 PostgreSQL extensions) and then C++ a few years after that. I have a lot of
372 experience with socket communications, including fully-featured DNS resolver
373 library code in machine language for Novell's NetWare that used C calling
374 conventions and supported varargs, which worked well for the few developers
375 who needed or wanted it.
378 - 2022-Nov-09 v1.00 Initial version
379 - 2022-Nov-26 v1.00 Moved exception handling to a separate class
380 - 2022-Nov-28 v1.00 Completed readline/send functionality
381 - 2022-Dec-03 v1.00 Added endianness transparency
382 - 2022-Dec-04 v1.00 Added printf() support
383 - 2022-Dec-24 v1.00 Added socket MUXing
384 - 2023-Feb-22 v1.00 Added TLS/SSL support
385 - 2023-Mar-10 v1.00 Added TLS-SNI support
386 - 2023-Apr-19 v1.00 Added TLS certificate loading from memory
387 - 2023-May-24 v1.00 Added support for clang++ compilation
388 - 2023-Jun-09 v1.00 Improvements to dynamic internal read buffering
389 - 2023-Oct-31 v1.00 Improvements to various classes
390 - 2024-Feb-21 v1.00 Added is_buffered() method
391 - 2024-Mar-31 v1.00 Completed @ref recvline (implemented a work-around for
392 a subtle SSL_peek failure to extract additional data
393 when a user at an end-point is communicating with
394 "icanon" mode enabled)
395 - 2024-May-24 v1.00 Changed source code to accomodate @c clang++ compiler
396 - 2024-Jun-04 v1.00 Added @c POSIX and @c TLS qualifiers to all applicable
397 methods (Doxygen incorporates into the documentation)
398 - 2024-Oct-23 v1.00 Various minor improvements to the documentation since
400 - 2024-Nov-05 v1.00 Added eol_consumed_seq() method
401 - 2024-Nov-10 v1.00 Added discard() method
402 - 2024-Nov-17 v1.00 Added recv_rline() method
403 - 2024-Nov-19 v1.00 Added recv_as_string() method
404 - 2024-Nov-22 v1.00 Added discard_line() method
405 - 2024-Nov-28 v1.00 Switched internal ring buffer to a proper ring buffer
406 that's now available in the rring class (which was made
407 initially for this purpose)
408 - 2024-Dec-08 v1.00 Improved shutdown() method to handle SSL shutdown fully
409 and added a parameter to prevent calling SSL_shutdown()
410 - 2024-Dec-10 v1.00 Changed eos() method to use faster ioctl POSIX function
411 and OpenSSL's SSL_has_pending function (with TLS)
412 - 2024-Dec-10 v1.00 Added RECVLINE_FLAGS enum to expand the functionality
413 of how the recvline() and recv_rline methods deal with
414 data in specific scenarios, which helps to satisify the
415 needs of specific advanced data processing scenarios
416 - 2024-Dec-23 v1.00 Updated calls to methods that were renamed today in the
418 - 2024-Dec-28 v1.00 Improvements to internal handling of SNI in all four
419 accept() methods along with the SNI callback function,
420 and added @ref is_tls_sni_has_name() method; <s>also
421 added the @c TLS_SNI_PROMISCUOUS flag</s>
422 - 2024-Dec-30 v1.00 Added the setsockopt() method without a value (and uses
423 a zero-length value internally)
424 - 2024-Dec-31 v1.00 Removed SSL_BIO handle generation calls along with all
425 references to it because we don't need it since we're
426 already handling our own buffering directly and working
427 with OpenSSL's raw API functions directly (the extra
428 layer of abstraction through BIO therefore doesn't
429 benefit what we're doing, and also adds slight overhead
430 that will become noticeable on extremely busy systems)
431 - 2025-Jan-05 v1.00 Added send_rline() method
432 - 2025-Jan-07 v1.00 Made major improvements to the @c recv(), @c recvline()
433 and @c recv_rilne methods, and added an internal class
434 that utilizes RAII to temporarily change the timeout
435 (for the @c recvline and @c recv_rline methods), then
436 finally restores the previous timeout, and added debug
437 output support accordingly; also made major
438 improvements to all recv() and related methods in how
439 they interact with the internal ring buffers (the code
440 is more efficient and a lot simpler)
441 - 2025-Jan-08 v1.00 Updated the @ref timeout methods by adding @c direction
442 (@c SO_RCVTIMEO {default} and @c SO_SNDTIMEO options),
443 and changed @c seconds from type @c int to type @c long
444 - 2025-Jan-18 v1.00 Improved internal memory handling, optimized discard()
445 and discard() line method loops, and simplified the
446 family() method, on top of various minor improvements
447 throughout this entire class during the past week
449 @author Randolf Richardson
450 *///=========================================================================
453 // --------------------------------------------------------------------------
454 // The rsocket_group class needs access to some of our internal variables.
455 // --------------------------------------------------------------------------
456 friend class rsocket_group; // Grant the rsocket_group class access to our internals
458 // --------------------------------------------------------------------------
460 // --------------------------------------------------------------------------
461 int __socket_fd = 0; // Raw socket handle
462 int __socket_type = 0; // Socket type (e.g., SOCK_STREAM)
463 int __socket_protocol = 0; // Socket protocol (e.g., AF_INET)
464 struct sockaddr_storage* __socket_addr = (struct sockaddr_storage*)::malloc(sizeof(sockaddr_storage)); // Initialize to all elements to their default values
465 socklen_t __socket_addr_size = sizeof(sockaddr_storage); // We need to point to this (and it might be modified)
466 int __socket_backlog = SOMAXCONN; // Default backlog is 4096 on Linux, and 128 on older systems
468 // --------------------------------------------------------------------------
469 // Socket flags (internal, but with get-methods to provide read-only access).
470 // --------------------------------------------------------------------------
471 std::atomic_bool __socket_open = false; // Socket is open by way of socket() function
472 std::atomic_bool __socket_connected = false; // Socket is connected
474 // --------------------------------------------------------------------------
475 // TLS flags and variables.
476 // --------------------------------------------------------------------------
477 std::atomic_bool __tls = false; // Indicates whether socket I/O is encrypted with OpenSSL
478 SSL_CTX* __tls_ctx = nullptr; // TLS context (nullptr == no encryption initialized)
479 rsocket_sni* __tls_sni = nullptr; // SNI maps
480 bool __tls_sni_has_name = false; // Set to TRUE only if SNI received a hostname
481 bool __tls_sni_match = false; // Set to TRUE only if SNI matched in the SNI callback
482 SSL* __tls_fd = nullptr; // TLS connection structure (nullptr == no connection)
483 bool __tls_exclusive = false; // See enum TLS_FLAGS::TLS_EXCLUSIVE
484 bool __tls_egress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_EGRESS
485 bool __tls_ingress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_INGRESS
486 bool __tls_server_mode = true; // See enum TLS_FLAGS::TLS_SERVER
487 inline static std::atomic_int __fn_counter = 0; // Used in generating unique-and-threadsafe temporary filenames
488 const rsocket* __rtemplate = nullptr; // Weak pointer to rsocket that was a template for this rsocket
490 // --------------------------------------------------------------------------
491 // Keep track of test for whether this host stores integers using big endian.
492 // --------------------------------------------------------------------------
493 inline static const bool __endian_is_msb = htons(42) == 42; // TRUE==MSB / FALSE==LSB
495 // --------------------------------------------------------------------------
496 // General variables. Atomic variables are used for variables as needed to
497 // support operations in a thread-safe manner.
498 // --------------------------------------------------------------------------
499 std::string __name; // Arbitrary name of this rsocket (used by some applications)
500 std::string __name_sni; // SNI hostname (only used when tls_sni is configured)
501 std::atomic_bool __debug = false;
502 std::atomic<std::FILE*> __debug_fd = stderr;
503 std::string __debug_prefix = "rsocket-debug";
505 // --------------------------------------------------------------------------
506 // Dynamic buffer expansion for extra-long lines resolves problems with the
507 // default of 4,096 bytes for the read-ahead policy in Linux sockets, so some
508 // automatic expansion is handled by the randolf::rring class (which was
509 // originally created to simplify most of coding for the ring buffer in this
510 // randolf::rsocket class).
511 // --------------------------------------------------------------------------
512 #define RSOCKET_BUFFER_SIZE 8192 // Hard-coded default buffer size
513 size_t __buffer_size = RSOCKET_BUFFER_SIZE; // Buffer size used by recv() when a buffer size isn't provided
514 rring* __buffer = nullptr; // Ring buffer; instantiated by calling recvline() or recv_rline()
515 rring_bam* __buffer_bam = nullptr; // Map of rring's memory blocks
517 // --------------------------------------------------------------------------
518 // EoL sequence variables.
519 // --------------------------------------------------------------------------
520 static const char __CR = (char)13; // CTRL-M / ASCII 13 (0Dh) / "\r" / Used by eol() methods
521 static const char __LF = (char)10; // CTRL-J / ASCII 10 (0Ah) / "\n" / Used by eol() methods
522 static constexpr const char* __CRLF = "\x0d\x0a"; // Used by eol() methods
523 std::string __eol; // Used by recvline() method; maintained by eol() methods
524 std::string __eol_consumed_seq; // The EoL sequence that was successfully received by the most recent use of the recvline() method
525 bool __eol_adoption = true; // Whether to adopt the dynamically detected EoL sequence when __eol is empty (the default)
526 std::string __eol_out = std::string(__CRLF); // Used by sendline() method; maintained by eol() methods; also used by sendline() method
527 bool __eol_fix_printf = true; // Whether to replace "\n" sequence in printf() methods to EoL sequence
529 // --------------------------------------------------------------------------
530 // Fine-tuning for __recvline(). This is used with nanosleep().
532 // To test, also use commands in canonical mode, like this:
533 // stty -icanon && openssl s_client host:port
534 // stty -icanon && netcat host port
535 // stty -icanon && telnet host port
536 // --------------------------------------------------------------------------
537 long __recvline_timeout = 0; // Number of seconds (0 = unlimited; maximum for "long" equates to approximately 292 billion years since the UNIX/Linux epoch)
539 // --------------------------------------------------------------------------
540 // Statistical variables.
541 // --------------------------------------------------------------------------
542 rsocket_io* __io_final_addr = nullptr; // Used by ~rsocket() destructor, net_io_final(), and net_io_update()
543 std::atomic_ulong __bytes_rx = 0; // Total number of bytes received
544 std::atomic_ulong __bytes_tx = 0; // Total number of bytes transmitted
545 std::atomic_ulong __crypt_rx = 0; // Total number of encrypted bytes recevied
546 std::atomic_ulong __crypt_tx = 0; // Total number of encrypted bytes transmitted
549 /*======================================================================*//**
551 Optional flags used with rsocket's @ref recvline() and @ref recv_rline()
552 methods to specify relevant text-line reading policies/semantics.
555 *///=========================================================================
556 enum RECVLINE_FLAGS: int {
558 /*----------------------------------------------------------------------*//**
559 The RECVLINE_DEFAULT flag isn't necessary, but it's included here for
560 completeness as it accomodates programming styles that prefer to emphasize
561 when defaults are being relied upon.
562 *///-------------------------------------------------------------------------
563 RECVLINE_DEFAULT = 0,
565 /*----------------------------------------------------------------------*//**
566 The RECVLINE_NO_DISCARD_ON_OVERFLOW flag prevents data from being discarded
567 when an @ref randolf::rex::xEOVERFLOW exception is thrown (which is caused
568 when a line of text exceeds the specified maximum line length).
571 The @c MSG_PEEK flag also prevents the automatic discarding of data,
572 regardless of whether the @ref randolf::rex::xEOVERFLOW is thrown, thus it
573 differs from this flag in multiple ways that vary with how and why it's used.
576 Data will need to be consumed in a different way, such as by attempting to
577 read into a larger buffer than what had been allocated, or by receiving
578 portions or discarding some or all that remains.
580 By default, when the @ref randolf::rex::xEOVERFLOW exception is thrown, all
581 pending data (up to and including the upcoming EoL sequence, or the EoS,
582 whichever comes first) is discarded.
584 *///-------------------------------------------------------------------------
585 RECVLINE_NO_DISCARD_ON_OVERFLOW = 1,
587 /*----------------------------------------------------------------------*//**
588 The RECVLINE_PARTIAL flag receives an incomplete line of text and returns it
589 instead of throwing the @ref randolf::rex::xEOVERFLOW exception, and causes
590 the @ref eol_consumed_seq() method to return an empty string (which indicates
591 that no EoL sequence was consumed).
592 @see eol_consumed_seq()
593 *///-------------------------------------------------------------------------
594 RECVLINE_PARTIAL = 2,
596 }; // -x- enum RECVLINE_FLAGS -x-
598 /*======================================================================*//**
600 Optional flags used with various methods to determine whether they will throw
601 an @ref randolf::rex::xETIMEDOUT exception or merely return a @c nullptr, an
602 empty set, or a 0 (zero) when a timeout duration elapses.
604 If you're not sure of whether to use @ref TIMEOUT_EMPTY, @ref TIMEOUT_NULL,
605 or @ref TIMEOUT_ZERO in your code, then I suggest using @ref TIMEOUT_NULL
606 since @c NULL will likely be the most recognizable in most code reviews.
609 You'll know when this is an option because the method will support this.
610 *///=========================================================================
611 enum TIMEOUT_BEHAVIOUR: bool {
613 /*----------------------------------------------------------------------*//**
614 Indicate that an @ref randolf::rex::xETIMEDOUT exception should be thrown
615 when the timeout duration elapses.
616 *///-------------------------------------------------------------------------
617 TIMEOUT_EXCEPTION = true,
619 /*----------------------------------------------------------------------*//**
620 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
621 when the timeout duration elapses.
622 *///-------------------------------------------------------------------------
623 TIMEOUT_EMPTY = false,
625 /*----------------------------------------------------------------------*//**
626 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
627 when the timeout duration elapses.
628 *///-------------------------------------------------------------------------
629 TIMEOUT_NULL = false,
631 /*----------------------------------------------------------------------*//**
632 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
633 when the timeout duration elapses.
634 *///-------------------------------------------------------------------------
635 TIMEOUT_ZERO = false,
637 }; // -x- enum TIMEOUT_BEHAVIOUR -x-
639 /*======================================================================*//**
641 Optional flags used with rsocket's @ref tls() and @ref tls_ctx() methods to
642 specify relevant policies/semantics.
643 *///=========================================================================
644 enum TLS_FLAGS: int {
646 /*----------------------------------------------------------------------*//**
647 The TLS_DEFAULT flag isn't necessary, but it's included here for completeness
648 as it accomodates programming styles that prefer to emphasize when defaults
649 are being relied upon.
650 *///-------------------------------------------------------------------------
653 /*----------------------------------------------------------------------*//**
654 Only encrypted connections are permitted, initially, and all attempts to
655 begin with unencrypted connections will consistently fail.
657 Encrypted connections must begin with a cryptographic handshake packet, or
658 else the connection will be rejected as due to being reasonably assumed to be
661 If this flag isn't set, an application-level mechanism can be used to upgrade
662 to TLS encryption (this is common with connections that begin unencrypted,
663 and issue a proprietary command {such as the @c STARTTLS command in SMTP} to
664 upgrade to TLS later on {of which the @ref tls() method is used to affect an
665 ingress; see the @ref TLS_NO_INGRESS flag for additional information}).
667 Creating an exclusively @c unencrypted connection is accomplished by not
670 @see TLS_NO_EGRESS to prevent support for application-level downgrades to
671 unencrypted connections
672 *///-------------------------------------------------------------------------
675 /*----------------------------------------------------------------------*//**
676 Mid-stream upgrades to encrypted connections are not permitted (e.g., via
677 application-level initiations like the @c STARTTLS command as seen in SMTP),
678 which will also cause calls to the @ref tls() method to fail fast after plain
679 non-encrypted data has already been sent or received.
681 This flag is not necessary when the @ref TLS_EXCLUSIVE flag is set.
682 @see is_tls_ingress_okay()
683 @see tls_do_handshake()
685 *///-------------------------------------------------------------------------
688 /*----------------------------------------------------------------------*//**
689 Mid-stream downgrades to unencrypted connections are not permitted (e.g., via
690 application-level initiations like a hypothetical @c STOPTLS command as seen
691 in FTPS), which will also cause calls to the @ref tls() method to fail fast
692 after encrypted data has already been sent or received.
694 This flag may be combined with the @ref TLS_EXCLUSIVE flag to prevent a
695 connection from being downgraded programatically.
697 Although egress to an unencrypted connection doesn't occur automatically
698 (since egress can only be affected programatically to support commands at the
699 application level), this flag is useful to prevent third-party code from
700 downgrading an encrypted @ref rsocket to unencrypted.
702 Supporting unencrypted communications is strongly discouraged over public
703 networks (e.g., the internet) because unencrypted streams are trivially
704 susceptible to man-in-the-middle attacks that can alter the contents of the
705 data in both directions (which is a particularly dangerous prospect for
706 sending/receiving sensitive information).
707 @see is_tls_egress_okay()
709 *///-------------------------------------------------------------------------
712 /*----------------------------------------------------------------------*//**
713 This is a convenience flag that provides an option for developers to be more
714 clear in their use of the @ref tls() and @ref tls_ctx() methods to indicate
715 intent to rely on what is already the default.
716 @see tls_do_handshake()
718 *///-------------------------------------------------------------------------
721 /*----------------------------------------------------------------------*//**
722 Indicates that this rsocket will be for a server daemon, and to initialize a
723 new TLS context (when one isn't being provided) using OpenSSL's
724 @c TLS_server_method() function instead of OpenSSL's @c TLS_client_method()
725 function (the latter is the default because most code is anticipated to be
729 Setting the CLIENT/SERVER flag incorrectly will typically cause the following
730 error that's difficult to track down, which is usually triggered by calling
731 @ref accept, @ref accept4(), or @ref connect(), and so I hope that including
732 this error message here in this documentation will be helpful:
734 error:140C5042:SSL routines:ssl_undefined_function:called a function you should not call
737 The absence of this flag has the same effect as specifying the @ref
740 *///-------------------------------------------------------------------------
743 }; // -x- enum TLS_FLAGS -x-
746 /*======================================================================*//**
747 Return Code check, and throws an rsocket-specific exception if an error
748 occurred, which is indicated by @c -1 (a lot of example code shows @c < @c 1
749 comparisons, but we specifically test for @c -1 because the documentation
750 clearly states @c -1. This is important because a system that can support an
751 extremely high number of socket handles might, in theory, assign handles with
752 values that get interpreted as negative integers, and so less-than-zero tests
753 would result in dropped packets or dropped sockets (any such socket code that
754 allocates such handles obviously must not ever allocate @c -1 since this
755 would definitely be misinterpreted as an error).
757 If rc is not @c -1, then it is simply returned as is.
759 If n is nonzero and rc is 0, then n will override errno. (This option is
760 provided to accomodate the few socket library functions that return values
761 that are never errors, and expect the developer to rely on other means of
762 detecting whether an error occurred. This is an example of where Object
763 Oriented Programming is helpful in making things better.)
764 @returns Original value of @c rc (if no exceptions were thrown)
765 *///=========================================================================
766 const int __rc_check(
769 /// Override @c errno (if not 0)
771 /// Exception handling flags (see @ref rex::REX_FLAGS for details)
772 const int flags = rex::rex::REX_FLAGS::REX_DEFAULT) {
773 if (rc == -1 || (rc == 0 && n != 0)) {
774 const int socket_errno = n == 0 ? errno : n; // If "n" is not zero, use it instead
775 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
776 randolf::rex::mk_exception("", socket_errno, flags); // This function doesn't return (this code ends here)
779 } // -x- int __rc_check -x-
781 /*======================================================================*//**
782 Similar to @ref __rc_check(), but specialized for handling OpenSSL return
784 *///=========================================================================
785 const int __rc_check_tls(
786 /// Return code (from OpenSSL's API functions)
789 if (__debug) debug("__rc_check_tls(" + std::to_string(rc) + ") throwing exception"); // TODO: Remove this
790 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)
793 } // -x- int __rc_check -x-
795 /*======================================================================*//**
796 Internal function that opens the socket. This is used by the constructors
797 and their accompanying socket() methods.
799 Throws randolf::rex::xEALREADY exception if the socket is already open to
800 prevent multiple calls to @c open, which can be particularly problematic if
801 any of the additional function calls specify different parameters -- this
802 helps developer(s) to avoid unexpected results if they're inadvertently using
803 the same rsocket object when they intend to use different rsocket objects.
804 *///=========================================================================
806 /// Communication domain; usually one of:@n
808 /// AF_INET6 (IPv6)@n
809 /// AF_UNIX (UNIX domain sockets)
811 /// Communication semantics; usually one of:@n
812 /// SOCK_STREAM (common for TCP)@n
813 /// SOCK_DGRAM (common for UDP)
815 /// Network protocol; usually one of:@n
819 const int protocol) {
820 if (__debug) debug("socket(" + std::to_string(family)
821 + ", " + std::to_string(type)
822 + ", " + std::to_string(protocol)
825 // --------------------------------------------------------------------------
827 // --------------------------------------------------------------------------
828 if (__socket_open) throw randolf::rex::xEALREADY("EALREADY: Socket is already open"); // Socket has already been opened
829 if (family == AF_UNSPEC) throw randolf::rex::xEAFNOSUPPORT("EAFNOSUPPORT: AF_UNSPEC family (SO_DOMAIN) is not supported by socket()");
831 // --------------------------------------------------------------------------
832 // Build minimum parts of __socket_addr structure.
833 // --------------------------------------------------------------------------
834 __socket_addr->ss_family = family;
835 __socket_type = type;
836 __socket_protocol = protocol;
838 // --------------------------------------------------------------------------
839 // Create new socket handle, and save it, then set internal variable to
840 // indicate that the socket is open.
841 // --------------------------------------------------------------------------
842 __socket_fd = __rc_check(::socket(__socket_addr->ss_family, __socket_type, __socket_protocol));
843 __socket_open = true;
845 } // -x- void __socket -x-
848 /*======================================================================*//**
850 Instantiate an empty rsocket without actually opening a socket, and therefore
851 also without throwing any exceptions (useful in header-file definitions).
854 Instantiating an empty rsocket is particularly useful for header-file
855 definitions since exceptions can't be handled outside of subroutines, and
856 it's also useful for enabling debug() mode @em before setting the socket's
857 configuration with one of the socket() methods; for example:
859 randolf::rsocket r; // Empty rsocket (empty / incomplete initialization)
860 r.debug(true); // Enable debug mode
861 r.socket(...); // Required to complete rsocket initialization
866 The built-in defaults, when not provided, are as follows ("family" is also
867 known as the "communication domain"):
868 - @c family = AF_INET
869 - @c type = SOCK_STREAM
870 - @c protocol = PF_UNSPEC
872 You will need to use one of the socket(...) methods to specify socket details
873 after defining rsocket objects with empty constructors so that you can catch
874 runtime exceptions. (This also provides you with an option to enable debug
875 mode during runtime prior to attempting to open an rsocket.)
880 #include <iostream> // std::cout, std::cerr, std::endl, etc.
881 #include <randolf/rex>
882 #include <randolf/rsocket>
884 randolf::rsocket r; // <-- Empty rsocket initialization (no exceptions)
886 int main(int argc, char *argv[]) {
888 r.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Required to complete rsocket initialization
889 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
890 // ... other socket I/O operations
892 } catch (const randolf::rex::xALL e) {
893 std::cerr << "Socket exception: " << e.what() << std::endl;
895 } catch (const std::exception e) {
896 std::cerr << "Other exception: " << e.what() << std::endl;
900 } // -x- int main -x-
906 @see socket_protocol()
909 *///=========================================================================
910 rsocket() noexcept {} // -x- constructor rsocket -x-
912 /*======================================================================*//**
914 Instantiate an rsocket based on a minimal subset of the settings in the
915 specified rsocket (using it as a template), without actually opening a
916 socket, and therefore also without throwing any exceptions.
919 This constructor does not suffice as a full clone()-like operation, and is
920 minimal because it's used internally by the @ref accept() and @ref accept4()
923 Details that are absorbed from the template/source rsocket (which eliminates
924 the need to assign, set, and configure various parameters (TLS and TLS SNI
925 parameters will be copied in a passive way by default):
926 - Socket family (SO_DOMAIN)
927 - Socket type (SO_TYPE)
928 - Socket protocol (SO_PROTOCOL)
929 - TLS details (status, context {which includes loaded certificates},
930 policies specified with @ref TLS_FLAGS, etc.)
932 - TLS context (you'll still need to call `tls_ctx(r->tls_ctx())`)
933 - TLS SNI map (you'll still need to call `tls_sni(r->tls_sni())`)
936 The TLS Context will not be initialized because it needs a real socket to
937 draw from. If using TLS, you'll need to use the @ref tls() method after
938 the underlying socket has been initiated.
940 When @c flag_create_socket is set to FALSE, no exceptions will be thrown.
946 @see socket_protocol()
949 *///=========================================================================
951 /// Source rsocket object to use as a template to absorb settings from
952 const rsocket* rtemplate,
953 /// TRUE = create a new socket handle (default)@n
954 /// FALSE = don't create a new socket because a new one will be assigned or
955 /// created later (all variants of the @ref accept() methods do this)
956 const bool flag_create_socket = true) {
958 // --------------------------------------------------------------------------
959 // General socket variables.
960 // --------------------------------------------------------------------------
961 __rtemplate = rtemplate; // Used by SNI callback (TODO)
962 if (flag_create_socket) {
963 __socket(rtemplate->__socket_addr->ss_family,
964 rtemplate->__socket_type,
965 rtemplate->__socket_protocol);
966 } else { // !flag_create_socket
967 __socket_addr->ss_family = rtemplate->__socket_addr->ss_family;
968 __socket_type = rtemplate->__socket_type;
969 __socket_protocol = rtemplate->__socket_protocol;
970 } // -x- if flag_create_socket -x-
971 __name = rtemplate->__name;
973 // --------------------------------------------------------------------------
974 // TLS and SNI settings, but not whether TLS is enabled or other settings
975 // since this is at a pre-handshake stage since this constructor is mostly
976 // used by the accept() and accept4() methods before accepting a connection.
977 // --------------------------------------------------------------------------
978 __tls_ctx = rtemplate->__tls_ctx;
979 __tls_sni = rtemplate->__tls_sni;
981 // --------------------------------------------------------------------------
983 // --------------------------------------------------------------------------
984 __eol.assign( rtemplate->__eol); // Copy source std::string's contents (not a reference)
985 __eol_fix_printf = rtemplate->__eol_fix_printf;
987 } // -x- constructor rsocket -x-
989 /*======================================================================*//**
991 Instantiate an rsocket with IP/host address and [optional] port number.
993 This is either the endpoint that our underlying socket will be connecting to,
994 or it's the local address of the server daemon that our socket will listen()
995 to and accept() inbound connections from.
999 The built-in defaults, when not provided, are as follows ("family" is also
1000 known as the "communication domain"):
1001 - @c family = AF_INET
1002 - @c type = SOCK_STREAM
1003 - @c protocol = PF_UNSPEC
1005 The socket() methods do the same work as the constructors with matching
1006 arguments, and are provided as convenience methods intended to augment
1007 empty rsocket constructors used in header files, but do require an address to
1008 be specified (for protocols that need port numbers, such as TCP or UDP, a
1009 "port" number also needs to be specified since the default port 0 will result
1010 in the dynamic allocation of a port number by the system).
1012 For UNIX domain sockets use family AF_UNIX, type SOCK_STREAM, and protocol
1013 IPPROTO_IP when instantiating or opening an rsocket.
1018 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1019 #include <randolf/rex>
1020 #include <randolf/rsocket>
1022 int main(int argc, char *argv[]) {
1024 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Same as socket() method
1025 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
1026 r.bind("127.0.0.1", 32768);
1027 // ... other socket I/O operations
1029 } catch (const randolf::rex::xALL e) {
1030 std::cerr << "Socket exception: " << e.what() << std::endl;
1031 return EXIT_FAILURE;
1032 } catch (const std::exception e) {
1033 std::cerr << "Other exception: " << e.what() << std::endl;
1034 return EXIT_FAILURE;
1036 return EXIT_SUCCESS;
1037 } // -x- int main -x-
1040 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
1041 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
1042 @throws randolf::rex::xEINVAL Protocal family invalid or not available
1043 @throws randolf::rex::xEINVAL Invalid flags in type
1044 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1045 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1046 @throws randolf::rex::xENOBUFS Insufficient memory
1047 @throws randolf::rex::xENOMEM Insufficient memory
1048 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1049 supported within the specified family (a.k.a., communication domain)
1053 *///=========================================================================
1055 /// Communication domain; usually one of:@n
1056 /// AF_INET (IPv4)@n
1057 /// AF_INET6 (IPv6)@n
1058 /// AF_UNIX (UNIX domain sockets)
1060 /// Communication semantics; usually one of:@n
1061 /// SOCK_STREAM (common for TCP)@n
1062 /// SOCK_DGRAM (common for UDP)
1063 const int type = SOCK_STREAM,
1064 /// Network protocol; usually one of:@n
1068 /// PF_UNSPEC (auto-detect)
1069 const int protocol = PF_UNSPEC) {
1070 __socket(family, type, protocol);
1071 } // -x- constructor rsocket -x-
1073 /*======================================================================*//**
1075 Destructor, which closes any underlying sockets, frees any TLS structures
1076 that were allocated by OpenSSL, and performs any other necessary clean-up
1077 before finally copying the I/O statistics to a designated structure (if one
1078 was specified with the @ref net_io_final() method).
1080 Developers should take care to check that the @ref rsocket_io::is_final flag
1081 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
1082 since there's no guarantee that the destructor will necessarily be executed
1083 in a timely manner (this flag is set last, after all other statistics are
1084 copied into the structure while in a mutex-locked state).
1087 *///=========================================================================
1088 ~rsocket() noexcept {
1090 // --------------------------------------------------------------------------
1092 // --------------------------------------------------------------------------
1094 debug("destructor(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1096 debug(false); // Disable debug mode (might trigger extra clean-up in future)
1097 } // -x- if _debug -x-
1099 // --------------------------------------------------------------------------
1100 // Free memory and resources and close handles that need to be freed/closed.
1101 // --------------------------------------------------------------------------
1102 if (__tls_sni != nullptr) tls_sni(nullptr); // Remove any SNI callbacks // WE DON'T NEED THIS
1103 if (__tls_fd != nullptr) SSL_free(__tls_fd); // OpenSSL resource clean-up
1104 //if (__tls_ctx != nullptr) SSL_CTX_free(__tls_ctx); // This causes high CPU on non-encrypted connections
1105 if (__socket_fd != 0) ::close(__socket_fd); // POSIX close() is ensured with ::close()
1106 if (__buffer_bam != nullptr) ::free( __buffer_bam); // Release the rring_bam structure (C)
1107 if (__buffer != nullptr) delete __buffer; // Release the rring buffer (C++)
1108 if (__socket_addr != nullptr) ::free(__socket_addr);
1109 if (__rtemplate != nullptr) __rtemplate = nullptr;
1111 // --------------------------------------------------------------------------
1112 // Copy statistics to final location. (We do this last in case there's an
1113 // issue with locking; there shouldn't be, but if there is then at least
1114 // we're not inadvertently hanging on to any other resources at this point
1115 // that need to be closed and freed.)
1116 // --------------------------------------------------------------------------
1117 if (__io_final_addr != nullptr) {
1118 __io_final_addr->lock();
1119 __io_final_addr->bytes_rx = __bytes_rx;
1120 __io_final_addr->bytes_tx = __bytes_tx;
1121 // ->bytes_xx = <bytes in buffer; these are lost bytes that should be subtracted from __bytes_rx>
1122 __io_final_addr->crypt_rx = __crypt_rx;
1123 __io_final_addr->crypt_tx = __crypt_tx;
1124 // ->crypt_xx = <bytes in buffer; these are lost bytes that should be subtracted from __crypt_rx>
1125 __io_final_addr->is_final = true;
1126 __io_final_addr->unlock();
1127 } // -x- if __io_final_addr -x-
1129 } // -x- destructor ~rsocket -x-
1131 /*======================================================================*//**
1133 Accept new [inbound] socket connections. (This is typically used in a loop.)
1136 The resulting rsocket object is created before the actual call to the @c
1139 For faster initial connections, disabling the Nagle Algorithm (which was
1140 originally introduced to the Standards Track in 1984 by John Nagle as a
1141 solution that actually solved a lot of network congestion problems by adding
1142 a 200ms internal timeout in TCP packet assembly that made it possible to
1143 reduce the total quantity of packets being transmitted by combining multiple
1144 small portions of data, within this 200ms timeframe, into a single packet
1145 instead of sending multiple packets in rapid succession) will likely improve
1146 responsiveness from a direct end-user experience perspective. To disable the
1147 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1150 #include <netinet/tcp.h> // TCP_NODELAY
1152 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1153 randolf::rsocket* c = r->accept();
1155 This particular socket option will also be carried through automatically by
1156 the network stack and therefore also be set on the resulting client socket,
1157 so it's more efficient to set it this way.
1159 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1160 socket option on the resulting client socket, although this takes more work
1161 despite being used only where it's needed:
1163 #include <netinet/tcp.h> // TCP_CORK
1165 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1166 c->sendline("Welcome");
1167 c->sendline("=======");
1168 c->send("Login name: ");
1169 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1171 Upon the end of the packet-assembly phase, which is communicated to the
1172 network stack by setting the @c TCP_CORK socket potion to @c false, the
1173 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1174 which effectively serves as a temporary bypass of the Nagle Algorithm
1175 (this is probably better overall for optimizing internet traffic for the
1176 internet as a whole if your application doesn't need the Nagle Algorithm
1177 disabled for everything).
1180 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1181 select(), and accept()/accept4() when using multiple sockets.
1184 To prevent resource leaks, the resulting rsocket needs to be deleted after
1185 it's no longer needed.
1187 @throws randolf::rex::xEBADF The underlying socket is not open
1188 @throws randolf::rex::xECONNABORTED The connection was aborted
1189 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1190 part of the user address space
1191 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1192 @throws randolf::rex::xEHOSTUNREACH No route to host
1193 @throws randolf::rex::xEINTR Interrupted by a signal
1194 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1195 length of the address is invalid
1196 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1197 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1198 @throws randolf::rex::xENETUNREACH No route to network
1199 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1200 @throws randolf::rex::xENOBUFS Insufficient memory
1201 @throws randolf::rex::xENOMEM Insufficient memory
1202 @throws randolf::rex::xENONET Requested host is not reachable on the network
1203 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1205 @throws randolf::rex::xENOSR System ran out of stream resources
1206 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1207 doesn't refer to a socket
1208 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1209 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1210 @throws randolf::rex::xEPROTO Protocol error
1211 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1212 supported within the specified family (a.k.a., communication domain)
1213 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1214 a system call is restartable and can be intercepted-and-redirected
1215 (there is no need to catch this exception unless you are an advanced
1216 developer, in which case you likely still won't need to catch it in
1218 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1219 supported within the specified family (a.k.a., communication domain)
1220 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1221 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1222 no inbound connections are waiting
1224 @returns Newly-created rsocket object representing the connection received
1229 @see setsockopt(int, int, int)
1232 *///=========================================================================
1234 if (__debug) debug("accept(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1237 // --------------------------------------------------------------------------
1238 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1239 // new incoming connection.
1240 // --------------------------------------------------------------------------
1241 rsocket* r = new rsocket(this, false);
1243 // --------------------------------------------------------------------------
1244 // Wait for incoming connection, and update internal flags in client rsocket.
1245 // --------------------------------------------------------------------------
1246 r->__socket_fd = ::accept(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size);
1247 if (r->__socket_fd == -1) {
1248 delete r; // Memory management
1249 __rc_check(-1); // This part throws the exception
1251 r->__socket_open = true;
1252 r->__socket_connected = true;
1254 // --------------------------------------------------------------------------
1255 // Perform TLS accept() as well, but only if TLS is enabled.
1257 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1258 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1259 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1260 // --------------------------------------------------------------------------
1262 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1263 r->tls(true); // Make sure __tls_fd is configured correctly
1264 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1265 __rc_check_tls(SSL_accept(r->__tls_fd));
1266 } // -x- if __tls -x-
1269 } // -x- rsocket* accept -x-
1271 /*======================================================================*//**
1273 Accept new [inbound] socket connections. (This is typically used in a loop.)
1276 The resulting rsocket object is created before the actual call to the @c
1279 For faster initial connections, disabling the Nagle Algorithm (which was
1280 originally introduced to the Standards Track in 1984 by John Nagle as a
1281 solution that actually solved a lot of network congestion problems by adding
1282 a 200ms internal timeout in TCP packet assembly that made it possible to
1283 reduce the total quantity of packets being transmitted by combining multiple
1284 small portions of data, within this 200ms timeframe, into a single packet
1285 instead of sending multiple packets in rapid succession) will likely improve
1286 responsiveness from a direct end-user experience perspective. To disable the
1287 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1290 #include <netinet/tcp.h> // TCP_NODELAY
1292 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1293 std::shared_ptr<randolf::rsocket> c = r->accept_sp();
1295 This particular socket option will also be carried through automatically by
1296 the network stack and therefore also be set on the resulting client socket,
1297 so it's more efficient to set it this way.
1299 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1300 socket option on the resulting client socket, although this takes more work
1301 despite being used only where it's needed:
1303 #include <netinet/tcp.h> // TCP_CORK
1305 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1306 c->sendline("Welcome");
1307 c->sendline("=======");
1308 c->send("Login name: ");
1309 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1311 Upon the end of the packet-assembly phase, which is communicated to the
1312 network stack by setting the @c TCP_CORK socket potion to @c false, the
1313 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1314 which effectively serves as a temporary bypass of the Nagle Algorithm
1315 (this is probably better overall for optimizing internet traffic for the
1316 internet as a whole if your application doesn't need the Nagle Algorithm
1317 disabled for everything).
1320 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1321 select(), and accept()/accept4() when using multiple sockets.
1324 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1325 aids in the prevention of resource leaks).
1327 @throws randolf::rex::xEBADF The underlying socket is not open
1328 @throws randolf::rex::xECONNABORTED The connection was aborted
1329 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1330 part of the user address space
1331 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1332 @throws randolf::rex::xEHOSTUNREACH No route to host
1333 @throws randolf::rex::xEINTR Interrupted by a signal
1334 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1335 length of the address is invalid
1336 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1337 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1338 @throws randolf::rex::xENETUNREACH No route to network
1339 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1340 @throws randolf::rex::xENOBUFS Insufficient memory
1341 @throws randolf::rex::xENOMEM Insufficient memory
1342 @throws randolf::rex::xENONET Requested host is not reachable on the network
1343 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1345 @throws randolf::rex::xENOSR System ran out of stream resources
1346 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1347 doesn't refer to a socket
1348 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1349 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1350 @throws randolf::rex::xEPROTO Protocol error
1351 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1352 supported within the specified family (a.k.a., communication domain)
1353 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1354 a system call is restartable and can be intercepted-and-redirected
1355 (there is no need to catch this exception unless you are an advanced
1356 developer, in which case you likely still won't need to catch it in
1358 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1359 supported within the specified family (a.k.a., communication domain)
1360 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1361 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1362 no inbound connections are waiting
1364 @returns Newly-created rsocket object representing the connection received,
1365 wrapped in std::shared_ptr (a C++ smart pointer)
1370 @see setsockopt(int, int, int)
1372 *///=========================================================================
1373 std::shared_ptr<rsocket> accept_sp() {
1374 if (__debug) debug("accept_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1377 // --------------------------------------------------------------------------
1378 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1379 // new incoming connection.
1380 // --------------------------------------------------------------------------
1381 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1383 // --------------------------------------------------------------------------
1384 // Wait for incoming connection, and update internal flags in client rsocket.
1385 // --------------------------------------------------------------------------
1386 r->__socket_fd = __rc_check(::accept(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size));
1387 r->__socket_open = true;
1388 r->__socket_connected = true;
1390 // --------------------------------------------------------------------------
1391 // Perform TLS accept() as well, but only if TLS is enabled.
1393 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1394 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1395 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1396 // --------------------------------------------------------------------------
1397 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1398 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1399 r->tls(true); // Make sure __tls_fd is configured correctly
1400 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1401 __rc_check_tls(SSL_accept(r->__tls_fd));
1402 } // -x- if __tls -x-
1405 } // -x- std::shared_ptr<rsocket> accept_sp -x-
1407 /*======================================================================*//**
1409 Accept new [inbound] socket connections, with socket flags specified. (This
1410 is typically used in a loop.)
1413 The resulting rsocket object is created before the actual call to the @c
1416 For faster initial connections, disabling the Nagle Algorithm (which was
1417 originally introduced to the Standards Track in 1984 by John Nagle as a
1418 solution that actually solved a lot of network congestion problems by adding
1419 a 200ms internal timeout in TCP packet assembly that made it possible to
1420 reduce the total quantity of packets being transmitted by combining multiple
1421 small portions of data, within this 200ms timeframe, into a single packet
1422 instead of sending multiple packets in rapid succession) will likely improve
1423 responsiveness from a direct end-user experience perspective. To disable the
1424 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1427 #include <netinet/tcp.h> // TCP_NODELAY
1429 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1430 randolf::rsocket* c = r->accept4();
1432 This particular socket option will also be carried through automatically by
1433 the network stack and therefore also be set on the resulting client socket,
1434 so it's more efficient to set it this way.
1436 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1437 socket option on the resulting client socket, although this takes more work
1438 despite being used only where it's needed:
1440 #include <netinet/tcp.h> // TCP_CORK
1442 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1443 c->sendline("Welcome");
1444 c->sendline("=======");
1445 c->send("Login name: ");
1446 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1448 Upon the end of the packet-assembly phase, which is communicated to the
1449 network stack by setting the @c TCP_CORK socket potion to @c false, the
1450 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1451 which effectively serves as a temporary bypass of the Nagle Algorithm
1452 (this is probably better overall for optimizing internet traffic for the
1453 internet as a whole if your application doesn't need the Nagle Algorithm
1454 disabled for everything).
1457 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1458 select(), and accept()/accept4() when using multiple sockets.
1461 To prevent resource leaks, the resulting rsocket needs to be deleted after
1462 it's no longer needed.
1464 @throws randolf::rex::xEBADF The underlying socket is not open
1465 @throws randolf::rex::xECONNABORTED The connection was aborted
1466 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1467 part of the user address space
1468 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1469 @throws randolf::rex::xEHOSTUNREACH No route to host
1470 @throws randolf::rex::xEINTR Interrupted by a signal
1471 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1472 length of the address is invalid
1473 @throws randolf::rex::xEINVAL Invalid value in flags
1474 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1475 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1476 @throws randolf::rex::xENETUNREACH No route to network
1477 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1478 @throws randolf::rex::xENOBUFS Insufficient memory
1479 @throws randolf::rex::xENOMEM Insufficient memory
1480 @throws randolf::rex::xENONET Requested host is not reachable on the network
1481 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1483 @throws randolf::rex::xENOSR System ran out of stream resources
1484 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1485 doesn't refer to a socket
1486 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1487 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1488 @throws randolf::rex::xEPROTO Protocol error
1489 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1490 supported within the specified family (a.k.a., communication domain)
1491 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1492 a system call is restartable and can be intercepted-and-redirected
1493 (there is no need to catch this exception unless you are an advanced
1494 developer, in which case you likely still won't need to catch it in
1496 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1497 supported within the specified family (a.k.a., communication domain)
1498 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1499 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1500 no inbound connections are waiting
1502 @returns Newly-created rsocket object representing the connection received
1507 @see setsockopt(int, int, int)
1510 *///=========================================================================
1514 const int posix_flags = 0) {
1515 if (__debug) debug("accept4(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1516 + ", " + std::to_string(posix_flags)
1519 // --------------------------------------------------------------------------
1520 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1521 // new incoming connection.
1522 // --------------------------------------------------------------------------
1523 rsocket* r = new rsocket(this, false);
1525 // --------------------------------------------------------------------------
1526 // Wait for incoming connection, and update internal flags in client rsocket.
1527 // --------------------------------------------------------------------------
1528 r->__socket_fd = ::accept4(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size, posix_flags);
1529 if (r->__socket_fd == -1) {
1530 delete r; // Memory management
1531 __rc_check(-1); // This part throws the exception
1533 r->__socket_open = true;
1534 r->__socket_connected = true;
1536 // --------------------------------------------------------------------------
1537 // Perform TLS accept() as well, but only if TLS is enabled.
1539 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1540 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1541 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1542 // --------------------------------------------------------------------------
1543 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1544 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1545 r->tls(true); // Make sure __tls_fd is configured correctly
1546 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1547 __rc_check_tls(SSL_accept(r->__tls_fd));
1548 } // -x- if __tls -x-
1551 } // -x- rsocket* accept4 -x-
1553 /*======================================================================*//**
1555 Accept new [inbound] socket connections, with socket flags specified. (This
1556 is typically used in a loop.)
1559 The resulting rsocket object is created before the actual call to the @c
1562 For faster initial connections, disabling the Nagle Algorithm (which was
1563 originally introduced to the Standards Track in 1984 by John Nagle as a
1564 solution that actually solved a lot of network congestion problems by adding
1565 a 200ms internal timeout in TCP packet assembly that made it possible to
1566 reduce the total quantity of packets being transmitted by combining multiple
1567 small portions of data, within this 200ms timeframe, into a single packet
1568 instead of sending multiple packets in rapid succession) will likely improve
1569 responsiveness from a direct end-user experience perspective. To disable the
1570 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1573 #include <netinet/tcp.h> // TCP_NODELAY
1575 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1576 std::shared_ptr<randolf::rsocket> c = r->accept4_sp();
1578 This particular socket option will also be carried through automatically by
1579 the network stack and therefore also be set on the resulting client socket,
1580 so it's more efficient to set it this way.
1582 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1583 socket option on the resulting client socket, although this takes more work
1584 despite being used only where it's needed:
1586 #include <netinet/tcp.h> // TCP_CORK
1588 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1589 c->sendline("Welcome");
1590 c->sendline("=======");
1591 c->send("Login name: ");
1592 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1594 Upon the end of the packet-assembly phase, which is communicated to the
1595 network stack by setting the @c TCP_CORK socket potion to @c false, the
1596 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1597 which effectively serves as a temporary bypass of the Nagle Algorithm
1598 (this is probably better overall for optimizing internet traffic for the
1599 internet as a whole if your application doesn't need the Nagle Algorithm
1600 disabled for everything).
1603 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1604 select(), and accept()/accept4() when using multiple sockets.
1607 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1608 aids in the prevention of resource leaks).
1610 @throws randolf::rex::xEBADF The underlying socket is not open
1611 @throws randolf::rex::xECONNABORTED The connection was aborted
1612 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1613 part of the user address space
1614 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1615 @throws randolf::rex::xEHOSTUNREACH No route to host
1616 @throws randolf::rex::xEINTR Interrupted by a signal
1617 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1618 length of the address is invalid
1619 @throws randolf::rex::xEINVAL Invalid value in flags
1620 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1621 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1622 @throws randolf::rex::xENETUNREACH No route to network
1623 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1624 @throws randolf::rex::xENOBUFS Insufficient memory
1625 @throws randolf::rex::xENOMEM Insufficient memory
1626 @throws randolf::rex::xENONET Requested host is not reachable on the network
1627 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1629 @throws randolf::rex::xENOSR System ran out of stream resources
1630 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1631 doesn't refer to a socket
1632 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1633 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1634 @throws randolf::rex::xEPROTO Protocol error
1635 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1636 supported within the specified family (a.k.a., communication domain)
1637 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1638 a system call is restartable and can be intercepted-and-redirected
1639 (there is no need to catch this exception unless you are an advanced
1640 developer, in which case you likely still won't need to catch it in
1642 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1643 supported within the specified family (a.k.a., communication domain)
1644 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1645 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1646 no inbound connections are waiting
1648 @returns Newly-created rsocket object representing the connection received,
1649 wrapped in std::shared_ptr (a C++ smart pointer)
1654 @see setsockopt(int, int, int)
1656 *///=========================================================================
1657 std::shared_ptr<rsocket> accept4_sp(
1660 const int posix_flags = 0) {
1661 if (__debug) debug("accept4_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1662 + ", " + std::to_string(posix_flags)
1665 // --------------------------------------------------------------------------
1666 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1667 // new incoming connection.
1668 // --------------------------------------------------------------------------
1669 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1671 // --------------------------------------------------------------------------
1672 // Wait for incoming connection, and update internal flags in client rsocket.
1673 // --------------------------------------------------------------------------
1674 r->__socket_fd = __rc_check(::accept4(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size, posix_flags));
1675 r->__socket_open = true;
1676 r->__socket_connected = true;
1678 // --------------------------------------------------------------------------
1679 // Perform TLS accept() as well, but only if TLS is enabled.
1681 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1682 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1683 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1684 // --------------------------------------------------------------------------
1685 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1686 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1687 r->tls(true); // Make sure __tls_fd is configured correctly
1688 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1689 __rc_check_tls(SSL_accept(r->__tls_fd));
1690 } // -x- if __tls -x-
1691// if (__tls_ctx != nullptr) { r->tls(true); r->__tls = (bool)__tls; } // Make sure __tls_fd is allocated appropriately
1694 } // -x- std::shared_ptr<rsocket> accept4_sp -x-
1696 /*======================================================================*//**
1698 Override the default @ref listen backlog for this rsocket.
1701 The default backlog is SOMAXCONN (4096 on Linux, and 128 on older systems).
1703 @returns The same rsocket object so as to facilitate stacking
1706 *///=========================================================================
1708 /// Backlog queue size (0 = use SOMAXCONN, which is the system default of
1709 /// 4096 on Linux, and 128 on older systems)
1710 int backlog = 0) noexcept {
1711 __socket_backlog = backlog == 0 ? SOMAXCONN : backlog;
1713 } // -x- rsocket* backlog -x-
1715 /*======================================================================*//**
1717 Find out what this rsocket's default listen backlog is.
1718 @returns The default listen backlog
1721 *///=========================================================================
1722 int backlog() noexcept {
1723 return __socket_backlog;
1724 } // -x- int backlog -x-
1726 /*======================================================================*//**
1728 Bind this socket to the specified network address (and port number if the
1729 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1730 used for server deamons, but can also be used to control the address from
1731 which the local host will make an outbound connection via the @ref connect()
1732 method (this bound address is the address from which the endpoint will
1733 recieve your connection).
1735 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
1736 (or 1023 on some Operating Systems that test only for below 1024) in
1737 the absence of elevated access or the absence of a capability flag
1739 @throws randolf::rex::xEACCES If binding to an interface on systems that
1740 require elevated access for direct interface binding in absence of
1741 said elevated access
1742 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1743 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
1744 address is not available on any local interface
1745 @throws randolf::rex::xEBADF The underlying socket is not open
1746 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1747 part of the user address space
1748 @throws randolf::rex::xEINVAL Socket is already bound
1749 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
1750 valid for this socket's family (a.k.a., communication domain)
1751 @throws randolf::rex::xENOMEM Insufficient memory
1752 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1753 doesn't refer to a socket
1755 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1756 @throws randolf::rex::xEACCES Write permission or search permission denied
1757 on a component of the path prefix (@c AF_UNIX family)
1758 @throws randolf::rex::xELOOP Too many symbolic links encountered while
1759 resolving address (@c AF_UNIX family)
1760 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
1761 @throws randolf::rex::xENOENT One of the path's directory components doesn't
1762 exist (@c AF_UNIX family)
1763 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
1764 directory (@c AF_UNIX family)
1765 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
1767 @returns The same rsocket object so as to facilitate stacking
1772 @see bind(std::string, int)
1777 *///=========================================================================
1779 /// Socket address structure
1780 const struct sockaddr* addr,
1781 /// Length of socket structure
1782 const socklen_t addrlen = sizeof(sockaddr)) {
1783 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1785 + ", size=" + std::to_string(addrlen)
1787 if (addrlen > sizeof(sockaddr_storage)) randolf::rex::mk_exception("addlen is too large for bind()", EINVAL);
1788 __rc_check(::bind(__socket_fd, addr, addrlen));
1789 std::memcpy(__socket_addr, addr, addrlen); // No errors were returned by bind(), so now we'll copy addr to __socket_addr
1791 } // -x- rsocket* bind -x-
1793 //===========================================================================
1795 // TODO: Document usage of IP_BIND_ADDRESS_NO_PORT to support connecting
1796 // from a specific IP address without losing ephemeral port selection.
1797 // Notes: https://idea.popcount.org/2014-04-03-bind-before-connect/
1799 // TODO: Support AF_BLUETOOTH addresses
1801 // https://man7.org/linux/man-pages/man7/address_families.7.html
1803 // TODO: Support AF_IPX addresses
1805 // https://man7.org/linux/man-pages/man7/address_families.7.html
1807 // TODO: Support AF_APPLETALK addresses
1808 // Reference: https://man7.org/linux/man-pages/man7/ddp.7.html
1810 // TODO: Support AF_PACKET addresses
1811 // Reference: https://man7.org/linux/man-pages/man7/packet.7.html
1813 // TODO: Support AF_X25 addresses
1814 // Reference: https://man7.org/linux/man-pages/man7/x25.7.html
1816 // TODO: Support AF_NETLINK addresses
1817 // Reference: https://man7.org/linux/man-pages/man7/netlink.7.html
1819 /*======================================================================*//**
1821 Bind this socket to the specified network address (and port number if the
1822 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1823 used for server deamons, but can also be used to control the address from
1824 which the local host will make an outbound connection via the @ref connect()
1825 method (this bound address is address from which the endpoint will recieve
1828 In addition to the standard IPv4 (AF_INET family) and IPv6 (AF_INET6 family)
1829 support, rsocket also supports a few other binding options in a manner that
1830 makes it easy to utilize, without having to handle special implementation
1831 details (because they're handled behind-the-scenese by this rsocket class).
1833 The address string supports a number of different notations and formats,
1834 which are documented, hereunder, with examples:
1837 - Network interfaces
1838 - UNIX domain sockets
1841 @par IPv4 address notations (address family @c AF_INET)
1842 Takes the form of @c x.x.x.x where each "x" represents an octet in the range
1843 of @c 0 through @c 255 (e.g., @c 127.0.0.1).
1845 Under a proper implenetation of TCP/IP, an IPv4 address can be abbreviated
1846 to fewer octets. The following examples demonstrate this (an unabbreviated
1847 IPv4 address is included for completeness):
1848 - @c 0.0.0.1 may be abbreviated to @c 1
1849 - @c 4.0.0.1 may be abbrevaited to @c 4.1
1850 - @c 4.3.0.1 may be abbreviated to @c 4.3.1
1851 - @c 4.3.2.1 is not abbreviated (this is the most common usage)
1853 @par Example of binding to IPv4 localhost
1856 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1857 #include <randolf/rex>
1858 #include <randolf/rsocket>
1860 int main(int argc, char *argv[]) {
1862 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1863 r.bind("127.0.0.1", 32768); // <-- You are here
1864 // ... other socket I/O operations
1866 } catch (const randolf::rex::xEACCES e) {
1867 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1868 return EXIT_FAILURE;
1869 } catch (const randolf::rex::xALL e) {
1870 std::cerr << "Socket exception: " << e.what() << std::endl;
1871 return EXIT_FAILURE;
1872 } catch (const std::exception e) {
1873 std::cerr << "Other exception: " << e.what() << std::endl;
1874 return EXIT_FAILURE;
1876 return EXIT_SUCCESS;
1877 } // -x- int main -x-
1881 Specifying the IP address of @c 0.0.0.0 will bind to all IPv4 addresses that
1882 are assigned to all of the host machine's network interfaces with IPv4
1885 Specifying the IP address of @c 127.0.0.1 (localhost) is useful for serving
1886 only those applications that are running on the local host and use an IPv4
1887 socket to communicate.
1890 @par IPv6 address notations (address family @c AF_INET6)
1891 Takes the form of @c x:x:x:x:x:x:x:x where each "x" represents a segment in
1892 the range of @c 0 through @c ffff in hexadecimal (e.g., `0:0:0:0:0:0:0:1`),
1893 or `x:x:x:x:x:x:y.y.y.y` where `y.y.y.y` represents an [unabbreviated] IPv4
1894 address (which merely replaces the last two IPv6 segments).
1896 Under a proper implenetation of TCP/IP, an IPv6 address can be abbreviated
1897 to fewer segments by using a sequence of two colons (`::`) to indicate that
1898 the undefined segments are @c 0 (this abbreviation can only be used once,
1899 and may represent segments at the beginning or end, or anywhere in between).
1900 The following examples demonstrate this (an unabbreviated IPv6 address is
1901 included for completeness):
1902 - `0:0:0:0:0:0:0:1` may be abbreviated to `::1`
1903 - `0:0:0:0:0:0:2:1` may be abbreviated to `::2:1`
1904 - `8:0:0:0:0:0:2:1` may be abbreviated to `8::2:1`
1905 - `8:7:0:0:0:0:2:1` may be abbreviated to `8:7::2:1`
1906 - `8:7:0:0:0:0:0:0` may be abbreviated to `8:7::`
1907 - `8:0:0:0:0:0:0:0` may be abbreviated to `8::`
1908 - `8:7:6:5:4:3:2:1` is not abbreviated
1910 @par Example of binding to IPv6 localhost
1913 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1914 #include <randolf/rex>
1915 #include <randolf/rsocket>
1917 int main(int argc, char *argv[]) {
1919 randolf::rsocket r(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1920 r.bind("::1", 32768); // <-- You are here
1921 // ... other socket I/O operations
1923 } catch (const randolf::rex::xEACCES e) {
1924 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1925 return EXIT_FAILURE;
1926 } catch (const randolf::rex::xALL e) {
1927 std::cerr << "Socket exception: " << e.what() << std::endl;
1928 return EXIT_FAILURE;
1929 } catch (const std::exception e) {
1930 std::cerr << "Other exception: " << e.what() << std::endl;
1931 return EXIT_FAILURE;
1933 return EXIT_SUCCESS;
1934 } // -x- int main -x-
1938 Specifying the IP address of @c :: will bind to all IPv6 addresses that are
1939 assigned to all of the host machine's network interfaces with IPv6 bindings.
1941 Specifying the IP address of @c ::1 (localhost) is useful for serving only
1942 those applications that are running on the local host and use an IPv6 socket
1946 @par Interface address notation (address families @c AF_INET and @c AF_INET6)
1947 Takes the form of @c if=name where "name" represents the name of a local
1950 Interface binding is useful when binding to interfaces that aren't configured
1951 with a static IP address because they were dymamically configured via the
1952 Dynamic Host Configuration Protocol (DHCP) or Stateless Address Configuration
1953 (SLAAC), or the configuration was changed manually by an administrator and
1954 you don't want your software to handle such changes gracefully, even if the
1955 new IP address is within the scope of an entirely different CIDR. (Changing
1956 between the IPv4 and IPv6 addresses is not supported, however, due to the
1957 fundamental differences between these two address families that includes
1958 differences beyond that of IP address format, although under a proper
1959 implementation of TCP/IP the binding should survive such changes when the IP
1960 address is reverted to the initial IP address family of the bound interface.)
1962 Examples of interfaces include:
1963 - `if=lo` typical for localhost virtual network adapter
1964 - `if=bond0` typical for the first bonded virtual network adapter (used in
1965 failover and load-balancing network configurations)
1966 - `if=br0` typical for the first bridge virtual network adapter
1967 - `if=eth0` typical for the first ethernet network adapter
1968 - `if=tap0` typical for the first virtual layer 2 ethernet switch (used by
1969 VPNs to extend a remote network into the local network stack)
1970 - `if=tun0` typical for the first virtual layer 3 ethernet tunnel (used by
1971 VPNs to provide access to a remote network)
1972 - `if=wlo1` typical for the first wireless network adapter
1974 @par Example of binding to interface localhost
1977 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1978 #include <randolf/rex>
1979 #include <randolf/rsocket>
1981 int main(int argc, char *argv[]) {
1983 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1984 r.bind("if=lo", 32768); // <-- You are here
1985 // ... other socket I/O operations
1987 } catch (const randolf::rex::xEACCES e) {
1988 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1989 return EXIT_FAILURE;
1990 } catch (const randolf::rex::xALL e) {
1991 std::cerr << "Socket exception: " << e.what() << std::endl;
1992 return EXIT_FAILURE;
1993 } catch (const std::exception e) {
1994 std::cerr << "Other exception: " << e.what() << std::endl;
1995 return EXIT_FAILURE;
1997 return EXIT_SUCCESS;
1998 } // -x- int main -x-
2002 This is not a standard feature of the POSIX bind() function. This rsocket
2003 class uses the setsockopt() function behind-the-scenes to configure (enable)
2004 the @c SO_BINDTODEVICE option, and then the bind() function is called with
2005 the IPv4 address @c 0.0.0.0 if the underlying socket was initialized to the
2006 @c AF_INET family, or the IPv6 address @c :: if the underlying socket was
2007 initialized to the @c AF_INET6 family.
2009 Specifying the interface address of `if=lo` (localhost) is useful for serving
2010 only those applications that are running on the local host and use an IPv4 or
2011 IPv6 socket to communicate.
2014 @par UNIX Domain Sockets address-notation (address family @c AF_UNIX)
2015 Takes the form of @c /path where "/path" represents an absolute path on the
2016 local file system (the path can also be relative, in which case it doesn't
2017 begin with a slash (`/`) character, but extra care is needed to ensure that
2018 the path will actually be at its expected location).
2020 @par Example of binding to UNIX domain sockets
2023 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2024 #include <randolf/rex>
2025 #include <randolf/rsocket>
2027 int main(int argc, char *argv[]) {
2029 randolf::rsocket r(AF_UNIX, SOCK_STREAM);
2030 r.bind("/var/run/rsocket-test-socket.tmp"); // <-- You are here
2031 // ... other socket I/O operations
2033 } catch (const randolf::rex::xEACCES e) {
2034 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
2035 return EXIT_FAILURE;
2036 } catch (const randolf::rex::xALL e) {
2037 std::cerr << "Socket exception: " << e.what() << std::endl;
2038 return EXIT_FAILURE;
2039 } catch (const std::exception e) {
2040 std::cerr << "Other exception: " << e.what() << std::endl;
2041 return EXIT_FAILURE;
2043 return EXIT_SUCCESS;
2044 } // -x- int main -x-
2048 Normal socket I/O functionality is used to exchange data with another process
2049 that can open the same UNIX domain socket (normally on the same host,
2050 although it may not be outside the realm of possibility for future Linux
2051 kernels to support UNIX domain sockets on remote hosts).
2054 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
2055 (or 1023 on some Operating Systems that test only for below 1024) in
2056 the absence of elevated access or the absence of a capability flag
2058 @throws randolf::rex::xEACCES If binding to an interface on systems that
2059 require elevated access for direct interface binding in absence of
2060 said elevated access
2061 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
2062 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
2063 address is not available on any local interface
2064 @throws randolf::rex::xEBADF The underlying socket is not open
2065 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2066 part of the user address space
2067 @throws randolf::rex::xEINVAL Socket is already bound
2068 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
2069 valid for this socket's family (a.k.a., communication domain)
2070 @throws randolf::rex::xENOMEM Insufficient memory
2071 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2072 doesn't refer to a socket
2074 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
2075 @throws randolf::rex::xEACCES Write permission or search permission denied
2076 on a component of the path prefix (@c AF_UNIX family)
2077 @throws randolf::rex::xELOOP Too many symbolic links encountered while
2078 resolving address (@c AF_UNIX family)
2079 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
2080 @throws randolf::rex::xENOENT One of the path's directory components doesn't
2081 exist (@c AF_UNIX family)
2082 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
2083 directory (@c AF_UNIX family)
2084 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
2086 @returns The same rsocket object so as to facilitate stacking
2091 @see bind(const struct sockaddr*, const socklen_t)
2095 *///=========================================================================
2097 /// IPv4 address, IPv6 address, hostname, UNIX domain sockets path, or specialized variant (see notes, above)
2098 const std::string address,
2099 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
2100 const int port = 0) {
2101 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2103 + " port=" + std::to_string(port) + (port == 0 ? " (emphemeral or not-applicable)" : "")
2104 + ", size=" + std::to_string(__socket_addr_size)
2107//std::cout << " address: " << address << std::endl;
2108 if (address.starts_with("if=")) { // Bind to interface
2109// TODO: Use family() to simplify this section of code
2111 if (address.size() - 3 >= sizeof(ifr.ifr_name)) {} // TODO: Throw exception because ASCIIZ name will be too long
2112 address.copy(ifr.ifr_name, address.size() - 3, 3); // Copy address, excluding the "if=" portion
2113 __rc_check(::setsockopt(__socket_fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)));
2114 // TODO: Figure out how to use INADDR_ANY instead of 0 or :: (assuming this is even possible)
2115 // TODO: Test IPv6 more (telnet doesn't seem to be able to connect, and continues to work on IPv4 address)
2116// __socket_addr = *mk_sockaddr_storage(__socket_addr.ss_family == AF_INET6 ? "::" : "0", port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
2117 addrinfo ai{0, __socket_addr->ss_family, __socket_type, __socket_protocol};
2118 struct sockaddr_storage* sa = mk_sockaddr_storage(__socket_addr->ss_family == AF_INET6 ? "::" : "0", port, &ai);
2119 ::free((void*)__socket_addr);
2121 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
2122// TODO: Figure out whether to use (sockaddr*)&__socket_addr or (sockaddr*)__socket_addr
2123// } else if (address.starts_with("if_cidr=")) { // Bind to interface based on matching CIDR
2124// // REMINDER: Update static family() method as well
2125// getifaddrs: https://man7.org/linux/man-pages/man3/getifaddrs.3.html
2126// } else if (address.starts_with("if_mac=")) { // Bind to interface based on its physical/hardware machine address
2127// // REMINDER: Update static family() method as well
2128// // Resolve interface name, and then just do what if= does (above)
2131// __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
2132 addrinfo ai{0, __socket_addr->ss_family, __socket_type, __socket_protocol};
2133 struct sockaddr_storage* sa = mk_sockaddr_storage(address, port, &ai);
2134 ::free((void*)__socket_addr);
2136// TODO: Figure out whether to use (sockaddr*)&__socket_addr or (sockaddr*)__socket_addr
2137// __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
2138 __rc_check(::bind(__socket_fd, (sockaddr*)__socket_addr, __socket_addr_size));
2139// __rc_check(::bind(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
2143 } // -x- rsocket* bind -x-
2145 /*======================================================================*//**
2147 Find out what buffer size is used by the various recv() methods.
2148 @returns Buffer size (in bytes)
2149 @see buffer_size(const size_t nbytes)
2150 @see buffer_size_reset
2153 *///=========================================================================
2154 const size_t buffer_size() noexcept { return __buffer->get_size(); } // -x- size_t buffer_size -x-
2156 /*======================================================================*//**
2158 Override the default buffer size (typically 8,192 bytes) used by the various
2159 @ref recv() methods.
2161 If resetting to the compiled-in default, use the buffer_size_reset() method
2162 instead of setting the value directly. This ensures that future versions,
2163 with a different compiled-in default, will be reset to the compiled-in value.
2164 @exception std::overflow_error If the new size exceeds the amount of data
2165 that's already present in the ring buffer
2166 @returns The same rsocket object so as to facilitate stacking
2168 @see buffer_size_reset()
2170 *///=========================================================================
2171 rsocket* buffer_size(
2172 /// Size of the new buffer (in bytes)
2173 const size_t nbytes) {
2174 __buffer_size = nbytes;
2175 if (__debug) debug("buffer_size(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2176 + " nbytes=" + std::to_string(nbytes)
2178 if (__buffer != nullptr) __buffer->set_size(nbytes);
2180 } // -x- rsocket* buffer_size -x-
2182 /*======================================================================*//**
2184 Reset the default buffer size (typically 8,192 bytes) used by the various
2185 @ref recv() methods.
2187 This method is preferred for resetting to the compiled-in default instead of
2188 setting the value directly. This ensures that future versions, with a
2189 different compiled-in default, will be reset to the compiled-in value.
2190 @exception std::overflow_error If the new size exceeds the amount of data
2191 that's already present in the ring buffer
2192 @returns The same rsocket object so as to facilitate stacking
2193 @see buffer_size(const size_t nbytes)
2195 *///=========================================================================
2196 rsocket* buffer_size_reset() {
2197 __buffer_size = RSOCKET_BUFFER_SIZE;
2198 if (__debug) debug("buffer_size_reset(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2199 + " nbytes=" + std::to_string(RSOCKET_BUFFER_SIZE)
2201 if (__buffer != nullptr) __buffer->set_size(__buffer_size);
2203 } // -x- rsocket* buffer_size_reset -x-
2205 //===========================================================================
2207 // TODO: If family is AF_UNIX then also unlink the domain socket file.
2209 /*======================================================================*//**
2211 Close this rsocket. (If this rsocket was already closed, then calling this
2212 method additional times will have no effect, and will not cause exceptions to
2215 This method may throw exceptions in some circumstances, which is extremely
2216 rare. If you prefer to close without having to contend with any exceptions
2217 (e.g., while calling close() from within a destructor), the close_passive()
2218 method will return an integer indicating success/failure instead of throwing
2219 an exception, and then you'll have to handle errno manually (which is useful
2220 in destructors because any error can merely be handled procedurally).
2222 @throws randolf::rex::xEBADF The underlying socket is not open
2223 @throws randolf::rex::xEINTR Interrupted by a signal
2224 @throws randolf::rex::xEIO An I/O error occurred
2226 @returns The same rsocket object so as to facilitate stacking
2230 *///=========================================================================
2232 if (__socket_open) {
2233 __rc_check(::close(__socket_fd));
2234 __socket_open = false;
2235 } // -x- if __socket_open -x-
2237 } // -x- rsocket* close -x-
2239 /*======================================================================*//**
2241 Close this rsocket without throwing any exceptions (an error code is returned
2242 instead, which is useful while calling close_passive() from within a
2244 @returns 0 = success
2245 @returns -1 = error (errno will be set accordingly)
2248 *///=========================================================================
2249 int close_passive() noexcept {
2250 if (__socket_open) {
2251 int rc = ::close(__socket_fd);
2252 if (rc == 0) __socket_open = false;
2254 } // -x- if __socket_open -x-
2255 return 0; // Indicate success (because the socket was previously closed successfully)
2256 } // -x- int close_passive -x-
2258 /*======================================================================*//**
2260 Connect this socket to a specific endpoint (which may differ from this
2261 rsocket's address that was previously configured by the @ref bind() method).
2263 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
2264 @throws randolf::rex::xEADDRNOTAVAIL No ephemeral ports are available for
2265 assignment to unbound socket
2266 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
2267 @throws randolf::rex::xEAGAIN Insufficient entries in the routing cache
2268 @throws randolf::rex::xEALREADY Previous connection attempt not completed on
2270 @throws randolf::rex::xEBADF The underlying socket is not open
2271 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
2273 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2274 part of the user address space
2275 @throws randolf::rex::xEINPROGRESS Connection cannot be completed immediately
2276 on nonblocking socket (@c AF_UNIX family fails with xEAGAIN instead)
2277 @throws randolf::rex::xEINTR Interrupted by a signal
2278 @throws randolf::rex::xEISCONN Socket is already connected
2279 @throws randolf::rex::xENETUNREACH No route to network
2280 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2281 doesn't refer to a socket
2282 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
2283 @throws randolf::rex::xEPERM Can't connect to a broadcast address when the
2284 socket's broadcast flag isn't enabled
2285 @throws randolf::rex::xEPROTOTYPE Socket type doesn't support the requested
2286 communications protocol (e.g., connecting a UNIX domain socket of
2287 type @c SOCK_STREAM to an endpoint expecting type @c SOCK_DGRAM)
2288 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
2290 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
2291 @throws randolf::rex::xEACCES Write permission or search permission denied
2292 on a component of the path prefix (@c AF_UNIX family)
2293 @throws randolf::rex::xEAGAIN Connection cannot be completed immediately on
2294 nonblocking socket (@c AF_UNIX family)
2296 @returns The same rsocket object so as to facilitate stacking
2297 @see tls_do_handshake()
2300 *///=========================================================================
2302 /// IPv4 address, IPv6 address, hostname, or UNIX domain sockets path
2303 const std::string address,
2304 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
2305 const int port = 0) {
2306 if (__debug) debug("connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2308 + " (port=" + std::to_string(port) + ")"
2309 + ", " + std::to_string(__socket_addr_size)
2312 addrinfo ai{0, __socket_addr->ss_family, __socket_type, __socket_protocol};
2313 struct sockaddr_storage* sa = mk_sockaddr_storage(address, port, &ai);
2314 ::free((void*)__socket_addr); // Free before re-assigning new pointer
2317 __rc_check(::connect(__socket_fd, (sockaddr*)__socket_addr, __socket_addr_size), 0, rex::rex::REX_FLAGS::REX_ALT_EAGAIN);
2318 __socket_connected = true;
2320 // --------------------------------------------------------------------------
2322 // --------------------------------------------------------------------------
2324 if (__debug) debug("tls_connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2326 __rc_check_tls(SSL_connect(__tls_fd));
2327 } // -x- if __tls -x-
2330 } // -x- rsocket* connect -x-
2332 /*======================================================================*//**
2334 Find out whether debug mode is enabled.
2337 This method is threadsafe.
2338 @returns TRUE = enabled
2339 @returns FALSE = not enabled
2342 *///=========================================================================
2343 const bool debug() noexcept { return __debug; } // -x- bool debug -x-
2345 /*======================================================================*//**
2347 Debug mode. When debug mode is enabled, output is sent to stderr by default,
2348 unless a second parameter specifies a different file handle (e.g., stdout, or
2351 debug(...) returns -1 if fd can't be written to (errno will be set in
2352 accordance with std::fprintf's behaviour since std::fprintf is used for
2353 writing debug output). Normally we'd throw an exception for such errors, but
2354 with debug is a special case because debugging needs to be as quick and
2355 convenient as possible for developers.
2357 debug(...) returns -2 if writing to stderr failed when attempting to announce
2358 that fd can't be written to (as described above).
2360 debug(...) returns -3 if some other error occurs (e.g., internal formatting
2361 error {which should not happen} or memory allocation failed, in which case
2362 there's a more serious problem that will be causing other problems elsewhere
2365 Developers may add their own debug messages by using debug(std::string),
2366 which will only be written out if debug mode is enabled. This same method is
2367 used internally, so messages will be indistinguishable from developer's
2371 This method is thread-safe.
2372 @returns 0 = success
2373 @returns -1 = error writing to stream (errno will be set accordingly)
2374 @returns -2 = error writing to stderr (errno will be set accordingly)
2375 @returns -3 = error (errno will be set accordingly)
2378 *///=========================================================================
2380 /// TRUE = enable debug mode@n
2381 /// FALSE = disable debug mode (does not close any file handles)
2382 const bool debug_flag,
2383 /// File descriptor/handle to use for debug output
2384 std::FILE* fd = stderr) noexcept {
2386 int rc = debug_fd(fd); // Attempt to change debug handle
2387 if (rc != 0) return rc; // Return error id new debug handle failed
2388 time_t tm = std::time(nullptr);
2389 std::string dt(27, 0);
2390 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2392 if (debug_flag || __debug) std::fprintf(__debug_fd,
2393 "[%s] %s: Debug mode is %s\n",
2395 __debug_prefix.data(),
2396 debug_flag ? "ON" : "OFF");
2397 } catch (const std::system_error& e) { // Error writing to fd
2399 std::fprintf(stderr,
2400 "[%s] %s: error writing to stream\n",
2402 __debug_prefix.data());
2403 } catch (const std::exception& e) {
2407 } catch (const std::exception& e) {
2410 __debug = debug_flag; // Save debug flag
2412 return 0; // Indicate success (no errors)
2413 } // -x- int debug -x-
2415 /*======================================================================*//**
2417 Send the specified message as debug output (as long as debug mode is enabled;
2418 if disabled, no debug output will be sent).
2419 @returns 0 = success
2420 @returns -1 = error writing to stream (errno will be set accordingly)
2421 @returns -2 = error writing to stderr (errno will be set accordingly)
2422 @returns -3 = error (errno will be set accordingly)
2426 *///=========================================================================
2428 /// Debug message as an ASCIIZ string
2429 const char* msg) noexcept {
2430 return __debug ? __debug_out(std::string(msg)) : 0;
2431 } // -x- int debug -x-
2433 /*======================================================================*//**
2434 @copydoc debug(const char*)
2436 *///=========================================================================
2438 /// Debug message as an std::string object
2439 const std::string msg) noexcept {
2440 return __debug ? __debug_out(msg) : 0;
2441 } // -x- int debug -x-
2443 /*======================================================================*//**
2445 Find out which file descriptor/handle is used for debug output.
2446 @returns file handle/descriptor currently used for debug output
2447 *///=========================================================================
2448 const std::FILE* debug_fd() noexcept { return __debug_fd; } // Get debug-output file handle
2450 /*======================================================================*//**
2452 Specify a different file descriptor/handle to use for debug output
2453 @returns 0 = success
2454 @returns -1 = error writing to stream (errno will be set accordingly)
2455 @returns -2 = error writing to stderr (errno will be set accordingly)
2456 @returns -3 = error (errno will be set accordingly)
2457 *///=========================================================================
2459 /// File descriptor/handle to use for debug output
2460 std::FILE* fd) noexcept { // Set debug-output file handle
2461 if (__debug_fd != fd) {
2462 debug("Current debug-output file handle unset"); // For this message, we only want to send this out if debug mode is enabled
2463 __debug_fd = fd; // Save new debug-output file handle
2464 return debug("New debug-output file handle set"); // For this message, we only want to send this out if debug mode is enabled
2466 return 0; // Indicate success (no errors)
2467 } // -x- int debug_fd -x-
2469 /*======================================================================*//**
2471 Find out what the current prefix is set to that's used in debug output.
2473 This may be set at any time, including before or after enabling or disabling
2475 @returns debug prefix string
2478 *///=========================================================================
2479 const std::string debug_prefix() noexcept { return __debug_prefix; } // -x- std::string debug_prefix -x-
2481 /*======================================================================*//**
2483 Change the prefix used in debug output.
2484 @returns The same rsocket object so as to facilitate stacking
2487 *///=========================================================================
2488 rsocket* debug_prefix(
2489 /// New debug prefix string
2490 const std::string prefix) noexcept {
2491 __debug_prefix = prefix;
2493 } // -x- std::string debug_prefix -x-
2496 /*----------------------------------------------------------------------*//**
2498 Internal debug-output function. This doesn't check __debug flag because it's
2499 expected that the normal debug() methods are taking care of this.
2500 @returns 0 = success
2501 @returns -1 = error writing to stream (errno will be set accordingly)
2502 @returns -2 = error writing to stderr (errno will be set accordingly)
2503 @returns -3 = error (errno will be set accordingly)
2506 *///-------------------------------------------------------------------------
2507 const int __debug_out(
2508 /// Debugging message
2509 const std::string msg) noexcept {
2511 time_t tm = std::time(nullptr);
2512 std::string dt(27, 0);
2513 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2515 std::fprintf(__debug_fd,
2518 __debug_prefix.data(),
2520 } catch (const std::system_error& e) { // Error writing to fd
2522 std::fprintf(stderr,
2523 "[%s] %s: error writing to stream\n",
2525 __debug_prefix.data());
2526 } catch (const std::exception& e) {
2530 } catch (const std::exception& e) {
2534 return 0; // Indicate success (no errors)
2535 } // -x- int __debug_out -x-
2537 // --------------------------------------------------------------------------
2538 // These are specialized internal debug output methods for presenting socket
2539 // options when they are get or set. These methods are marked as "private"
2540 // because they are really aren't useful outside of internal-use contexts.
2543 // Some of these methods are thread-safe.
2545 // These methods are thread-safe when they don't reference structures (e.g.,
2546 // integer {int}, unsigned integer {u_int}, and unsigned character {u_char}).
2548 // These methods are not necessarily thread-safe if they reference structures
2549 // unless those structures are defined with std::atomic, or they were
2550 // constructed on-the-fly as anonymous parameters.
2551 // --------------------------------------------------------------------------
2552 const int __debug_sockopt(
2553 /// Name of function or method
2554 const std::string func,
2555 /// The level at which the option resides; typically @c SOL_SOCKET
2557 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2559 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2560 + ", " + std::to_string(level)
2561 + ", " + std::to_string(option)
2565 } // -x- int __debug_sockopt -x-
2566 const int __debug_sockopt(
2567 /// Name of function or method
2568 const std::string func,
2569 /// The level at which the option resides; typically @c SOL_SOCKET
2571 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2573 /// The structure that this socket option will be set to
2575 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2576 + ", " + std::to_string(level)
2577 + ", " + std::to_string(option)
2578 + ", " + std::to_string(value)
2579 + ", size=" + std::to_string(sizeof(value))
2581 } // -x- int __debug_sockopt -x-
2582 const int __debug_sockopt(
2583 /// Name of function or method
2584 const std::string func,
2585 /// The level at which the option resides; typically @c SOL_SOCKET
2587 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2589 /// The structure that this socket option will be set to
2590 const u_int value) {
2591 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2592 + ", " + std::to_string(level)
2593 + ", " + std::to_string(option)
2594 + ", u_int{" + std::to_string(value) + "}"
2595 + ", size=" + std::to_string(sizeof(value))
2597 } // -x- int __debug_sockopt -x-
2598 const int __debug_sockopt(
2599 /// Name of function or method
2600 const std::string func,
2601 /// The level at which the option resides; typically @c SOL_SOCKET
2603 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2605 /// The structure that this socket option will be set to
2606 const u_char value) {
2607 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2608 + ", " + std::to_string(level)
2609 + ", " + std::to_string(option)
2610 + ", u_char{" + std::to_string(value) + "}"
2611 + ", size=" + std::to_string(sizeof(value))
2613 } // -x- int __debug_sockopt -x-
2614 const int __debug_sockopt(
2615 /// Name of function or method
2616 const std::string func,
2617 /// The level at which the option resides; typically @c SOL_SOCKET
2619 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2621 /// The structure that this socket option will be set to
2622 const linger* value) {
2623 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2624 + ", " + std::to_string(level)
2625 + ", " + std::to_string(option)
2626 + ", linger{l_onoff=" + std::to_string(value->l_onoff)
2627 + ", l_linger=" + std::to_string(value->l_linger) + "}"
2628 + ", " + std::to_string(sizeof(value))
2630 } // -x- int __debug_sockopt -x-
2631 const int __debug_sockopt(
2632 /// Name of function or method
2633 const std::string func,
2634 /// The level at which the option resides; typically @c SOL_SOCKET
2636 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2638 /// The structure that this socket option will be set to
2639 const timeval* value) {
2640 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2641 + ", " + std::to_string(level)
2642 + ", " + std::to_string(option)
2643 + ", timeval{tv_sec=" + std::to_string(value->tv_sec)
2644 + ", tv_usec=" + std::to_string(value->tv_usec) + "}"
2645 + ", " + std::to_string(sizeof(value))
2647 } // -x- int __debug_sockopt -x-
2648 const int __debug_sockopt(
2649 /// Name of function or method
2650 const std::string func,
2651 /// The level at which the option resides; typically @c SOL_SOCKET
2653 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2655 /// The structure that this socket option will be set to
2656 const in_addr* value) {
2657 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2658 + ", " + std::to_string(level)
2659 + ", " + std::to_string(option)
2660 + ", in_addr{" + std::to_string(value->s_addr) + "}"
2661 + ", " + std::to_string(sizeof(value))
2663 } // -x- int __debug_sockopt -x-
2664 const int __debug_sockopt(
2665 /// Name of function or method
2666 const std::string func,
2667 /// The level at which the option resides; typically @c SOL_SOCKET
2669 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2671 /// The structure that this socket option will be set to
2672 const ip_mreq* value) {
2673 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2674 + ", " + std::to_string(level)
2675 + ", " + std::to_string(option)
2676 + ", ip_mreq{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2677 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2678 + ", " + std::to_string(sizeof(value))
2680 } // -x- int __debug_sockopt -x-
2681 const int __debug_sockopt(
2682 /// Name of function or method
2683 const std::string func,
2684 /// The level at which the option resides; typically @c SOL_SOCKET
2686 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2688 /// The structure that this socket option will be set to
2689 const ip_mreq_source* value) {
2690 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2691 + ", " + std::to_string(level)
2692 + ", " + std::to_string(option)
2693 + ", ip_mreq_source{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2694 + ", source=" + std::to_string(value->imr_sourceaddr.s_addr)
2695 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2696 + ", " + std::to_string(sizeof(value))
2698 } // -x- int __debug_sockopt -x-
2699 const int __debug_sockopt(
2700 /// Name of function or method
2701 const std::string func,
2702 /// The level at which the option resides; typically @c SOL_SOCKET
2704 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2706 /// The structure that this socket option will be set to
2707 const icmp6_filter* value) {
2708 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2709 + ", " + std::to_string(level)
2710 + ", " + std::to_string(option)
2711 + ", icmp6_filter{[0]=" + std::to_string(value->icmp6_filt[0])
2712 + ", [1]=" + std::to_string(value->icmp6_filt[1])
2713 + ", [2]=" + std::to_string(value->icmp6_filt[2])
2714 + ", [3]=" + std::to_string(value->icmp6_filt[3])
2715 + ", [4]=" + std::to_string(value->icmp6_filt[4])
2716 + ", [5]=" + std::to_string(value->icmp6_filt[5])
2717 + ", [6]=" + std::to_string(value->icmp6_filt[6])
2718 + ", [7]=" + std::to_string(value->icmp6_filt[7]) + "}"
2719 + ", " + std::to_string(sizeof(value))
2721 } // -x- int __debug_sockopt -x-
2722 const int __debug_sockopt(
2723 /// Name of function or method
2724 const std::string func,
2725 /// The level at which the option resides; typically @c SOL_SOCKET
2727 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2729 /// The structure that this socket option will be set to
2730 const sockaddr_in6* value) {
2731 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2732 + ", " + std::to_string(level)
2733 + ", " + std::to_string(option)
2734 + ", sockaddr_in6{family=" + std::to_string(value->sin6_family)
2735 + ", port=" + std::to_string(value->sin6_port)
2736 + ", flowinfo=" + std::to_string(value->sin6_flowinfo)
2737 + ", addr=" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[0])
2738 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[1])
2739 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[2])
2740 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[3])
2741 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[4])
2742 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[5])
2743 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[6])
2744 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[7])
2745 + ", scope_id=" + std::to_string(value->sin6_scope_id) + "}"
2746 + ", " + std::to_string(sizeof(value))
2748 } // -x- int __debug_sockopt -x-
2749 const int __debug_sockopt(
2750 /// Name of function or method
2751 const std::string func,
2752 /// The level at which the option resides; typically @c SOL_SOCKET
2754 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2756 /// The structure that this socket option will be set to
2757 const ip6_mtuinfo* value) {
2758 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2759 + ", " + std::to_string(level)
2760 + ", " + std::to_string(option)
2761 + ", ip6_mtuinfo{addr=" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[0])
2762 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[1])
2763 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[2])
2764 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[3])
2765 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[4])
2766 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[5])
2767 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[6])
2768 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[7])
2769 + ", mtu=" + std::to_string(value->ip6m_mtu) + "}"
2770 + ", " + std::to_string(sizeof(value))
2772 } // -x- int __debug_sockopt -x-
2773 const int __debug_sockopt(
2774 /// Name of function or method
2775 const std::string func,
2776 /// The level at which the option resides; typically @c SOL_SOCKET
2778 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2780 /// The structure that this socket option will be set to
2781 const ipv6_mreq* value) {
2782 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2783 + ", " + std::to_string(level)
2784 + ", " + std::to_string(option)
2785 + ", ipv6_mreq{addr=" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[0])
2786 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[1])
2787 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[2])
2788 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[3])
2789 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[4])
2790 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[5])
2791 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[6])
2792 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[7])
2793 + ", iface=" + std::to_string(value->ipv6mr_interface) + "}"
2794 + ", " + std::to_string(sizeof(value))
2796 } // -x- int __debug_sockopt -x-
2797 const int __debug_sockopt(
2798 /// Name of function or method
2799 const std::string func,
2800 /// The level at which the option resides; typically @c SOL_SOCKET
2802 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2804 /// The structure that this socket option will be set to
2805 const group_req* value) {
2806 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2807 + ", " + std::to_string(level)
2808 + ", " + std::to_string(option)
2809 + ", group_req{iface=" + std::to_string(value->gr_interface)
2811 + ", " + std::to_string(sizeof(value))
2813 } // -x- int __debug_sockopt -x-
2814 const int __debug_sockopt(
2815 /// Name of function or method
2816 const std::string func,
2817 /// The level at which the option resides; typically @c SOL_SOCKET
2819 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2821 /// The structure that this socket option will be set to
2822 const group_source_req* value) {
2823 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2824 + ", " + std::to_string(level)
2825 + ", " + std::to_string(option)
2826 + ", group_source_req{iface=" + std::to_string(value->gsr_interface)
2827 + ", <group_source_req>}"
2828 + ", " + std::to_string(sizeof(value))
2830 } // -x- int __debug_sockopt -x-
2831 const int __debug_sockopt_other(
2832 /// Name of function or method
2833 const std::string func,
2834 /// The level at which the option resides; typically @c SOL_SOCKET
2836 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2838 /// The structure that this socket option will be set to
2839 const socklen_t size) {
2840 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2841 + ", " + std::to_string(level)
2842 + ", " + std::to_string(option)
2844 + ", " + std::to_string(size)
2846 } // -x- int __debug_sockopt_other -x-
2849 /*======================================================================*//**
2851 Discards the specified number of 8-bit bytes efficiently, and without closing
2852 the stream, and without consuming excessive quantities of memory.
2853 @exception randolf::rex::xERANGE An invalid value was specified for either
2854 the @c nbytes or @c memory_size parameter (e.g., a negative value,
2855 except for -1 with the @c nbytes parameter)
2856 @returns Number of bytes that were successfully discarded
2861 *///=========================================================================
2863 /// Number of bytes to discard@n
2864 /// 0 = use internal @ref buffer_size() @n
2865 /// -1 = all remaining data waiting to be received (in other words, this
2866 /// method will repeatedly consume all data until encountering @ref eos)
2869 /// MSG_PEEK (ignored, to prevent an endless loop)@n
2872 /// MSG_CMSG_CLOEXEC
2873 int posix_flags = 0,
2874 /// Maximum internal read size@n
2875 /// 0 = default to @ref buffer_size (when @c nbytes is larger than this size,
2876 /// multiple reads into this buffer will be utilized behind-the-scenes to
2877 /// consume the quantity of data specified by the @c nbytes parameter)
2878 const size_t memory_size = 0) {
2879 int buf_size = memory_size != 0 ? memory_size : __buffer_size;
2880 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2881 + ", " + std::to_string(nbytes)
2882 + ", " + std::to_string(posix_flags)
2883 + ", " + std::to_string(buf_size)
2886 // --------------------------------------------------------------------------
2888 // --------------------------------------------------------------------------
2889 if (nbytes == 0) nbytes = __buffer_size;
2890 else if (nbytes < -1) throw randolf::rex::xERANGE( "nbytes parameter of " + std::to_string(nbytes) + " is below -1");
2891 if (memory_size < 0) throw randolf::rex::xERANGE("memory_size parameter of " + std::to_string(memory_size) + " is below 0");
2893 // --------------------------------------------------------------------------
2894 // The MSG_PEEK flag is incompatible with this method, so we're turning it
2895 // off to prevent an endless loop. We're providing a debug message for this
2896 // in case someone's trying to track something down where they're using the
2897 // MSG_PEEK flag with a misunderstanding of the purpose of this method.
2898 // --------------------------------------------------------------------------
2899 if (posix_flags & MSG_PEEK) {
2900 posix_flags &= (~MSG_PEEK); // Remove MSG_PEEK flag if it was enabled
2901 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2902 + " ignoring incompatible MSG_PEEK flag"
2904 } // -x- if MSG_PEEK -x-
2906 // --------------------------------------------------------------------------
2907 // Internal variables.
2908 // --------------------------------------------------------------------------
2909 int discarded = 0; // Total number of bytes discarded
2912 // --------------------------------------------------------------------------
2913 // Discard loop. We are intentionally handling nbytes in two different ways
2914 // hear with a little bit more code to benefit from the trade-off of faster
2915 // overall performance.
2916 // --------------------------------------------------------------------------
2919 // --------------------------------------------------------------------------
2920 // Discard all remaining data waiting to be received.
2921 // --------------------------------------------------------------------------
2924 discarded += __recv(buf, buf_size, posix_flags);
2925 } // -x- while n -x-
2927 // --------------------------------------------------------------------------
2928 // Discard specific amount of data waiting to be received.
2929 // --------------------------------------------------------------------------
2932 int inbytes = nbytes;
2933 while (inbytes > 0 && !eos()) {
2934 int n = __recv(buf, std::min(inbytes, buf_size), posix_flags);
2937 } // -x- while nbytes -x-
2938 } // -x- if nbytes -x-
2940 // --------------------------------------------------------------------------
2941 // Ignore exception so that we can simply indicate how many bytes were
2942 // successfully received and discarded.
2943 // --------------------------------------------------------------------------
2944 } catch(const rex::xALL e) {}
2946 // --------------------------------------------------------------------------
2947 // Erase all data to prevent data from being unexpectedly exposed through any
2948 // other memory allocations later that use some or all of the same memory.
2949 // --------------------------------------------------------------------------
2950// for (int i = nbytes <= 0 ? buf_size : std::min(nbytes, buf_size); i > 0; i--) {
2951 for (int i = std::min(discarded, buf_size); i > 0; i--) {
2956 } // -x- int discard -x-
2958 /*======================================================================*//**
2960 Discards the specified number of 8-bit bytes efficiently, but stops upon
2961 encountering an EoL sequence (which will also be discarded; to find out which
2962 EoL sequence was consumed, use the @ref eol_consumed_seq method which will
2963 also return an empty string if no EoL sequence was consumed), and without
2964 closing the stream, and without consuming excessive quantities of memory.
2966 This method is particularly useful for discarding lines that are too long,
2967 which, for example, makes it possible to ignore lines that are too long (and
2968 optionally present or log an error) and continue reading the next line(s)
2971 @exception randolf::rex::xERANGE An invalid value was specified for either
2972 the @c nbytes or @c memory_size parameter (e.g., a negative value,
2973 except for -1 with the @c nbytes parameter)
2974 @returns Number of bytes that were successfully discarded
2979 *///=========================================================================
2981 /// Number of bytes to discard@n
2982 /// 0 = use internal @ref buffer_size() @n
2983 /// -1 = all remaining data waiting to be received (in other words, this
2984 /// method will repeatedly consume all data until encountering an EoL
2985 /// sequence or once @ref eos is reached)
2988 /// MSG_PEEK (ignored, to prevent an endless loop)@n
2991 /// MSG_CMSG_CLOEXEC
2992 int posix_flags = 0,
2993 /// Line timeout (in seconds)@n
2994 /// 0 = no timeout (default), unless it was configured by way of the
2995 /// @ref timeout_recvline(long) method
2997 /// Maximum internal read size@n
2998 /// 0 = default to internal buffer's base buffer size (when @c nbytes is
2999 /// larger than this size, multiple reads into this buffer will be utilized
3000 /// behind-the-scenes to consume @c nbytes quantity of data)
3001 const size_t memory_size = 0) {
3002 int buf_size = memory_size != 0 ? memory_size : __buffer->get_base_size();
3003 if (__debug) debug("discard_line(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3004 + ", " + std::to_string(nbytes)
3005 + ", " + std::to_string(posix_flags)
3006 + ", " + std::to_string(buf_size)
3009 // --------------------------------------------------------------------------
3011 // --------------------------------------------------------------------------
3012 bool infinite = nbytes == -1; // Keep track of whether nbytes was -1 before changing its value
3013 if (nbytes == 0) nbytes = __buffer_size;
3014 else if (nbytes == -1) nbytes = INT_MAX;
3015 else if (nbytes < -1) throw randolf::rex::xERANGE("nbytes parameter of " + std::to_string(nbytes) + " is below -1");
3016//std::cout << "buf_size=" << buf_size << std::endl;
3018 // --------------------------------------------------------------------------
3019 // Syntax checks as part of preparation of timeout variable for faster
3020 // comparisons within line-reading loop.
3021 // --------------------------------------------------------------------------
3022 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
3023 if (timeout == 0) timeout = __recvline_timeout;
3024 timeout = timeout == 0 ? LONG_MAX : timeout + time(0);
3026 // --------------------------------------------------------------------------
3027 // The MSG_PEEK flag is incompatible with this method, so we're turning it
3028 // off to prevent an endless loop. We're providing a debug message for this
3029 // in case someone's trying to track something down where they're using the
3030 // MSG_PEEK flag with a misunderstanding of the purpose of this method.
3031 // --------------------------------------------------------------------------
3032 if (posix_flags & MSG_PEEK) {
3033 posix_flags &= (~MSG_PEEK); // Remove MSG_PEEK flag if it was enabled
3034 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3035 + " ignoring incompatible MSG_PEEK flag"
3037 } // -x- if MSG_PEEK -x-
3039 // --------------------------------------------------------------------------
3040 // Internal variables.
3041 // --------------------------------------------------------------------------
3042 int discarded = 0; // Total number of bytes discarded
3043 int buf_size_eol = buf_size + 1;
3044 char buf[buf_size_eol];
3046// buf.resize(buf_size_eol); // Pre-allocate underlying array to prevent memory corruption when __recv writes directly into it, with +1 for straddled EoL
3048 // --------------------------------------------------------------------------
3049 // Discard loop. We are intentionally handling nbytes in two different ways
3050 // hear with a little bit more code to benefit from the trade-off of faster
3051 // overall performance.
3052 // --------------------------------------------------------------------------
3055 // --------------------------------------------------------------------------
3056 // Discard all remaining data waiting to be received.
3057 // --------------------------------------------------------------------------
3058 int len_with_eol = 0;
3061 while (len_with_eol == 0 && n != 0 && !eos()) {
3062 n = __recv(buf, buf_size_eol, posix_flags | MSG_PEEK);
3064 // --------------------------------------------------------------------------
3065 // Affect an actual discard of the correct amount of data. If "len" is -1,
3066 // then just discard everything because it means that we didn't encounter an
3067 // EoL sequence in this iteration.
3068 // --------------------------------------------------------------------------
3069 int len = eol_index(buf, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
3070//std::cout << "///// n=" << n << " len=" << len << " len_with_eol=" << len_with_eol << " buf_size_eol=" << buf_size_eol << std::endl;
3071 n = __recv(buf, len > 0 ? len_with_eol : n, posix_flags); // Consume data
3072//std::cout << " n=" << n << std::endl;
3074 } // -x- while n -x-
3076 // --------------------------------------------------------------------------
3077 // Discard specific amount of data waiting to be received.
3078 // --------------------------------------------------------------------------
3080 int inbytes = nbytes;
3081 while (len_with_eol == 0 && n != 0 && inbytes > 0 && !eos()) {
3082 n = __recv(buf, std::min(inbytes + 1, buf_size_eol), posix_flags | MSG_PEEK);
3084 // --------------------------------------------------------------------------
3085 // Affect an actual discard of the correct amount of data. If "len" is -1,
3086 // then just discard everything because it means that we didn't encounter an
3087 // EoL sequence in this iteration.
3088 // --------------------------------------------------------------------------
3089 int len = eol_index(buf, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
3090//std::cout << "||||| n=" << n << " len=" << len << " len_with_eol=" << len_with_eol << " buf_size_eol=" << buf_size_eol << std::endl;
3091 n = __recv(buf, len >= 0 ? len_with_eol : n, posix_flags); // Consume data
3092//std::cout << " n=" << n << std::endl;
3095 } // -x- while nbytes -x-
3096 } // -x- if nbytes -x-
3098 // --------------------------------------------------------------------------
3099 // Ignore exception so that we can simply indicate how many bytes were
3100 // successfully received and discarded.
3101 // --------------------------------------------------------------------------
3102 } catch(const rex::xALL e) {
3103//std::cout << "Exception: " << e.what() << std::endl;
3106//std::cout << "discarded=" << discarded << std::endl;
3108 // --------------------------------------------------------------------------
3109 // Erase all data to prevent data from being unexpectedly leaked to any other
3110 // memory allocations later that use some or all of the same memory.
3112 // Note: The +1 in the buffer size calcuation is to include a straddled EoL.
3113 // --------------------------------------------------------------------------
3114// for (int i = infinite ? buf_size : std::min(nbytes + 1, buf_size_eol); i > 0; i--) {
3115 for (int i = std::min(discarded, buf_size); i > 0; i--) {
3120 } // -x- int discard_line -x-
3122 /*======================================================================*//**
3124 Find out what the current EoL (End of Line) sequence is set to.
3126 To send an EoL sequence do not use `send(r.eol())` because it may not be
3127 initialized yet and the endpoint you're sending to may seem unresponsive or
3128 other unexpected behaviour may occur.@n
3130 To send an EoL sequence properly, use @ref sendline(); although specifying no
3131 parameters is more efficient than specifying an empty string (@c ""), the
3132 specialized @ref send_eol() method is the most efficient option for sending
3133 an EoL sequence separately.
3135 An empty EoL sequence means that CRLF will be sent by @ref sendline(), and
3136 that @ref recvline(), and @ref recv_rline() will automatically detect from
3137 one of @c CR, @c CRLF, @c LF, and @c LFCR.
3138 @returns Current EoL sequence
3148 *///=========================================================================
3149 const std::string eol() noexcept { return __eol; } // -x- std::string eol -x-
3151 /*======================================================================*//**
3153 Set EoL (End of Line) sequence. This sequence is used by recvline(),
3154 recv_rline(), sendline(), and related functions, and it defaults to an empty
3155 string which results in the EoL sequence being detected automatically
3158 - @c "" (empty string) = automatically detect on-the-fly (default)
3159 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
3160 - @c \\r (CR) = Carriage Return (typical for MacOS)
3161 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
3164 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
3165 IMAP4, FINGER, NICNAME/WHOIS, etc.).
3166 @returns The same rsocket object so as to facilitate stacking
3176 *///=========================================================================
3178 /// EoL sequence as an ASCIIZ string
3179 const char* eol) noexcept {
3180 __eol = std::string(eol);
3181 __eol_out = __eol.empty() ? __CRLF : __eol;
3183 } // -x- rsocket* eol -x-
3185 /*======================================================================*//**
3187 Set EoL (End of Line) sequence. This sequence is used by recvline(),
3188 recv_rline(), sendline(), and related functions, and it defaults to an empty
3189 string which results in the EoL sequence being detected automatically
3192 - @c "" (empty string) = automatically detect on-the-fly (default)
3193 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
3194 - @c \\r (CR) = Carriage Return (typical for MacOS)
3195 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
3198 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
3199 IMAP4, FINGER, NICNAME/WHOIS, etc.).
3200 @returns The same rsocket object so as to facilitate stacking
3209 *///=========================================================================
3211 /// EoL sequence as an std::string object
3212 const std::string eol) noexcept {
3215 } // -x- rsocket* eol -x-
3217 /*======================================================================*//**
3219 Configure EoL adoption policy for the @ref recvline() and @ref recv_rline()
3220 methods. By default, @ref rsocket is configured with the EoL adoption policy
3221 enabled alongside an empty @ref eol() sequence, which results in the default
3222 operation being that the EoL sequence automatically gets detected and updated
3223 internally upon the first use of either the @ref recvline() or
3224 @ref recv_rline method.
3226 The EoL adoption policy is only effective when the @ref eol() sequence is not
3227 defined (which is indicated by an empty EoL sequence string).
3229 The EoL sequence is updated only when the EoL sequence string is empty, and
3230 when this EoL adoption policy is enabled.
3231 @returns The same rsocket object so as to facilitate stacking
3234 @see is_eol_adoption
3240 *///=========================================================================
3241 rsocket* eol_adoption(
3242 /// TRUE = enable EoL adoption (default)@n
3243 /// FALSE = disable EoL adoption
3244 const bool flag) noexcept {
3245 __eol_adoption = flag;
3247 } // -x- rsocket* eol_adoption -x-
3249 /*======================================================================*//**
3251 Returns a String containing the EoL character sequence that was consumed by
3252 the most recent successful call to the @ref recvline() or @ref recv_rline
3253 method ("successful" in this context means that the received line was
3254 terminated by a valid EoL character sequence; otherwise the
3255 previous/unmodified value is returned).
3257 This method must not be used to determine whether the @ref recvline() method
3258 successfully consumed an EoL character sequence. The reason for this is
3259 that @ref recvline() doesn't update this string when it doesn't consume an
3260 EoL character sequence, hence interpreting its results can (and likely will)
3261 lead to false positives.
3262 @returns EoL character sequence
3268 *///=========================================================================
3269 const std::string eol_consumed_seq() noexcept { return __eol_consumed_seq; } // -x- std::string eol_consumed_seq -x-
3271 /*======================================================================*//**
3273 Configure EoL substitution policy for the @ref printf(), @ref printfline(),
3274 @ref vprintf(), and @ref vprintfline() methods. By default, @ref rsocket
3275 substitutes printf's @c \\n sequence with the EoL sequence (if defined), but
3276 this method can be used to disable this behaviour.
3278 The @c \\n sequence used in the printf format string normally coincides with
3279 the local Operating System's newline standard, which is very likely different
3280 from the @ref rsocket endpoint's newline standard, and/or the newline
3281 standard of the protocol being implemented. This policy setting makes it
3282 possible to control whether to use the configured EoL sequence when sending a
3283 formatted string to the endpoint.
3284 @returns The same rsocket object so as to facilitate stacking
3286 @see is_eol_fix_printf
3292 *///=========================================================================
3293 rsocket* eol_fix_printf(
3294 /// TRUE = enable EoL substitution (default)@n
3295 /// FALSE = disable EoL substitution
3296 const bool flag) noexcept {
3297 __eol_fix_printf = flag;
3299 } // -x- rsocket* eol_fix_printf -x-
3301 /*======================================================================*//**
3303 Finds the first instance of the EoL sequence and returns its offset (which is
3304 effectively the same as the size of the text, not including the characters
3305 that the EoL sequence is comprised of).
3308 This method is specialized primarily for internal use by the @ref recvline()
3309 and @ref recv_rline() methods, but is made available here in case there's a
3310 need to check in-memory text using this rsocket's EoL detection policy.
3311 @returns Size of EoL sequence
3312 @returns -1 if EoL sequence wasn't found
3316 *///=========================================================================
3317 const int eol_index(
3318 /// Buffer that probably contains at least one EoL sequence
3319 const std::string buffer,
3320 /// Size of string with EoL character sequence included (will be updated by this method)
3321 int* with_eol_size) noexcept {
3323 // --------------------------------------------------------------------------
3324 // An EoL character sequence was specified, so a simple search will suffice.
3325 // --------------------------------------------------------------------------
3326 if (!__eol.empty()) {
3327//std::cout << "!__eol.empty() ------------------------------ " << randolf::rtools::to_hex(__eol) << std::endl;
3328 int pos = buffer.find(__eol);
3330 *with_eol_size = pos + __eol.size(); // Update size of EoL sequence
3331//std::cout << "------0------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3334//std::cout << "------1------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3335 if (!__eol_adoption) return pos; // Return the -1 error if we're not adopting different EoF sequences on-the-fly
3336 } // -x- if !__eol.empty() -x-
3337//std::cout << "__eol.empty() ------------------------------ String is empty" << std::endl;
3339 // --------------------------------------------------------------------------
3340 // Automatic detection of EoL sequence, so a more flexible approach will be
3343 // Search for all four possible EoL sequences (as indicated by an empty EoL
3344 // character sequence string):
3346 // CRLF: Common for text lines with internet protocols such as SMTP, POP3,
3347 // IMAP4, HTTP, FINGER, WHOIS, etc. Also: DOS and OS/2 EoL sequence.
3349 // CR: MacOS EoL character.
3351 // LF: UNIX/Linux EoL character.
3353 // LFCR: Extremely rare, but I've encountered multiple instances where the
3354 // intended CRLF was reversed to LFCR, possibly due to a programming
3355 // error or an artifact of inappropriate/unintended endian conversion.
3356 // --------------------------------------------------------------------------
3357 int pos = buffer.find(__CR); // CR or CRLF
3358//std::cout << "------2------ <CR> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3359 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)
3360 *with_eol_size = pos;// + 1;
3361// *with_eol_size = 1;
3362//std::cout << "------3------ <!CR> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3364 } else if (pos >= 0) { // EoL sequence found
3365 *with_eol_size = pos + (buffer[pos + 1] == __LF ? 2 : 1); // 2=CRLF, 1=CR
3366// *with_eol_size = buffer[pos + 1] == __LF ? 2 : 1; // 2=CRLF, 1=CR
3367 if (__eol_adoption) {
3368 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
3369 __eol.resize(*with_eol_size - pos); // Truncate extra characters
3370 } // -x- if __eol_adoption -x-
3371//std::cout << "------4------ <CR/CRLF-adopted> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3372 return pos; // Either way, we're done
3375 pos = buffer.find(__LF); // LF or LFCR
3376 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)
3377 *with_eol_size = pos;// + 1;
3378// *with_eol_size = 1;
3379//std::cout << "------5------ <LF> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3381 } else if (pos >= 0) { // EoL sequence found
3382 *with_eol_size = pos + (buffer[pos + 1] == __CR ? 2 : 1); // 2=LFCR, 1=LF
3383// *with_eol_size = buffer[pos + 1] == __CR ? 2 : 1; // 2=LFCR, 1=LF
3384 if (__eol_adoption) {
3385 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
3386 __eol.resize(*with_eol_size - pos); // Truncate extra characters
3387 } // -x- if __eol_adoption -x-
3388//std::cout << "------6------ <LF/LFCR-adopted> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3389 return pos; // Either way, we're done
3392//std::cout << "------7------ <LF> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3394 } // -x- int eol_index -x-
3396 /*======================================================================*//**
3398 Find out if the stream is at its end and that this @ref rsocket's internal
3399 buffer (if one had been set up by the @ref recvline() method) is empty. This
3400 doesn't necessarily mean that the stream is closed; but rather that the
3401 endpoint just hasn't sent any more data (yet).
3403 If the stream isn't open, then this method will always return @c true to
3404 implicitly indicate that there's no data pending to be received.
3407 You can optionally specify a timeout with the timeout parameter, which will
3408 cause this method to wait for the specified period of time for pending data.
3410 It's better (more efficient) to use this method instead of the @c MSG_PEEK
3411 flag (with the various @c recv methods) to determine whether any data is
3412 waiting on the stream (e.g., data that's received by the sockets, but not by
3413 any @c recv methods yet) because this method is specialized in handling this
3414 particular condition and responds with an easy-to-use boolean flag.
3417 EoS is an acronym for: End of Stream
3419 @throws randolf::rex::xEBADF The underlying socket is not open
3420 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3421 part of the user address space
3422 @throws randolf::rex::xEINTR Interrupted by a signal
3423 @throws randolf::rex::xENOMEM Insufficient memory
3424 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3425 doesn't refer to a socket
3426 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
3427 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
3428 is a highly improbable chance that a timeout could still occur if the
3429 data is read by another thread before the `recv(..., MSG_PEEK)` call)
3430 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
3433 @returns TRUE = End of Stream (no buffered data, the stream @ref is_closed(),
3434 or there's no pending data, checked in this order)
3435 @returns FALSE = Pending data available
3441 *///=========================================================================
3443 /// Number of milliseconds to wait@n
3444 /// 0 = no waiting (default)
3445 const int timeout = 0) {
3447 // --------------------------------------------------------------------------
3448 // Check internal buffer first, if there is one, and return immediately if
3449 // the buffer is not empty.
3450 // --------------------------------------------------------------------------
3451 if (__buffer != nullptr && !__buffer->empty()) return false;
3453 // --------------------------------------------------------------------------
3454 // If socket is closed, then just indicate EoS to be done with it. No need
3455 // to fall through to timeouts as this is not applicable.
3456 // --------------------------------------------------------------------------
3457 if (!__socket_open) return true;
3459 // --------------------------------------------------------------------------
3460 // Start by checking whether OpenSSL has pending data, and if it doesn't (or
3461 // there's an error, which is also indicated by 0) then fall through to
3462 // checking the underlying socket using ioctl's FIONREAD...
3464 // Use ioctl's FIONREAD to determine if any data is waiting to be read. If
3465 // ioctl returns non-zero, then an error occurred which always equates to
3466 // EoS (otherwise the FIONREAD operation would have been successful).
3467 // --------------------------------------------------------------------------
3468 if (__tls && SSL_has_pending(__tls_fd) == 1) return false;
3469 int n = 0; // Number of bytes waiting (default to 0 in case of ioctl error)
3470 ioctl(__socket_fd, FIONREAD, &n); // Ignore return code because "n" will be zero if there's an error
3471 if (n > 0) return false; // Indicate !EoS because 0 bytes remaining means EoS
3473 // --------------------------------------------------------------------------
3474 // Timeout polling (if a timeout was specified).
3475 // --------------------------------------------------------------------------
3478 poll(POLLIN, timeout); // This is calling internally, and not ::poll (POSIX) directly
3479 return false; // EoS, because the "poll" method was triggered by new data
3480 } catch (const randolf::rex::xETIMEDOUT e) {
3481 return true; // EoS, because the "poll" method timed out while waiting for new data
3482 } // This is a specialized catch situation -- the ::recv function could still return an ETIMEDOUT error
3483 } // -x- if timeout -x-
3485 return true; // EoS, because new data was not detected
3486 } // -x- bool eos -x-
3488 /*======================================================================*//**
3490 Find out what the specified IP address string's family (@c SO_DOMAIN) is.
3491 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
3492 address is not available on any local interface
3493 @throws randolf::rex::xEAI_NONAME If @c address is an empty string
3494 @returns @c AF_UNSPEC (if family couldn't be determined)
3495 @returns @c AF_INET (IPv4 address)
3496 @returns @c AF_INET6 (IPv6 address)
3497 @returns @c AF_UNIX (UNIX Domain address)
3498 @returns ...or other family as applicable
3499 @see socket_family()
3500 @see socket_protocol()
3503 *///=========================================================================
3505 /// Address, similar to @ref bind() addressing, including non-standard "if="
3506 /// variant that names a network interface
3507 const std::string address,
3508 /// Preferred family to return first (used only with interface mode where the
3509 /// network interface is specified after the "if=" prefix); the default value
3510 /// of @c AF_UNSPEC will return the first family interface found
3511 const int preferred_family = AF_UNSPEC) {
3513 // --------------------------------------------------------------------------
3514 // Simple checks first.
3515 // --------------------------------------------------------------------------
3516 if (address.size() == 0) randolf::rex::mk_exception("", EAI_NONAME);
3517 if (address.front() == '/') return AF_UNIX;
3519 // --------------------------------------------------------------------------
3520 // if=<interface-name>: Same "interface" option that we support in bind()
3521 // --------------------------------------------------------------------------
3522 if (address.starts_with("if=")) {
3523 //std::cout << "address=" << address.substr(3).data() << std::endl; // Debug
3524 struct ifaddrs* ifaddr; // Chained interface addresses structure
3525 if (::getifaddrs(&ifaddr) == -1) { // Error occurred, so we're done here
3526 freeifaddrs(ifaddr); // Memory management
3527 randolf::rex::mk_exception("getifaddrs() error (prequisite to searching for " + address + ")", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
3528 } // -x- if getifaddrs -x
3530 // --------------------------------------------------------------------------
3531 // Iterate through interface addresses. Each address should have a different
3532 // address family/domain, and is never AF_UNSPEC (this should never happen
3533 // because it wouldn't make sense, but if the network implementation was
3534 // broken and including an AF_UNSPEC family/domain, it would be useless for
3535 // the purposes of calling the ::socket() function so our code will just
3536 // ignore it anyway.
3537 // --------------------------------------------------------------------------
3538 int first_family = AF_UNSPEC; // We're using AF_UNSPEC (0) to indicate that there isn't a family/domain
3539 for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
3540 if (ifa->ifa_addr == nullptr) continue; // No address structure, so this isn't useful to us at all
3541 if (ifa->ifa_addr->sa_family == AF_UNSPEC) continue; // Skip AF_UNSPEC family/domain, as we can't use this
3542 if (address.compare(3, -1, ifa->ifa_name) != 0) continue; // Not the interface name we're looking for
3544 // --------------------------------------------------------------------------
3545 // Return current address's family if preferred_family is not specified (if
3546 // it's set to AF_UNSPEC {0}), or if it matches the current address.
3547 // --------------------------------------------------------------------------
3548 //std::cout << "if=" << ifa->ifa_name << " family=" << ifa->ifa_addr->sa_family << std::endl; // Debug
3549 if (preferred_family == AF_UNSPEC || preferred_family == ifa->ifa_addr->sa_family) {
3550 first_family = ifa->ifa_addr->sa_family; // Arbitrarily re-using first_family variable
3551 //freeifaddrs(ifaddr); // Memory management
3552 //return first_family; // Return current family/domain (re-used first_family variable)
3554 } // -x- if preferred_family -x-
3556 // --------------------------------------------------------------------------
3557 // Keep track of only the first_family/domain that we found; if we can't find
3558 // the preferred_family, then we'll be able to return the first one we found
3559 // once the current loop is exhausted without finding a preferred familiy
3560 // (this is, in effect, a fallback).
3561 // --------------------------------------------------------------------------
3562 if (first_family == AF_UNSPEC && ifa->ifa_addr->sa_family != AF_UNSPEC)
3563 first_family = ifa->ifa_addr->sa_family;
3564 //std::cout << " first_family=" << first_family << std::endl; // Debug
3565 } // -x- for *ifa -x-
3567 // --------------------------------------------------------------------------
3568 // Return first_family/domain (or throw an exception if it wasn't found).
3569 // --------------------------------------------------------------------------
3570 freeifaddrs(ifaddr); // Memory management
3571 if (first_family == AF_UNSPEC) randolf::rex::mk_exception("Interface (" + address.substr(3) + ") doesn't exist" + (preferred_family == AF_UNSPEC ? "" : " or the preferred family/domain (" + std::to_string(preferred_family) + ") is not supported"), EADDRNOTAVAIL);
3572 return first_family;
3574 } // -x- if /^if=/ -x-
3576 // --------------------------------------------------------------------------
3577 // Attempt to detect IPv4 or IPv6 using a simple shortcut with ::inet_pton(),
3578 // which requires some additional memory allocation (that the simple checks
3579 // from earlier don't need).
3580 // --------------------------------------------------------------------------
3581 char buf[sizeof(struct in6_addr)]; // Binary address storage
3582 if (::inet_pton(AF_INET, address.data(), buf) == 1) return AF_INET;
3583 if (::inet_pton(AF_INET6, address.data(), buf) == 1) return AF_INET6;
3585 // --------------------------------------------------------------------------
3586 // Throw xEAI_FAMILY exception because no family/domain was found.
3587 // --------------------------------------------------------------------------
3588 randolf::rex::mk_exception("No family/domain available for: " + address, EAI_FAMILY);
3589 } // -x- int family -x-
3591 /*======================================================================*//**
3593 Get peer name returns the address of the socket as a sockaddr_storage
3596 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
3597 that aids in the prevention of resource leaks).
3599 @throws randolf::rex::xEBADF The underlying socket is not open
3600 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3601 part of the user address space
3602 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3603 valid for this socket's family (a.k.a., communication domain)
3604 @throws randolf::rex::xENOBUFS Insufficient memory
3605 @throws randolf::rex::xENOTCONN Underlying socket is not connected
3606 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3607 doesn't refer to a socket
3609 @returns sockaddr_storage structure
3612 *///=========================================================================
3613 const std::shared_ptr<sockaddr_storage> getpeername() {
3614 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3615 socklen_t len = sizeof(sockaddr_storage);
3616 __rc_check(::getpeername(__socket_fd, (struct sockaddr*)sa.get(), &len));
3618 } // -x- std::shared_ptr<sockaddr_storage> getpeername -x-
3620 /*======================================================================*//**
3622 Get peer name returns the address of the socket as a std::string object.
3624 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3625 (e.g., because the @c family doesn't utilize or support an address
3626 {or the format isn't known}
3627 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3628 @throws randolf::rex::xEBADF The underlying socket is not open
3629 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3630 part of the user address space
3631 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3632 valid for this socket's family (a.k.a., communication domain)
3633 @throws randolf::rex::xENOBUFS Insufficient memory
3634 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3635 @throws randolf::rex::xENOTCONN Underlying socket is not connected
3636 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3637 doesn't refer to a socket
3639 @returns string representation of peer name
3641 *///=========================================================================
3642 const std::string getpeername_ntop() {
3643 sockaddr_storage sa;
3644 socklen_t len = sizeof(sockaddr_storage);
3645 __rc_check(::getpeername(__socket_fd, (sockaddr*)&sa, &len));
3646 return inet_ntop(&sa);
3647 } // -x- std::string getpeername_ntop -x-
3649 /*======================================================================*//**
3651 Get specified "sockaddr_storage" structure's address as a "sockaddr"
3652 structure, for sockets in one of the supported families:
3656 - AF_UNIX (Domain socket path)
3657 - AF_PACKET (Ethernet node/mac. address)
3659 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3660 (e.g., because the @c family doesn't utilize or support an address
3661 {or the format isn't known}
3663 @returns pointer to sockaddr structure within provided sockaddr_storage
3665 @see mk_sockaddr_storage
3671 *///=========================================================================
3672 static sockaddr* getsockaddr(
3673 /// The @c sockaddr_storage structure wherein @c sockaddr will be referenced from
3674 const sockaddr_storage* sa) {
3675 switch (sa->ss_family) {
3676 case AF_INET: // IPv4 address
3677 return (sockaddr*)&(((struct sockaddr_in*)sa)->sin_addr);//->sin_addr);
3678 case AF_INET6: // IPv6 address
3679 return (sockaddr*)&(((struct sockaddr_in6*)sa)->sin6_addr);//->sin6_addr);
3680 case AF_UNIX: // UNIX (path) domain socket
3681 return (sockaddr*)&(((struct sockaddr_un*)sa)->sun_path);//->sun_path);
3682 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
3683 // case /*AF_LINK* /18: // Link layer interface (arp)
3685 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3686 return (sockaddr*)&(((struct sockaddr_ll*)sa)->sll_addr);//->sll_addr);
3687 } // -x- switch family -x-
3688 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY"); // Everything else
3689 } // -x- sockaddr* getsockaddr -x-
3691 /*======================================================================*//**
3693 Get socket name returns the address of the socket as a "sockaddr_storage"
3696 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
3697 that aids in the prevention of resource leaks).
3699 @throws randolf::rex::xEBADF The underlying socket is not open
3700 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3701 part of the user address space
3702 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3703 valid for this socket's family (a.k.a., communication domain)
3704 @throws randolf::rex::xENOBUFS Insufficient memory
3705 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3706 doesn't refer to a socket
3708 @returns sockaddr_storage structure
3711 *///=========================================================================
3712 const std::shared_ptr<sockaddr_storage> getsockname() {
3713 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3714 socklen_t len = sizeof(sockaddr_storage);
3715 __rc_check(::getsockname(__socket_fd, (struct sockaddr*)sa.get(), &len));
3717 } // -x- std::string getsockname -x-
3719 /*======================================================================*//**
3721 Get socket name returns the name of the socket as a std::string object.
3723 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3724 (e.g., because the @c family doesn't utilize or support an address
3725 {or the format isn't known}
3726 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3727 @throws randolf::rex::xEBADF The underlying socket is not open
3728 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3729 part of the user address space
3730 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3731 valid for this socket's family (a.k.a., communication domain)
3732 @throws randolf::rex::xENOBUFS Insufficient memory
3733 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3734 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3735 doesn't refer to a socket
3737 @returns string representation of socket name
3739 *///=========================================================================
3740 const std::string getsockname_ntop() {
3741 sockaddr_storage sa;
3742 socklen_t len = sizeof(sockaddr_storage);
3743 __rc_check(::getsockname(__socket_fd, (sockaddr*)&sa, &len));
3744 return inet_ntop(&sa);
3745 } // -x- std::string getsockname_ntop -x-
3747 /*======================================================================*//**
3749 Get socket option details in the form of an integer.
3751 Most options return an integer, with the remaining options returning a
3752 pointer to a structure wrapped in a std::shared_ptr (a C++ smart pointer that
3753 aids in the prevention of resource leaks); the primitive types int, u_int,
3754 and u_char are not wrapped in C++ smart pointers because returning them by
3755 value is more efficient since allocating memory for an entire structure isn't
3759 It is up to the developer to know which return type is needed according to
3760 the socket option, otherwise an exception will likely be thrown -- in some
3761 cases where the wrong type will seem to work, this is due to the wrong type
3762 providing a minimally sufficient amount of memory for the storage of the
3763 resulting structure.
3766 The returned values/structures are not marked as "const" because they may
3767 need to be modified for unforseen purposes. Modifying the returend values or
3768 structures is fine because they are intended to be independent and are
3769 expected to have no direct impact on the rsocket's internal variables and
3772 Templates in C++ aren't used here because they don't work properly for our
3773 needs due to neccesity to handle both fundamental types and structures; it
3774 turns out that mixing these is impossible when using the same function name,
3775 so this just doesn't work as well as we'd like it to. (We may try to work on
3776 this again in the future as time permits to provide an additional method for
3777 obtaining socket options, but with the intention of never removing this
3778 current set of methods so as to ensure backward compatibility in the future.)
3780 @throws randolf::rex::xEBADF The underlying socket is not open
3781 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3782 part of the user address space
3783 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3784 valid for this socket's family (a.k.a., communication domain)
3785 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
3787 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3788 doesn't refer to a socket
3790 @returns socket option value
3791 @see setsockopt(const int, const int)
3792 @see setsockopt(const int, const int, const int)
3794 *///=========================================================================
3796 /// The level at which the option resides; typically @c SOL_SOCKET
3798 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3801 socklen_t slt{sizeof(value)};
3802 __rc_check(::getsockopt(__socket_fd, level, option, &value, &slt));
3803 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3805 } // -x- int getsockopt_int -x-
3807 /*======================================================================*//**
3809 Get socket option details in the form of an unsigned integer.
3810 @copydetails getsockopt_int(const int, const int)
3811 @returns socket option value
3812 @see setsockopt(const int, const int, const u_int)
3814 *///=========================================================================
3815 u_int getsockopt_u_int(
3816 /// The level at which the option resides; typically @c SOL_SOCKET
3818 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3821 socklen_t slt{sizeof(value)};
3822 __rc_check(::getsockopt(__socket_fd, level, option, &value, &slt));
3823 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3825 } // -x- u_int getsockopt_u_int -x-
3827 /*======================================================================*//**
3829 Get socket option details in the form of an unsigned character.
3830 @copydetails getsockopt_int(const int, const int)
3831 @returns socket option value
3832 @see setsockopt(const int, const int, const u_char)
3834 *///=========================================================================
3835 u_char getsockopt_u_char(
3836 /// The level at which the option resides; typically @c SOL_SOCKET
3838 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3841 socklen_t slt{sizeof(value)};
3842 __rc_check(::getsockopt(__socket_fd, level, option, &value, &slt));
3843 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3845 } // -x- u_char getsockopt_u_char -x-
3847 /*======================================================================*//**
3849 Get socket option details in the form of a structure.
3850 @copydetails getsockopt_int(const int, const int);
3851 @returns socket option structure wrapped in std::shared_ptr
3852 @see setsockopt(const int, const int, const linger&)
3854 *///=========================================================================
3855 std::shared_ptr<linger> getsockopt_linger(
3856 /// The level at which the option resides; typically @c SOL_SOCKET
3858 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3860 std::shared_ptr value = std::make_shared<linger>();
3861 socklen_t slt{sizeof(value)};
3862 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3863 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3865 } // -x- linger getsockopt_linger -x-
3867 /*======================================================================*//**
3868 @copydoc getsockopt_linger(const int, const int)
3869 @see setsockopt(const int, const int, const timeval&)
3871 *///=========================================================================
3872 std::shared_ptr<timeval> getsockopt_timeval(
3873 /// The level at which the option resides; typically @c SOL_SOCKET
3875 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3877 std::shared_ptr value = std::make_shared<timeval>();
3878 socklen_t slt{sizeof(value)};
3879 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3880 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3882 } // -x- timeval getsockopt_timeval -x-
3884 /*======================================================================*//**
3885 @copydoc getsockopt_linger(const int, const int)
3886 @see setsockopt(const int, const int, const in_addr&)
3888 *///=========================================================================
3889 std::shared_ptr<in_addr> getsockopt_in_addr(
3890 /// The level at which the option resides; typically @c SOL_SOCKET
3892 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3894 std::shared_ptr value = std::make_shared<in_addr>();
3895 socklen_t slt{sizeof(value)};
3896 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3897 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3899 } // -x- in_addr getsockopt_in_addr -x-
3901 /*======================================================================*//**
3902 @copydoc getsockopt_linger(const int, const int)
3903 @see setsockopt(const int, const int, const ip_mreq&)
3905 *///=========================================================================
3906 std::shared_ptr<ip_mreq> getsockopt_ip_mreq(
3907 /// The level at which the option resides; typically @c SOL_SOCKET
3909 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3911 std::shared_ptr value = std::make_shared<ip_mreq>();
3912 socklen_t slt{sizeof(value)};
3913 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3914 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3916 } // -x- ip_mreq getsockopt_ip_mreq -x-
3918 /*======================================================================*//**
3919 @copydoc getsockopt_linger(const int, const int)
3920 @see setsockopt(const int, const int, const ip_mreq_source&)
3922 *///=========================================================================
3923 std::shared_ptr<ip_mreq_source> getsockopt_ip_mreq_source(
3924 /// The level at which the option resides; typically @c SOL_SOCKET
3926 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3928 std::shared_ptr value = std::make_shared<ip_mreq_source>();
3929 socklen_t slt{sizeof(value)};
3930 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3931 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3933 } // -x- ip_mreq_source getsockopt_ip_mreq_source -x-
3935 /*======================================================================*//**
3936 @copydoc getsockopt_linger(const int, const int)
3937 @see setsockopt(const int, const int, const icmp6_filter&)
3939 *///=========================================================================
3940 std::shared_ptr<icmp6_filter> getsockopt_icmp6_filter(
3941 /// The level at which the option resides; typically @c SOL_SOCKET
3943 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3945 std::shared_ptr value = std::make_shared<icmp6_filter>();
3946 socklen_t slt{sizeof(value)};
3947 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3948 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3950 } // -x- icmp6_filter getsockopt_icmp6_filter -x-
3952 /*======================================================================*//**
3953 @copydoc getsockopt_linger(const int, const int)
3954 @see setsockopt(const int, const int, const sockaddr_in6&)
3956 *///=========================================================================
3957 std::shared_ptr<sockaddr_in6> getsockopt_sockaddr_in6(
3958 /// The level at which the option resides; typically @c SOL_SOCKET
3960 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3962 std::shared_ptr value = std::make_shared<sockaddr_in6>();
3963 socklen_t slt{sizeof(value)};
3964 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3965 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3967 } // -x- sockaddr_in6 getsockopt_sockaddr_in6 -x-
3969 /*======================================================================*//**
3970 @copydoc getsockopt_linger(const int, const int)
3971 @see setsockopt(const int, const int, const ip6_mtuinfo&)
3973 *///=========================================================================
3974 std::shared_ptr<ip6_mtuinfo> getsockopt_ip6_mtuinfo(
3975 /// The level at which the option resides; typically @c SOL_SOCKET
3977 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3979 std::shared_ptr value = std::make_shared<ip6_mtuinfo>();
3980 socklen_t slt{sizeof(value)};
3981 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3982 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3984 } // -x- ip6_mtuinfo getsockopt_ip6_mtuinfo -x-
3986 /*======================================================================*//**
3987 @copydoc getsockopt_linger(const int, const int)
3988 @see setsockopt(const int, const int, const ipv6_mreq&)
3990 *///=========================================================================
3991 std::shared_ptr<ipv6_mreq> getsockopt_ipv6_mreq(
3992 /// The level at which the option resides; typically @c SOL_SOCKET
3994 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3996 std::shared_ptr value = std::make_shared<ipv6_mreq>();
3997 socklen_t slt{sizeof(value)};
3998 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3999 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4001 } // -x- ipv6_mreq getsockopt_ipv6_mreq -x-
4003 /*======================================================================*//**
4004 @copydoc getsockopt_linger(const int, const int)
4005 @see setsockopt(const int, const int, const group_req&)
4007 *///=========================================================================
4008 std::shared_ptr<group_req> getsockopt_group_req(
4009 /// The level at which the option resides; typically @c SOL_SOCKET
4011 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4013 std::shared_ptr value = std::make_shared<group_req>();
4014 socklen_t slt{sizeof(value)};
4015 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4016 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4018 } // -x- group_req getsockopt_group_req -x-
4020 /*======================================================================*//**
4021 @copydoc getsockopt_linger(const int, const int)
4022 @see setsockopt(const int, const int, const group_source_req&)
4024 *///=========================================================================
4025 std::shared_ptr<group_source_req> getsockopt_group_source_req(
4026 /// The level at which the option resides; typically @c SOL_SOCKET
4028 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4030 std::shared_ptr value = std::make_shared<group_source_req>();
4031 socklen_t slt{sizeof(value)};
4032 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4033 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4035 } // -x- group_source_req getsockopt_group_source_req -x-
4037 /*======================================================================*//**
4038 @copydoc getsockopt_linger(const int, const int)
4039 @see setsockopt(const int, const int)
4040 @see setsockopt(const int, const int, const int)
4042 *///=========================================================================
4043 template<class T> std::shared_ptr<T> getsockopt_other(
4044 /// The level at which the option resides; typically @c SOL_SOCKET
4046 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4048 std::shared_ptr value = std::make_shared<T>();
4049 socklen_t slt{sizeof(value)};
4050 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4051 if (__debug) __debug_sockopt_other("getsockopt_other(socket{0x", level, option, socklen_t(sizeof(value)));
4053 } // -x- T getsockopt_other -x-
4055 /*======================================================================*//**
4057 Get underlying socket's address as a std::string, for sockets in one of the
4062 - AF_UNIX (Domain socket path)
4063 - AF_PACKET (Ethernet node/mac. address)
4065 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
4066 (e.g., because the @c family doesn't utilize or support an address
4067 {or the format isn't known}
4068 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
4069 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
4071 @returns string representation of underlying socket's address
4072 @see inet_ntop(sockaddr_storage*) static method
4075 *///=========================================================================
4076// const std::string inet_ntop() { return inet_ntop((sockaddr_storage*)&__socket_addr); } // -x- std::string inet_ntop -x-
4077 const std::string inet_ntop() { return inet_ntop((sockaddr_storage*)__socket_addr); } // -x- std::string inet_ntop -x-
4079 /*======================================================================*//**
4081 Get specified "sockaddr_storage" structure's address as a std::string, for
4082 sockets in one of the supported families:
4086 - AF_UNIX (Domain socket path)
4087 - AF_PACKET (Ethernet node/mac. address)
4089 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
4090 (e.g., because the @c family doesn't utilize or support an address
4091 {or the format isn't known}
4092 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
4093 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
4095 @returns string representation of underlying socket's address
4096 @see inet_ntop() non-static method
4099 *///=========================================================================
4100 static const std::string inet_ntop(
4101 /// Source structure that [should] contain address data
4102 sockaddr_storage* sa) {
4104 switch (sa->ss_family) {
4105 case AF_INET: // IPv4 address
4107 char ntop[sizeof(sockaddr_in)]{0};
4108 const char* rc = ::inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), ntop, sizeof(ntop));
4109// TODO: if (rc == nullptr) _rc...
4110 str = std::string(ntop);
4113 case AF_INET6: // IPv6 address
4115 char ntop[sizeof(sockaddr_in6)]{0};
4116 const char* rc = ::inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), ntop, sizeof(ntop));
4117// TODO: if (rc == nullptr) _rc...
4118 str = std::string(ntop);
4121 case AF_UNIX: // UNIX (path) domain socket
4122 str = std::string(((struct sockaddr_un *)sa)->sun_path);
4124 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
4125 // case /*AF_LINK* /18: // Link layer interface (arp)
4127 case AF_PACKET: // Packet (ethernet) address (packet capturing)
4128 str = to_mac(((struct sockaddr_ll *)sa)->sll_addr);
4130 default: // Everything else
4131 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY");
4132 } // -x- switch family -x-
4134 } // -x- std::string inet_ntop -x-
4136 /*======================================================================*//**
4138 Find out whether an internal read buffer was allocated (this is most likely
4139 triggered by an attempt to read a line of text).
4141 The buffer_size() methods report on how much memory was allocated for the
4142 internal read buffer or to set its size (in bytes).
4143 @returns TRUE = an internal read buffer was allocated
4144 @returns FALSE = an internal read buffer was not allocated
4146 @see buffer_size(const size_t nbytes)
4148 *///=========================================================================
4149 const bool is_buffered() noexcept { return __buffer != nullptr; } // -x- bool is_buffered -x-
4151 /*======================================================================*//**
4153 Find out whether the underlying socket is not open (which may not be the same
4154 as specifically "closed" since a newly instantiated empty socket begins in a
4155 "not open" state despite the underlying socket not explicitly having been
4157 @returns TRUE = not open
4158 @returns FALSE = open
4162 *///=========================================================================
4163 const bool is_closed() noexcept { return !__socket_open; } // -x- bool is_closed -x-
4165 /*======================================================================*//**
4167 Find out whether the underlying socket is connected with/to an endpoint.
4168 @returns TRUE = open
4169 @returns FALSE = not open
4174 *///=========================================================================
4175 const bool is_connected() noexcept { return __socket_connected; } // -x- bool is_connected -x-
4177 /*======================================================================*//**
4179 Find out whether the default byte order for this host is LSB (small endian).
4181 If you're trying to choose which endian type to use when designing a new
4182 internet protocol, then big endian is normally the better option. However,
4183 if your new protocol will only be used by hardware that all share the same
4184 endianness, then that endianness is probably the more optimal option since it
4185 will translate to an overall lesser consumption of CPU cycles by reducing or
4186 eliminating endianness conversions.
4187 @returns TRUE = LSB (little endian)
4188 @returns FALSE = MSB (big endian / network byte order)
4191 *///=========================================================================
4192 const bool is_endian_lsb() noexcept { return !__endian_is_msb; } // -x- bool is_endian_lsb -x-
4194 /*======================================================================*//**
4196 Find out whether the default byte order for this host is MSB (big endian).
4198 Big endian is the standard known as "network byte order" that's also used in
4199 various header fields in internet packets.
4201 If you're trying to choose which endian type to use when designing a new
4202 internet protocol, then big endian is normally the better option. However,
4203 if your new protocol will only be used by hardware that all share the same
4204 endianness, then that endianness is probably the more optimal option since it
4205 will translate to an overall lesser consumption of CPU cycles by reducing or
4206 eliminating endianness conversions.
4207 @returns TRUE = MSB (big endian / network byte order)
4208 @returns FALSE = LSB (little endian)
4211 *///=========================================================================
4212 const bool is_endian_msb() noexcept { return __endian_is_msb; } // -x- bool is_endian_msb -x-
4214 /*======================================================================*//**
4216 Find out if the EoL adoption policy is enabled for the @ref recvline() method
4217 (see the @ref eol_adoption method to find out how the dynamically-detected
4218 EoL sequence gets adopted, and under what conditions).
4219 @returns TRUE = EoL adoption is enabled
4220 @returns FALSE = EoL adoption is disabled
4228 *///=========================================================================
4229 const bool is_eol_adoption() noexcept {
4230 return __eol_adoption;
4231 } // -x- bool is_eol_adoption -x-
4233 /*======================================================================*//**
4235 Find out if the EoL substitution policy is enabled for the @ref printf(),
4236 @ref printfline(), @ref vprintf(), and @ref vprintfline() methods.
4237 @returns TRUE = EoL substitution is enabled
4238 @returns FALSE = EoL substitution is disabled
4248 *///=========================================================================
4249 const bool is_eol_fix_printf() noexcept {
4250 return __eol_fix_printf;
4251 } // -x- bool is_eol_fix_printf -x-
4253 /*======================================================================*//**
4255 Find out whether the underlying socket is open.
4256 @returns TRUE = open
4257 @returns FALSE = not open
4260 *///=========================================================================
4261 const bool is_open() noexcept { return __socket_open; } // -x- bool is_open -x-
4263 /*======================================================================*//**
4265 Find out whether encrypted communications is enabled or disabled.
4266 @returns TRUE = encrypted communications is enabled
4267 @returns FALSE = encrypted communications is disabled
4268 @see tls(bool, TLS_FLAGS)
4271 *///=========================================================================
4272 const bool is_tls() noexcept { return __tls; } // -x- bool is_tls -x-
4274 /*======================================================================*//**
4276 Find out whether TLS context is in TLS_CLIENT mode.
4277 @returns TRUE = TLS context is in TLS_CLIENT mode
4278 @returns FALSE = TLS context is in TLS_SERVER mode
4282 *///=========================================================================
4283 const bool is_tls_client_mode() noexcept { return !__tls_server_mode; } // -x- bool is_tls_client_mode -x-
4285 /*======================================================================*//**
4287 Find out whether egress from encryption (to unencrypted mode) is allowed.
4288 @returns TRUE = egress from encrypted communications is allowed
4289 @returns FALSE = egress from encrypted communications is not allowed
4293 *///=========================================================================
4294 const bool is_tls_egress_okay() noexcept { return __tls_egress; } // -x- bool is_tls_egress_okay -x-
4296 /*======================================================================*//**
4298 Find out whether encrypted communications is exclusive.
4299 @returns TRUE = encrypted communications is exclusive
4300 @returns FALSE = encrypted communications is not exclusive
4303 *///=========================================================================
4304 const bool is_tls_exclusive() noexcept { return __tls_exclusive; } // -x- bool is_tls_exclusive -x-
4306 /*======================================================================*//**
4308 Find out whether ingress to encryption (from unencrypted mode) is allowed.
4309 @returns TRUE = ingress to encrypted communications is allowed
4310 @returns FALSE = ingress to encrypted communications is not allowed
4314 *///=========================================================================
4315 const bool is_tls_ingress_okay() noexcept { return __tls_ingress; } // -x- bool is_tls_ingress_okay -x-
4317 /*======================================================================*//**
4319 Find out whether TLS context is in TLS_SERVER mode.
4320 @returns TRUE = TLS context is in TLS_SERVER mode
4321 @returns FALSE = TLS context is in TLS_CLIENT mode
4325 *///=========================================================================
4326 const bool is_tls_server_mode() noexcept { return __tls_server_mode; } // -x- bool is_tls_server_mode -x-
4328 /*======================================================================*//**
4330 Find out whether SNI (Server Name Identifier) is enabled (configured, which
4331 implies that an internal callback function was also set up).
4332 @returns TRUE = SNI is enabled
4333 @returns FALSE = SNI is disabled
4337 *///=========================================================================
4338 const bool is_tls_sni() noexcept { return __tls_sni != nullptr; } // -x- bool is_tls_sni -x-
4340 /*======================================================================*//**
4342 Find out whether SNI (Server Name Identifier) received an empty hostname or
4343 if no hostname was provided.
4344 @returns TRUE = SNI hostname was received and it was not empty
4345 @returns FALSE = SNI hostname was not received or it was empty
4346 @see is_tls_sni_match()
4349 *///=========================================================================
4350 const bool is_tls_sni_has_name() noexcept { return __tls_sni_has_name; } // -x- bool is_tls_sni_has_name -x-
4352 /*======================================================================*//**
4354 Find out whether SNI (Server Name Identifier) was matched, which means that
4355 we're using one of the supplementary TLS certificates that are included in
4356 the associated @ref rsocket_sni object as separate TLS contexts.
4358 When this method returns @c TRUE, it means the @c OpenSSL callback (which
4359 occurs during the TLS handshake process) completed the TLS handshake with one
4360 of the TLS certificates that's included in this @c rsocket's @ref rsocket_sni
4361 object instead of the primary TLS certificate assigned to this @c rsocket,
4362 and this @c rsocket is using the respective TLS context instead of the
4363 primary (default) one.
4364 @returns TRUE = SNI was matched
4365 @returns FALSE = SNI wasn't matched
4368 @see tls_sni_has_name
4370 *///=========================================================================
4371 const bool is_tls_sni_match() noexcept { return __tls_sni_match; } // -x- bool is_tls_sni_match -x-
4373 /*======================================================================*//**
4375 Enable listening mode for this rsocket to prepare it to accept() new inbound
4378 The backlog defaults to SOMAXCONN (4096 on Linux, and 128 on older systems),
4379 which is common on most systems. If a higher value is supplied that exceeds
4380 @c kern.somaxconn, the kernel will automatically override the backlog with
4381 the value stored in @c kern.somaxconn without generating any errors or
4384 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
4385 @throws randolf::rex::xEADDRINUSE No ephemeral ports are available for
4386 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
4387 but it really isn't)
4388 @throws randolf::rex::xEBADF The underlying socket is not open
4389 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4390 doesn't refer to a socket
4391 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
4392 @returns The same rsocket object so as to facilitate stacking
4401 *///=========================================================================
4403 /// Backlog queue size (0 = uses rsocket's default; see @ref backlog for more
4404 /// details about this); specifying a non-zero backlog also updates rocket's
4405 /// internal default (SOMAXCONN is 4096 on Linux, and 128 on older systems)
4407 if (__debug) debug("listen(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4408 + ", " + std::to_string(backlog)
4411 // --------------------------------------------------------------------------
4412 // Use rsocket's default backlog if not specified, otherwise update it
4413 // with the new value specified here.
4414 // --------------------------------------------------------------------------
4415 if (backlog = 0) backlog = __socket_backlog;
4416 else __socket_backlog = backlog;
4418 // --------------------------------------------------------------------------
4419 // Configure underlying socket to queue up to "backlog" inbound connections.
4420 // --------------------------------------------------------------------------
4421 __rc_check(::listen(__socket_fd, backlog));
4424 } // -x- rsocket* listen -x-
4426 /*======================================================================*//**
4428 Convert an IPv4 address, IPv6 address, ethernet packet, or UNIX domain
4429 socket to a sockaddr_storage structure.
4431 If service_name is an absolute path (that begins with a "/" charcter) and the
4432 family is set to AF_UNSPEC (the default), then the resulting family will be
4436 This method utilizes the results of getaddrinfo().
4438 Other families like AF_LINK and AF_PACKET should work, but haven't been
4439 tested thoroughly. The additional support we provide for IPv4 and IPv6
4440 addresses is to copy the port number into the resulting structure (as a
4444 The resulting sockaddr_storage structure is wrapped in std::shared_ptr (a C++
4445 smart pointer that aids in the prevention of resource leaks).
4448 This method is thread-safe.
4450 @throws randolf::rex::xEAI_ADDRFAMILY If specified network host doesn't have
4451 any addresses in the specified address family
4452 @throws randolf::rex::xEAI_AGAIN Temporary failure code from DNS server (try
4454 @throws randolf::rex::xEAI_BADFLAGS Invalid flags in @c hints.ai_flags (or
4455 `hints.ai_flags` included @c AI_CANONNAME with @c nullptr as @c name)
4456 @throws randolf::rex::xEAI_FAIL Permanent failure code from DNS server
4457 @throws randolf::rex::xEAI_FAMILY The specified family is not supported
4458 @throws randolf::rex::xEAI_MEMORY Out of memory
4459 @throws randolf::rex::xEAI_NONAME If node_name is nullptr or an empty string
4460 @throws randolf::rex::xEAI_SERVICE The specified service is not available
4461 for the specified socket type
4462 @throws randolf::rex::xEAI_SOCKTYPE The specified socket type is not
4464 @throws randolf::rex::xEAI_SYSTEM Other system error (use errno to determine
4465 what the error is, then run use @ref randolf::rex::rex::mk_exception
4466 to throw the correct exception)
4468 @returns sockaddr_storage structure
4470 *///=========================================================================
4471// static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
4472 static struct sockaddr_storage* mk_sockaddr_storage(
4473 /// IP address or UNIX domain socket address to convert
4474 const char* node_name,
4475 /// Port number (or service name used by some other families)
4476 const char* service_name = nullptr,
4477 /// Optional pointer to a helpful addrinfo structure
4478 const addrinfo* hints = nullptr) {
4480 // --------------------------------------------------------------------------
4481 // Initial sanity checks.
4482 // --------------------------------------------------------------------------
4483 if (node_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME); // if (node_name == nullptr && service_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME);
4485 // --------------------------------------------------------------------------
4486 // Handle any special cases.
4487 // --------------------------------------------------------------------------
4488 switch (hints == nullptr ? AF_UNSPEC : hints->ai_family) { // Default to AF_UNSPEC in the absence of hints
4490 if (node_name[0] != '/') break;
4491 // Next entry MUST be "case AF_UNIX" for this fall-through to work
4494 // For some unknown reason, clang++ can't compile this line that
4495 // g++ has absolutely no trouble with at all:
4496 // std::shared_ptr sa = std::make_shared<sockaddr_storage>(AF_UNIX);
4497 // So, after wasting more than a year trying to figure out what the
4498 // hundreds of lines of cryptic errors meant, I eventually gave up
4499 // on clang++ for being such a dumbfuck, and used this work-around:
4500 struct sockaddr_storage* sa = (struct sockaddr_storage*)::malloc(sizeof(sockaddr_storage));
4501 sa->ss_family = AF_UNIX;
4502// std::shared_ptr sa = std::make_shared<sockaddr_storage>();
4503// sa->ss_family = AF_UNIX;
4504 std::strcpy(((struct sockaddr_un *)sa)->sun_path, node_name); // Copy path
4508 } // -x- switch hints->family -x-
4510 // --------------------------------------------------------------------------
4511 // Acquire addrinfo[] linked-list array.
4512 // --------------------------------------------------------------------------
4513 addrinfo* __addr_result = nullptr; // This is temporary
4514//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;
4515 randolf::rex::mk_exception("", getaddrinfo(node_name,
4520 // --------------------------------------------------------------------------
4521 // Find first valid addrinfo[] array by trying to open a socket with it.
4522 // --------------------------------------------------------------------------
4523 for (addrinfo* ar = __addr_result; ar != nullptr; ar = ar->ai_next) {
4524//std::cout << " ... ai_family=" << ar->ai_family << " ai_socktype=" << ar->ai_socktype << " ai_flags=" << ar->ai_flags << std::endl; // *******************
4525// ***************************** TODO: Make sure this loop is working properly
4527//std::cout << " !!! ai_family=" << __addr_result->ai_family << std::endl; // *******************
4529 // --------------------------------------------------------------------------
4530 // Copy the address to "sa" and return it. The "addr" data for IPv4 and IPv6
4531 // addresses include the TCP/UDP port number (service) in first two bytes as
4532 // as an unsigned integer (u_int16), followed immediately by the IP address.
4534 // We're taking the first record only. We assume that hints{} is defined on
4535 // an as-needed basis.
4537 // Note: AF_LINK and AF_PACKET are not widely supported, but we've made an
4538 // effort to accomodate their usage.
4539 // --------------------------------------------------------------------------
4540 struct sockaddr_storage* sa = (struct sockaddr_storage*)::malloc(sizeof(sockaddr_storage));
4541 //std::shared_ptr sa = std::make_shared<sockaddr_storage>();
4542 switch (__addr_result->ai_family) {
4543 case AF_INET: // IPv4 address
4544 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_in));
4546 case AF_INET6: // IPv6 address
4547 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_in6));
4549 /* // We're handling this earlier as a special case: TODO: Move special handling to here
4550 case AF_UNIX: // UNIX (path) domain socket
4551 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_un));
4554 case /*AF_LINK*/18: // Link layer interface (arp)
4555 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_dl));
4557 case AF_PACKET: // Packet (ethernet) address (packet capturing)
4558 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_ll));
4560 default: // Everything else
4561 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_storage));
4562 } // -x- switch family -x-
4563 freeaddrinfo(__addr_result); // We don't need this anymore
4565 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
4567 /*======================================================================*//**
4568 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
4569 *///=========================================================================
4570 static struct sockaddr_storage* mk_sockaddr_storage(
4571 /// IP address or UNIX domain socket address to convert
4572 const char* node_name,
4574 const u_int16_t service_name,
4575 /// Optional pointer to a helpful addrinfo structure
4576 const addrinfo* hints = nullptr) {
4577 std::string port = std::to_string(service_name);
4578 return mk_sockaddr_storage(node_name, port.data(), hints);
4579 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
4581 /*======================================================================*//**
4582 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
4583 *///=========================================================================
4584 static struct sockaddr_storage* mk_sockaddr_storage(
4585 /// IP address or UNIX domain socket address to convert
4586 const std::string node_name,
4588 const u_int16_t service_name,
4589 /// Optional pointer to a helpful addrinfo structure
4590 const addrinfo* hints = nullptr) {
4591 std::string port = std::to_string(service_name);
4592 return mk_sockaddr_storage(node_name.data(), port.data(), hints);
4593 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
4595 /*======================================================================*//**
4596 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
4597 *///=========================================================================
4598 static struct sockaddr_storage* mk_sockaddr_storage(
4599 /// IP address or UNIX domain socket address to convert
4600 const std::string node_name,
4601 /// Port number (or server name used by some other families)
4602 const std::string service_name,
4603 /// Optional pointer to a helpful addrinfo structure
4604 const addrinfo* hints = nullptr) {
4605 return mk_sockaddr_storage(node_name.data(), service_name.data(), hints);
4606 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
4608 /*======================================================================*//**
4610 Specify a name for this rsocket.
4612 This is an arbitrary name that is entirely optional, and should be regarded
4613 as similar to the naming of threads.
4614 @returns The same rsocket object so as to facilitate stacking
4615 *///=========================================================================
4617 /// Name to assign to this @c rsocket
4618 const std::string name) noexcept {
4619 // const std::lock_guard<std::mutex> lock(__name);
4622 } // -x- rsocket* name -x-
4624 /*======================================================================*//**
4626 Find out what this rsocket's name is.
4628 The built-in SNI mechanism will overwrite this data to indicate the hostname
4629 that was specified by the TLS-encrypted endpoint that triggered an internal
4630 SNI callback -- use the @ref name_sni() method to find out which hostname is
4631 actually being used by TLS.
4632 @returns The name of this rsocket (or an empty @c std::string if this rsocket
4633 doesn't have a name)
4636 *///=========================================================================
4637 std::string name() noexcept {
4638 // const std::lock_guard<std::mutex> lock(__name);
4640 } // -x- std::string name -x-
4642 /*======================================================================*//**
4644 Find out what this rsocket's actual TLS SNI hostname is.
4646 This is the exact - or closest-matching (in the case of wildcards) - hostname
4647 associated with an actual TLS certificate that was provided in the configured
4648 @ref rsocket_sni object.
4649 @returns The hostname associated with the TLS certificate selected by SNI
4652 @see tls_sni_has_name
4654 *///=========================================================================
4655 std::string name_sni() noexcept {
4657 } // -x- std::string name_sni -x-
4659 /*======================================================================*//**
4661 Get socket I/O statistics from internally-tracked socket I/O counters.
4663 The number of bytes transmitted and received is tracked internally, so that
4664 the information can be used later in logging. These statistics are available
4665 at all times, but for logging purposes it makes the most sense to copy this
4666 information after the rsocket is closed, at which time the final statistics
4667 will continue to be available until the rsocket's destructor takes over.
4670 This method is threadsafe.
4671 @returns rsocket_io wrapped in a std::shared_ptr object to help ease memory
4677 *///=========================================================================
4678 std::shared_ptr<rsocket_io> net_io() noexcept {
4679 std::shared_ptr stats = std::make_shared<rsocket_io>();
4680 stats->bytes_rx = __bytes_rx;
4681 stats->bytes_tx = __bytes_tx;
4682 stats->crypt_rx = __crypt_rx;
4683 stats->crypt_tx = __crypt_tx;
4684 stats->is_final = false;
4686 } // -x- std::shared_ptr<rsocket_io> net_io -x-
4688 /*======================================================================*//**
4690 Get socket I/O statistics from internally-tracked socket I/O counters as a
4691 pre-formatted std::string object.
4693 The number of bytes transmitted and received is tracked internally, so that
4694 the information can be used later in logging. These statistics are available
4695 at all times, but for logging purposes it makes the most sense to copy this
4696 information after the rsocket is closed, at which time the final statistics
4697 will continue to be available until the rsocket's destructor takes over.
4700 The format string may contain any characters, with only instances of the
4701 following case-sensitive command sequences to be interpolated accordingly
4702 (invalid commands will be ignored and will remain in place, unmodified):
4704 <table cellpadding=8 cellspacing=0 border=1>
4705 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
4706 <tr><td>$$</td><td>One dollar sign ("$")</td><td>@e N/A</td></tr>
4707 <tr><td>$aR</td><td>Total (all) bytes received</td><td>bytes_rx @c + crypt_rx</td></tr>
4708 <tr><td>$aT</td><td>Total (all) bytes transmitted</td><td>bytes_tx @c + crypt_tx</td></tr>
4709 <tr><td>$bR</td><td>Unencrypted bytes received</td><td>bytes_rx</td></tr>
4710 <tr><td>$bT</td><td>Unencrypted bytes transmitted</td><td>bytes_tx</td></tr>
4711 <tr><td>$cR</td><td>Encrypted bytes recevied</td><td>crypt_rx</td></tr>
4712 <tr><td>$cT</td><td>Encrypted bytes transmitted</td><td>crypt_tx</td></tr>
4713 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
4716 Why do we use dollar signs instead of percent symbols, like other formatting
4717 functions like printf() does? So that the format string won't be in conflict
4718 with any percent-prefixed commands in printf() and similar funcions. This
4719 means that a printf() format string can be put through a first pass here to
4720 get the needed statistics interpolated into the needed positions.
4723 This method is threadsafe.
4724 @returns An interpolated format string as an std::string object.
4729 *///=========================================================================
4733 /// Length of format string (in bytes), or 0 to auto-detect length if format string is an ASCIIZ string
4735 /// Pointer to an @ref rsocket_io structure to read the statistics from (nullptr = use internal copy of current statistics)
4736 // 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)
4737 rsocket_io* addr = nullptr) noexcept {
4739 // --------------------------------------------------------------------------
4740 // Measure size of format string if an ASCIIZ string was indicated.
4741 // --------------------------------------------------------------------------
4742 if (len == 0) len = std::strlen(format);
4744 // --------------------------------------------------------------------------
4745 // Internal variables.
4746 // --------------------------------------------------------------------------
4747 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
4748 std::string stats; // Formatted result
4749 ulong val; // Value (to be inserted)
4750 char c; // Current character
4751 std::string cmd; // Accumulated command (may be subsituted)
4752 bool flag_commas = false; // Thousands separators flag
4753 bool flag_right = false; // Flush-right flag
4755 // --------------------------------------------------------------------------
4756 // Process format string and build resulting formatted stats string.
4758 // This is designed to be fast, and I'm taking huge shortcuts here since the
4759 // commands are all the same size (except for the first one, which is only
4760 // two dollar sign characters). Processing in this loop should be quite fast
4761 // compared to using library functions to search for dollar sign characters
4762 // since data needs to be copied anyway -- why run a loop over the text twice
4763 // when can get away with doing it faster by doing it only once? This is an
4764 // optimized approach, although it probably could be even faster by not using
4765 // std::string for temporary command storage (there are other ways to do this
4766 // but I think this should suffice for now since it isn't expected to be used
4768 // --------------------------------------------------------------------------
4769 for (int i = 0; i < len; i++) {
4771 // --------------------------------------------------------------------------
4772 // First character (potentially).
4773 // --------------------------------------------------------------------------
4774 if ((c = format[i]) != '$') { // Not a dollar sign -- save it and move on
4778 cmd = c; // Start building up the command string
4780 // --------------------------------------------------------------------------
4781 // Second character: Part 1 of 2
4783 // TODO: Add support for "," commas, and "r" right-alignment.
4784 // --------------------------------------------------------------------------
4785 if (++i == len) continue; // End of format string, so we're done
4786 cmd.push_back(c = format[i]);
4787 if (c == '$') { // Consume the previous dollar sign so that $$ turns into $
4788 stats.push_back(c); // Add $ to stats string (we're only keeping one dollar sign)
4791 flag_commas = flag_right = false; // Reset these flags for a clean start
4793 // --------------------------------------------------------------------------
4795 // , = Include thousands separators (commas)
4796 // r = Flush right (leading spaces will be added)
4797 // --------------------------------------------------------------------------
4800 if (++i == len) continue; // End of format string, so we're done
4801 cmd.push_back(c = format[i]);
4803 goto net_io_flags_loop;
4804 } else if (c == 'r') {
4805 if (++i == len) continue; // End of format string, so we're done
4806 cmd.push_back(c = format[i]);
4808 goto net_io_flags_loop;
4809 } // -x- if [,r] -x-
4812 // --------------------------------------------------------------------------
4813 // Second character: Part 1 of 2
4814 // --------------------------------------------------------------------------
4815 if (c < 'a' || c > 'c') { // Not a, b, or c, so treat it as a regular character
4816 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4818 } // -x- if !~[abc] -x-
4820 // --------------------------------------------------------------------------
4822 // --------------------------------------------------------------------------
4823 if (++i == len) continue; // End of format string, so we're done
4824 cmd.push_back(c = format[i]);
4825 if (c != 'R' && c != 'T') { // Not R or T, so treat it as a regular character
4826 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4828 } // -x- if !~[RT] -x-
4830 // --------------------------------------------------------------------------
4831 // Command processing. If the command is valid, then it will be interpolated
4832 // with its expected result by replacing it with the resulting value.
4833 // --------------------------------------------------------------------------
4834 //std::cout << "[" << cmd << "]"; // Debug
4835 if (cmd.ends_with("aR")) val = data->bytes_rx + data->crypt_rx;
4836 else if (cmd.ends_with("aT")) val = data->bytes_tx + data->crypt_tx;
4837 else if (cmd.ends_with("bR")) val = data->bytes_rx ;
4838 else if (cmd.ends_with("bT")) val = data->bytes_tx ;
4839 else if (cmd.ends_with("cR")) val = data->crypt_rx;
4840 else if (cmd.ends_with("cT")) val = data->crypt_tx;
4841 else { stats.append(cmd); continue; } // This is wrong, so ignore it
4843 // --------------------------------------------------------------------------
4844 // Re-use cmd to generate formatted value.
4845 // --------------------------------------------------------------------------
4847 cmd.resize(21); // Maximum length without commas (plus character zero): 18446744073709551615
4848 cmd.resize(sprintf(cmd.data(), flag_right ? "%20lu" : "%lu", val)); // The ::sprintf function can't use cmd.data() here
4849 if (flag_commas) cmd = randolf::rtools::insert_commas(cmd.data()); // Insert commas (this makes the string longer)
4851 // --------------------------------------------------------------------------
4852 // Convert to std::string and add to the stats string.
4853 // --------------------------------------------------------------------------
4859 } // -x- std::string net_io -x-
4861 /*======================================================================*//**
4863 Where the destructor should save final I/O statistics before this rsocket's
4864 resources are completely freed/deallocated.
4866 Developers should take care to check that the @ref rsocket_io::is_final flag
4867 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
4868 since there's no guarantee that the destructor will necessarily be executed
4869 in a timely manner (this flag is set last, after all other statistics are
4870 copied into the structure while in a mutex-locked state).
4871 @returns The same rsocket object so as to facilitate stacking
4877 *///=========================================================================
4878 rsocket* net_io_final(
4879 /// Pointer to @ref rsocket_io structure (nullptr = disabled)
4880 rsocket_io* addr) noexcept {
4881 __io_final_addr = addr;
4883 } // -x- rsocket* net_io_final -x-
4885 /*======================================================================*//**
4887 Where the destructor should save current I/O statistics.
4889 Statistics are copied into the structure while in a mutex-locked state, but
4890 the copy itself is not an overall atomic snapshot of the I/O statistics even
4891 though each statistic is copied atomically. This means that the actual
4892 statistics could be slightly different if updates occur independently (e.g.,
4893 due to activities in a different thread), but this likely won't matter since
4894 the anticipated use for these statistics is to display or otherwise present
4895 general statistical information to a user at regular intervals.
4896 @returns The same rsocket object so as to facilitate stacking
4902 *///=========================================================================
4903 rsocket* net_io_update(
4904 /// Pointer to @ref rsocket_io structure (nullptr = do nothing)
4905 rsocket_io* addr) noexcept {
4907 // --------------------------------------------------------------------------
4908 // Copy statistics to final location. (We do this last in case there's an
4909 // issue with locking; there shouldn't be, but if there is then at least we
4910 // already we're not inadvertently hanging on to resources at this point that
4911 // need to be freed/closed anyway.)
4912 // --------------------------------------------------------------------------
4913 if (addr != nullptr) {
4915 addr->bytes_rx = __bytes_rx;
4916 addr->bytes_tx = __bytes_tx;
4917 addr->crypt_rx = __crypt_rx;
4918 addr->crypt_tx = __crypt_tx;
4919 addr->is_final = false;
4921 } // -x- if addr -x-
4924 } // -x- rsocket* net_io_update -x-
4926 /*======================================================================*//**
4928 Return the number of bytes pending to be received, without actually receiving
4932 When using TLS, OpenSSL may not report all the pending data, but this is
4933 resolved here by also adding the total amount of pending raw data that is not
4934 currently being processed in the current record by OpenSSL, even when "read
4937 @throws randolf::rex::xEBADF The underlying socket is not open
4938 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4939 part of the user address space
4940 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
4941 @throws randolf::rex::xENOTTY The underlying socket is not associated with a
4942 character special device, or the specified operation does not apply
4943 to the kind of object that the file descriptor fd references (this
4944 exception will probably never occur unless the underlying socket
4945 handle was arbitrarily replaced with the type of handle that can
4946 cause this error to occur)
4948 @returns Total number of bytes pending (0 = none)
4952 *///=========================================================================
4954 if (__debug) debug("pending(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4957 // --------------------------------------------------------------------------
4958 // Get pending bytes from raw socket.
4959 // --------------------------------------------------------------------------
4960 ulong pending_bytes = 0; // BSD, glibc, etc., use ulong; older UNIX systems use int // TODO: Figure out how to detect what ioctl needs
4961 __rc_check(ioctl(__socket_fd, FIONREAD, &pending_bytes));
4963 // --------------------------------------------------------------------------
4964 // Add pending bytes from OpenSSL if TLS is active, because this total isn't
4965 // included in the raw socket count since OpenSSL received these bytes.
4966 // --------------------------------------------------------------------------
4967 return __tls ? pending_bytes + SSL_pending(__tls_fd) : pending_bytes;
4969 } // -x- ulong pending -x-
4971 /*======================================================================*//**
4973 Poll the underlying socket using the poll() method for data that's ready for
4974 receiving (default), etc.
4977 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4978 select(), and accept()/accept4() when using multiple sockets.
4980 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4981 part of the user address space
4982 @throws randolf::rex::xEINTR Interrupted by a signal
4983 @throws randolf::rex::xENOMEM Insufficient memory
4984 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4985 doesn't refer to a socket
4986 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4987 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4988 is a highly improbable chance that a timeout could still occur if the
4989 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4990 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4993 @returns The resulting (short)pollfd.revents bitfield
4994 @returns 0 if @c timeout_behaviour is set to TIMEOUT_ZERO
4999 *///=========================================================================
5000 // 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
5002 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
5003 const short events = POLLIN,
5004 /// Number of milliseconds to wait
5005 const int timeout = 0,
5006 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
5007 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
5008 if (__debug) debug("poll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5009 + ", pollfd.events=" + std::to_string(events)
5010 + ", timeout=" + std::to_string(timeout)
5012 struct pollfd fds{__socket_fd, events, 0};
5013 if (__rc_check(::poll(&fds, 1, timeout)) == 0) { // Timeout occurred when ::poll returns 0
5014 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
5018 } // -x- short poll -x-
5020 /*======================================================================*//**
5022 Get port number associated with underlying socket descriptor/handle.
5025 - an emphemeral port number assignment (dynamic) is intended
5026 - the underlying socket details are not defined (e.g., in the case of an
5027 empty rsocket instantiation)
5028 - port numbers are not supported/utilized by the current family (e.g., not
5029 AF_INET {IPv4} or AF_INET6 {IPv6})
5031 The port number can be set in most constructors, or via one of the socket()
5033 @returns Port number (typically TCP and UDP, although some other families
5034 such as SCTP and DCCP also utilize port numbers)
5035 @see socket_family()
5036 @see socket_protocol()
5039 *///=========================================================================
5040 const uint16_t port() noexcept {
5041 switch (__socket_addr->ss_family) {
5042 case AF_INET: // IPv4
5043// return ntohs(((sockaddr_in*)&__socket_addr)->sin_port);
5044 return ntohs(((sockaddr_in*)__socket_addr)->sin_port);
5045 case AF_INET6: // IPv6
5046// return ntohs(((sockaddr_in6*)&__socket_addr)->sin6_port);
5047 return ntohs(((sockaddr_in6*)__socket_addr)->sin6_port);
5048 default: // Everything else
5050 } // -x- switch __socket_addr->ss_family -x-
5051 } // -x- uint16_t port -x-
5053 /*======================================================================*//**
5055 Poll the underlying socket using the ppoll() method for data that's ready for
5056 receiving (default), etc.
5059 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
5060 select(), and accept()/accept4() when using multiple sockets.
5062 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5063 part of the user address space
5064 @throws randolf::rex::xEINTR Interrupted by a signal
5065 @throws randolf::rex::xENOMEM Insufficient memory
5066 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5067 doesn't refer to a socket
5068 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
5069 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
5070 is a highly improbable chance that a timeout could still occur if the
5071 data is read by another thread before the `recv(..., MSG_PEEK)` call)
5072 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5075 @returns The resulting (short)pollfd.revents bitfield
5080 *///=========================================================================
5081 // 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
5083 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
5084 const short events = POLLIN,
5086 const struct timespec* tmo_p = nullptr,
5088 const sigset_t* sigmask = nullptr,
5089 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
5090 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
5091 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5092 + ", pollfd.events=" + std::to_string(events)
5093 + ", timespec{tv_sec=" + std::to_string(tmo_p->tv_sec)
5094 + ", tv_nsec=" + std::to_string(tmo_p->tv_nsec) + "}"
5097 struct pollfd fds{__socket_fd, events, 0};
5098 if (__rc_check(::ppoll(&fds, 1, tmo_p, sigmask)) == 0) { // Timeout occurred when ::ppoll returns 0
5099 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
5103 } // -x- short ppoll -x-
5105 /*======================================================================*//**
5107 Poll the underlying socket using the ppoll() method for data that's ready for
5108 receiving (default), etc.
5111 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
5112 select(), and accept()/accept4() when using multiple sockets.
5114 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5115 part of the user address space
5116 @throws randolf::rex::xEINTR Interrupted by a signal
5117 @throws randolf::rex::xENOMEM Insufficient memory
5118 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5119 doesn't refer to a socket
5120 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
5121 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
5122 is a highly improbable chance that a timeout could still occur if the
5123 data is read by another thread before the `recv(..., MSG_PEEK)` call)
5124 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5127 @returns The resulting (short)pollfd.revents bitfield
5131 *///=========================================================================
5132 // 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
5134 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
5135 const short events = POLLIN,
5136 /// Timeout in seconds
5137 const long tv_sec = 0,
5138 /// Timeout in nanoseconds
5139 const long tv_nsec = 0,
5141 const sigset_t* sigmask = nullptr,
5142 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
5143 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
5144 struct timespec tmo_p{tv_sec, tv_nsec};
5145 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5146 + ", pollfd.events=" + std::to_string(events)
5147 + ", timespec{tv_sec=" + std::to_string(tmo_p.tv_sec)
5148 + ", tv_nsec=" + std::to_string(tmo_p.tv_nsec) + "}"
5151 struct pollfd fds{__socket_fd, events, 0};
5152 if (__rc_check(::ppoll(&fds, 1, &tmo_p, sigmask)) == 0) { // Timeout occurred when ::ppoll returns 0
5153 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
5157 } // -x- short ppoll -x-
5159 /*======================================================================*//**
5161 Send a formatted string to the @ref rsocket endpoint.
5163 The @c format is described in the documentation for the POSIX or Standard C
5164 Library @c printf() function.
5165 @throws randolf::rex::xEBADF The underlying socket is not open
5166 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
5167 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
5168 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
5169 @throws randolf::rex::xENOMEM Insufficient memory
5170 @returns The same rsocket object so as to facilitate stacking
5172 @see is_eol_fix_printf
5179 *///=========================================================================
5181 /// Format string to use
5183 /// Variadic arguments
5185 char* buf = nullptr;
5187 ::va_start(args, format);
5188 int rc = ::vasprintf(&buf, format, args);
5189 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
5190 if (rc < 0 && buf != nullptr) ::free(buf);
5191 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
5192 if (__eol_fix_printf && !__eol.empty()) {
5193 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
5195 __send(str.data(), str.length());
5200 } catch (std::exception& e) { // Free buf then re-throw the exception
5201 ::free(buf); // Prevent memory leak when an exception is thrown
5204 } // -x- if __eol_fix_printf -x-
5206 } // -x- rsocket* printf -x-
5208 /*======================================================================*//**
5210 Send a formatted string to the @ref rsocket endpoint, and append an EoL
5213 The @c format is described in the documentation for the POSIX or Standard C
5214 Library @c printf() function.
5215 @throws randolf::rex::xEBADF The underlying socket is not open
5216 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
5217 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
5218 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
5219 @throws randolf::rex::xENOMEM Insufficient memory
5220 @returns The same rsocket object so as to facilitate stacking
5223 @see is_eol_fix_printf
5230 *///=========================================================================
5231 rsocket* printfline(
5232 /// Format string to use
5234 /// Variadic arguments
5236 char* buf = nullptr;
5238 ::va_start(args, format);
5239 int rc = ::vasprintf(&buf, format, args);
5240 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
5241 if (rc < 0 && buf != nullptr) ::free(buf);
5242 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
5243 if (__eol_fix_printf && !__eol.empty()) {
5244 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
5247 __send(str.data(), str.length());
5250 __sendline(buf, rc);
5252 } catch (std::exception& e) { // Free buf then re-throw the exception
5253 ::free(buf); // Prevent memory leak when an exception is thrown
5256 } // -x- if __eol_fix_printf -x-
5258 } // -x- rsocket* printfline -x-
5260 /*======================================================================*//**
5262 Receive data from the endpoint into a @c std::vector<char> that is allocated
5265 If nbytes is 0, then the internal @ref buffer_size() will be used as the
5266 default, which can also be changed from its compiled-in default of 1024 by
5267 using one of the buffer_size() methods.
5270 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5271 reception, but it's important to note that it does implement temporary
5272 blocking while waiting for data.
5274 @throws randolf::rex::xEBADF The underlying socket is not open
5275 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5277 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5278 part of the user address space
5279 @throws randolf::rex::xEINTR Interrupted by a signal
5280 @throws randolf::rex::xEINVAL Invalid argument passed
5281 @throws randolf::rex::xENOMEM Insufficient memory
5282 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5283 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5284 doesn't refer to a socket
5285 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5288 @returns appropriately-sized vector of characters
5289 @see recv(std::vector<char>, const int)
5290 @see recvz(const size_t, const int)
5292 @see send(const std::vector<char>, const int)
5294 *///=========================================================================
5295 std::vector<char> recv(
5296 /// Maximum number of bytes to receive
5297 const size_t nbytes = 0,
5302 /// MSG_CMSG_CLOEXEC
5303 const int posix_flags = 0) {
5304 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5305 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5307 + ", " + std::to_string(buf_size)
5308 + ", " + std::to_string(posix_flags)
5310 std::vector<char> buf(buf_size);
5311 buf.resize(__recv(buf.data(), buf.size(), posix_flags));
5313 } // -x- std::vector<char> recv -x-
5315 /*======================================================================*//**
5317 Receive data from the endpoint into the @c std::vector object supplied in the
5320 The maximum number of bytes that can be received won't exceed the number of
5321 bytes that the supplied @c std::vector<char> was initialized or resized to.
5324 For @c std::vector it's important that the @c resize() method is used to
5325 pre-allocate the underlying char[] array, instead of the unfortunately-named
5326 @c reserve() method that doesn't pre-allocate, to avoid causing segmentation
5327 faults or other undefined behaviours.
5330 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5331 reception, but it's important to note that it does implement temporary
5332 blocking while waiting for data.
5334 @throws randolf::rex::xEBADF The underlying socket is not open
5335 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5337 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5338 part of the user address space
5339 @throws randolf::rex::xEINTR Interrupted by a signal
5340 @throws randolf::rex::xEINVAL Invalid argument passed
5341 @throws randolf::rex::xENOMEM Insufficient memory
5342 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5343 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5344 doesn't refer to a socket
5345 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5348 @returns The same array that was specified in the @c buf parameter
5349 @see recv(const size_t, const int)
5350 @see recvz(const size_t, const int)
5351 @see send(const std::vector<char>, const int)
5354 *///=========================================================================
5355 std::vector<char> recv(
5356 /// Target std::vector<char> to receive data into
5357 std::vector<char> buf,
5362 /// MSG_CMSG_CLOEXEC
5363 const int posix_flags = 0) {
5364 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5366 + ", " + std::to_string(buf.size())
5367 + ", " + std::to_string(posix_flags)
5369 buf.resize(__recv(buf.data(), buf.size(), posix_flags));
5371 } // -x- std::vector<char> recv -x-
5373 /*======================================================================*//**
5375 Receive data from the endpoint into a @c std::string object that is allocated
5378 If nbytes is 0, then the internal @ref buffer_size() will be used as the
5379 default, which can also be changed from its compiled-in default of 1024 by
5380 using one of the buffer_size() methods.
5383 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5384 reception, but it's important to note that it does implement temporary
5385 blocking while waiting for data.
5387 @throws randolf::rex::xEBADF The underlying socket is not open
5388 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5390 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5391 part of the user address space
5392 @throws randolf::rex::xEINTR Interrupted by a signal
5393 @throws randolf::rex::xEINVAL Invalid argument passed
5394 @throws randolf::rex::xENOMEM Insufficient memory
5395 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5396 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5397 doesn't refer to a socket
5398 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5401 @returns appropriately-sized vector of characters
5403 @see recv(std::vector<char>, const int)
5404 @see recvz(const size_t, const int)
5405 @see send(const std::string, const int)
5407 *///=========================================================================
5408 std::string recv_as_string(
5409 /// Maximum number of bytes to receive
5410 const size_t nbytes = 0,
5415 /// MSG_CMSG_CLOEXEC
5416 const int posix_flags = 0) {
5417 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5418 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5420 + ", " + std::to_string(buf_size)
5421 + ", " + std::to_string(posix_flags)
5424 buf.resize(buf_size); // Pre-fill anticipated string size
5425 buf.resize(__recv(buf.data(), buf.size(), posix_flags)); // Shorten string
5427 } // -x- std::string recv -x-
5429 /*======================================================================*//**
5431 Receive an ASCIIZ string from the endpoint, including the NULL terminator.
5433 @throws randolf::rex::xEBADF The underlying socket is not open
5434 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5436 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5437 part of the user address space
5438 @throws randolf::rex::xEINTR Interrupted by a signal
5439 @throws randolf::rex::xEINVAL Invalid argument passed
5440 @throws randolf::rex::xENOMEM Insufficient memory
5441 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5442 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5443 doesn't refer to a socket
5444 @throws randolf::rex::xERANGE if no NULL terminator is detected (this may
5445 occur before the underlying ASCIIZ string char* array is allocated)
5446 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5449 @returns Pointer to ASCIIZ string (will need to be deleted by caller)
5450 @see send_asciiz(const char*, const int)
5452 *///=========================================================================
5454 /// Maximum number of bytes to receive
5455 const size_t nbytes = 0,
5460 /// MSG_CMSG_CLOEXEC
5461 const int posix_flags = 0) {
5462 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5463 if (__debug) debug("recv_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5464 + ", " + std::to_string(buf_size)
5465 + ", " + std::to_string(posix_flags)
5468 // --------------------------------------------------------------------------
5469 // Calculate size of buffer (includes NULL terminator since we'll also be
5470 // receiving this from the endpoint).
5471 // --------------------------------------------------------------------------
5474 // --------------------------------------------------------------------------
5475 // Reduce buffer size to what is actually read (remember: we don't actually
5476 // know where the EoL sequence is yet, or if there even is one).
5477 // --------------------------------------------------------------------------
5478 int max = __recv(&buf, buf_size, posix_flags | MSG_PEEK); // Look-ahead at socket stream (buffer was inflated to include EoL sequence)
5480 for (int i = 0; i < max; i++) {
5484 } // -x- if v[i] -x-
5486 if (len < 0) throw randolf::rex::xERANGE("ERANGE: NULL terminator not found");
5488 // --------------------------------------------------------------------------
5489 // I'd love to use std::shared_ptr<char*> for this next part, but it leads
5490 // to intermittent segmentation faults and/or "malloc(): corrupted top size
5491 // occurs" errors outside of this method. So, my conclusion is that char*
5492 // (and char[], as I've tried with this too) are not properly supported by:
5493 // std::shared_ptr<char*> v = std::make_shared<char*>(new char[len + 1]);
5495 // Using std::string() is really not what we want because there is confusion
5496 // concerning the NULL terminator -- with char* strings, it's crystal clear
5497 // that there must be a NULL terminator (since a length isn't tracked). Now,
5498 // if you want an std::string, just initialize as follows:
5500 // char* temp_string = r.recv_asciiz();
5501 // std::string mystr = std::string(temp_string);
5502 // delete temp_string;
5503 // --------------------------------------------------------------------------
5504 char* v = new char[len + 1];
5505 int rc = __recv(v, len + 1, posix_flags); // Consume with NULL terminator from socket stream
5506// TODO: Free "v" in a catch-and-rethrow block (check other calls to __recv and __send too)
5509 } // -x- char* recv_asciiz -x-
5511 /*======================================================================*//**
5513 Receive one byte (unsigned 8-bit byte) of data from the endpoint.
5515 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5516 reception, but it's important to note that it does implement temporary
5517 blocking while waiting for data.
5519 @throws randolf::rex::xEBADF The underlying socket is not open
5520 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5522 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5523 part of the user address space
5524 @throws randolf::rex::xEINTR Interrupted by a signal
5525 @throws randolf::rex::xEINVAL Invalid argument passed
5526 @throws randolf::rex::xENOMEM Insufficient memory
5527 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5528 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5529 doesn't refer to a socket
5530 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5533 @returns one unsigned character
5534 @see recv(std::vector<char>, const int)
5535 @see recvz(const size_t, const int)
5540 *///=========================================================================
5546 /// MSG_CMSG_CLOEXEC
5547 const int posix_flags = 0) {
5549 if (__debug) debug("recv_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5551 + ", " + std::to_string(sizeof(buf))
5552 + ", " + std::to_string(posix_flags)
5554 int rc = __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5556 } // -x- byte recv_byte -x-
5558 /*======================================================================*//**
5560 Receive one character (signed 8-bit byte) of data from the endpoint.
5562 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5563 reception, but it's important to note that it does implement temporary
5564 blocking while waiting for data.
5566 @throws randolf::rex::xEBADF The underlying socket is not open
5567 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5569 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5570 part of the user address space
5571 @throws randolf::rex::xEINTR Interrupted by a signal
5572 @throws randolf::rex::xEINVAL Invalid argument passed
5573 @throws randolf::rex::xENOMEM Insufficient memory
5574 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5575 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5576 doesn't refer to a socket
5577 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5580 @returns one signed character
5581 @see recv(std::vector<char>, const int)
5582 @see recvz(const size_t, const int)
5587 *///=========================================================================
5593 /// MSG_CMSG_CLOEXEC
5594 const int posix_flags = 0) {
5596 if (__debug) debug("recv_char(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5598 + ", " + std::to_string(sizeof(buf))
5599 + ", " + std::to_string(posix_flags)
5601 int rc = __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5602 // How to detect disconnected stream when telnet user presses CTRL-C?
5603 //std::cout << "rc=" << std::to_string(rc) << std::endl; // Debug (to be removed)
5604// TODO: Investigate checking platform's char size and moving signing bit accordingly
5606 } // -x- char recv_char -x-
5608 /*======================================================================*//**
5610 Receive a line of data from the endpoint, into a new @ref randolf::rline
5611 object, with the EoL character(s) isolated. This is meant for multiline
5612 ASCII and UTF-8 text, and while it will also work with binary data that
5613 doesn't include any EoL sequence characters (ASCII `10` and `13`),
5614 @ref recv(), @ref recvz(), and other methods are much better-suited to
5615 receive binary data.
5617 This is essentially a wrapper around what recvline() does, but returns both
5618 the line of text and the EoL sequence together in a new @ref randolf::rline
5621 For additional details on the other parameters, please see the @ref recvline
5622 method's documentation for the remaining details.
5624 If you're using a customzied EoL sequence, then it's important to note that
5625 it probably won't be recognized by the @ref randolf::rline class, but do also
5626 check that documentation to confirm this.
5628 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
5629 @throws randolf::rex::xEBADF The underlying socket is not open
5630 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5632 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5633 part of the user address space
5634 @throws randolf::rex::xEINTR Interrupted by a signal
5635 @throws randolf::rex::xEINVAL Invalid argument passed
5636 @throws randolf::rex::xENOMEM Insufficient memory
5637 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5638 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5639 doesn't refer to a socket
5640 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
5641 no EoL character sequence is detected after recvline's buffer became
5642 full (whatever data is waiting may still be received using any of the
5643 recv_ methods {with or without the @c MSG_PEEK flag set}, including
5644 @c recvline() with a larger buffer {see the @c nbytes parameter})
5645 @throws randolf::rex::xERANGE if the timeout parameter is below 0
5646 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5649 @returns One line of text as a randolf::rline object.
5652 @see eol_consumed_seq()
5655 @see send_rline(const randolf::rline, const int)
5656 @see sendline(const std::string, const int)
5658 @see timeout_recvline
5659 @see timeout_recvline(long)
5661 *///=========================================================================
5662 randolf::rline recv_rline(
5663 /// Maximum number of bytes to receive (including EoL character sequence)
5664 const size_t nbytes = 0,
5669 /// MSG_CMSG_CLOEXEC
5670 const int posix_flags = 0,
5671 /// Line timeout (in seconds)@n
5672 /// 0 = no timeout (default), unless it was configured by way of the
5673 /// @ref timeout_recvline(long) method
5675 /// Configuration parameters
5676 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
5677 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
5678 // + (__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
5679 // Internally, the length of the @ref eol() sequence is added to the buffer size
5680 // so that the maximum line length without EoL characters matches the maximum
5681 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
5682 if (__debug) debug("recv_rline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5683 + ", " + std::to_string(buf_size)
5684 + ", " + std::to_string(posix_flags)
5686 std::string line = __recvline(buf_size, posix_flags, timeout, recvline_flags);
5687 return randolf::rline(line, __eol_consumed_seq);
5688 } // -x- randolf::rline recv_rline -x-
5690 /*======================================================================*//**
5692 Receive a line of data from the endpoint assigned into the specifieid
5693 @ref randolf::rline object, with the EoL character(s) isolated. This is
5694 meant for multiline ASCII and UTF-8 text, and while it will also work with
5695 binary data that doesn't include any EoL sequence characters (ASCII `10` and
5696 `13`), @ref recv(), @ref recvz(), and other methods are much better-suited to
5697 receive binary data.
5699 This is essentially a wrapper around what recvline() does, but returns both
5700 the line of text and the EoL sequence together, assigned into the existing
5701 @ref randolf::rline object.
5703 For additional details on the other parameters, please see the @ref recvline
5704 method's documentation for the remaining details.
5706 If you're using a customzied EoL sequence, then it's important to note that
5707 it probably won't be recognized by the @ref randolf::rline class, but do also
5708 check that documentation to confirm this.
5710 When providing @c nullptr for the line parameter, a new @ref randolf::rline
5711 object will be instantiated that will then need to be deleted after it's no
5714 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
5715 @throws randolf::rex::xEBADF The underlying socket is not open
5716 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5718 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5719 part of the user address space
5720 @throws randolf::rex::xEINTR Interrupted by a signal
5721 @throws randolf::rex::xEINVAL Invalid argument passed
5722 @throws randolf::rex::xENOMEM Insufficient memory
5723 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5724 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5725 doesn't refer to a socket
5726 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
5727 no EoL character sequence is detected after recvline's buffer became
5728 full (whatever data is waiting may still be received using any of the
5729 recv_ methods {with or without the @c MSG_PEEK flag set}, including
5730 @c recvline() with a larger buffer {see the @c nbytes parameter})
5731 @throws randolf::rex::xERANGE if the timeout parameter is below 0
5732 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5735 @returns One line of text as a randolf::rline object.
5738 @see eol_consumed_seq()
5741 @see send_rline(const randolf::rline, const int)
5742 @see sendline(const std::string, const int)
5744 @see timeout_recvline
5745 @see timeout_recvline(long)
5747 *///=========================================================================
5748 randolf::rline* recv_rline(
5749 /// Pointer to the pre-instantiated randolf::rline object to assign into@n
5750 /// nullptr = instantiate a new randolf::rline object internally, with this
5751 /// rsocket's current EoL sequence set to override the default
5752 randolf::rline* line,
5753 /// Maximum number of bytes to receive (including EoL character sequence)
5754 const size_t nbytes = 0,
5759 /// MSG_CMSG_CLOEXEC
5760 const int posix_flags = 0,
5761 /// Line timeout (in seconds)@n
5762 /// 0 = no timeout (default), unless it was configured by way of the
5763 /// @ref timeout_recvline(long) method
5765 /// Configuration parameters
5766 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
5767 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
5768 // + (__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
5769 // Internally, the length of the @ref eol() sequence is added to the buffer size
5770 // so that the maximum line length without EoL characters matches the maximum
5771 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
5772 if (__debug) debug("recv_rline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5773 + ", " + std::to_string(buf_size)
5774 + ", " + std::to_string(posix_flags)
5777 // --------------------------------------------------------------------------
5778 // Instantiate new object if nullptr was provided.
5779 // --------------------------------------------------------------------------
5780 if (line == nullptr) line = new randolf::rline("", __eol);
5782 // --------------------------------------------------------------------------
5783 // Append received line of data to line, and return it.
5784 // --------------------------------------------------------------------------
5785 line->assign(__recvline(buf_size, posix_flags, timeout, recvline_flags) + __eol_consumed_seq);
5788 } // -x- randolf::rline* recv_rline -x-
5790 /*======================================================================*//**
5792 Receive a data structure from the endpoint.
5794 MSB/LSB considerations are important for any integers within your structure,
5795 so be sure to sanitize them with htons(), ntohs(), and related methods prior
5796 to sending, and then after receiving. This way, your data will be protected
5797 against corruption resulting from byte order differences when communicating
5798 between hardware architectures that differ in MSB and LSB byte ordering.
5800 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5801 reception, but it's important to note that it does implement temporary
5802 blocking while waiting for data.
5804 @throws randolf::rex::xEBADF The underlying socket is not open
5805 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5807 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5808 part of the user address space
5809 @throws randolf::rex::xEINTR Interrupted by a signal
5810 @throws randolf::rex::xEINVAL Invalid argument passed
5811 @throws randolf::rex::xENOMEM Insufficient memory
5812 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5813 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5814 doesn't refer to a socket
5815 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5818 @returns Data structure
5821 *///=========================================================================
5822 template <typename T> T recv_struct(
5827 /// MSG_CMSG_CLOEXEC
5828 const int posix_flags = 0) {
5830 if (__debug) debug("recv_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5832 + ", " + std::to_string(sizeof(buf))
5833 + ", " + std::to_string(posix_flags)
5835 __recv(&buf, sizeof(buf), posix_flags);
5837 } // -x- T recv_struct -x-
5839 /*======================================================================*//**
5841 Receive one 16-bit unsigned integer of data in LSB (little endian) order from
5844 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5845 reception, which implements temporary blocking while waiting for data.
5847 @throws randolf::rex::xEBADF The underlying socket is not open
5848 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5850 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5851 part of the user address space
5852 @throws randolf::rex::xEINTR Interrupted by a signal
5853 @throws randolf::rex::xEINVAL Invalid argument passed
5854 @throws randolf::rex::xENOMEM Insufficient memory
5855 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5856 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5857 doesn't refer to a socket
5858 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5861 @returns uint16_t (converted to local endianness)
5862 @see recv_uint16_msb
5863 @see send_uint16_lsb
5865 *///=========================================================================
5866 uint16_t recv_uint16_lsb(
5871 /// MSG_CMSG_CLOEXEC
5872 const int posix_flags = 0) {
5874 if (__debug) debug("recv_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5876 + ", " + std::to_string(sizeof(buf))
5877 + ", " + std::to_string(posix_flags)
5879 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5880 return !__endian_is_msb ? buf : ntohs(buf);
5881 } // -x- uint16_t recv_uint16_lsb -x-
5883 /*======================================================================*//**
5885 Receive one 16-bit unsigned integer of data in MSB (big endian) order from
5888 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5889 reception, which implements temporary blocking while waiting for data.
5891 @throws randolf::rex::xEBADF The underlying socket is not open
5892 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5894 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5895 part of the user address space
5896 @throws randolf::rex::xEINTR Interrupted by a signal
5897 @throws randolf::rex::xEINVAL Invalid argument passed
5898 @throws randolf::rex::xENOMEM Insufficient memory
5899 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5900 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5901 doesn't refer to a socket
5902 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5905 @returns uint16_t (converted to local endianness)
5906 @see recv_uint16_lsb
5907 @see send_uint16_msb
5909 *///=========================================================================
5910 uint16_t recv_uint16_msb(
5915 /// MSG_CMSG_CLOEXEC
5916 const int posix_flags = 0) {
5918 if (__debug) debug("recv_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5920 + ", " + std::to_string(sizeof(buf))
5921 + ", " + std::to_string(posix_flags)
5923 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5924 return __endian_is_msb ? buf : htons(buf);
5925 } // -x- uint16_t recv_uint16_msb -x-
5927 /*======================================================================*//**
5929 Receive one 32-bit unsigned integer of data in LSB (little endian) order from
5932 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5933 reception, which implements temporary blocking while waiting for data.
5935 @throws randolf::rex::xEBADF The underlying socket is not open
5936 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5938 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5939 part of the user address space
5940 @throws randolf::rex::xEINTR Interrupted by a signal
5941 @throws randolf::rex::xEINVAL Invalid argument passed
5942 @throws randolf::rex::xENOMEM Insufficient memory
5943 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5944 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5945 doesn't refer to a socket
5946 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5949 @returns uint32_t (converted to local endianness)
5950 @see recv_uint32_msb
5951 @see send_uint32_lsb
5953 *///=========================================================================
5954 uint32_t recv_uint32_lsb(
5959 /// MSG_CMSG_CLOEXEC
5960 const int posix_flags = 0) {
5962 if (__debug) debug("recv_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5964 + ", " + std::to_string(sizeof(buf))
5965 + ", " + std::to_string(posix_flags)
5967 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5968 return !__endian_is_msb ? buf : htonl(buf);
5969 } // -x- uint32_t recv_uint32_lsb -x-
5971 /*======================================================================*//**
5973 Receive one 32-bit unsigned integer of data in MSB (big endian) order from
5976 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5977 reception, which implements temporary blocking while waiting for data.
5979 @throws randolf::rex::xEBADF The underlying socket is not open
5980 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5982 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5983 part of the user address space
5984 @throws randolf::rex::xEINTR Interrupted by a signal
5985 @throws randolf::rex::xEINVAL Invalid argument passed
5986 @throws randolf::rex::xENOMEM Insufficient memory
5987 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5988 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5989 doesn't refer to a socket
5990 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5993 @returns uint32_t (converted to local endianness)
5994 @see recv_uint32_lsb
5995 @see send_uint32_msb
5997 *///=========================================================================
5998 uint32_t recv_uint32_msb(
6003 /// MSG_CMSG_CLOEXEC
6004 const int posix_flags = 0) {
6006 if (__debug) debug("recv_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6008 + ", " + std::to_string(sizeof(buf))
6009 + ", " + std::to_string(posix_flags)
6011 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6012 return __endian_is_msb ? buf : ntohl(buf);
6013 } // -x- uint32_t recv_uint32_msb -x-
6015 /*======================================================================*//**
6017 Receive one 64-bit unsigned integer of data in LSB (little endian) order from
6020 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
6021 reception, which implements temporary blocking while waiting for data.
6023 @throws randolf::rex::xEBADF The underlying socket is not open
6024 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6026 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6027 part of the user address space
6028 @throws randolf::rex::xEINTR Interrupted by a signal
6029 @throws randolf::rex::xEINVAL Invalid argument passed
6030 @throws randolf::rex::xENOMEM Insufficient memory
6031 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6032 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6033 doesn't refer to a socket
6034 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6037 @returns uint64_t (converted to local endianness)
6038 @see recv_uint64_msb
6039 @see send_uint64_lsb
6041 *///=========================================================================
6042 uint64_t recv_uint64_lsb(
6047 /// MSG_CMSG_CLOEXEC
6048 const int posix_flags = 0) {
6050 if (__debug) debug("recv_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6052 + ", " + std::to_string(sizeof(buf))
6053 + ", " + std::to_string(posix_flags)
6055 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6056 return !__endian_is_msb ? buf : ((((uint64_t)ntohl((buf) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((buf) >> 32)));
6057 } // -x- uint64_t recv_uint64_lsb -x-
6059 /*======================================================================*//**
6061 Receive one 64-bit unsigned integer of data in MSB (big endian) order from
6064 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
6065 reception, which implements temporary blocking while waiting for data.
6067 @throws randolf::rex::xEBADF The underlying socket is not open
6068 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6070 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6071 part of the user address space
6072 @throws randolf::rex::xEINTR Interrupted by a signal
6073 @throws randolf::rex::xEINVAL Invalid argument passed
6074 @throws randolf::rex::xENOMEM Insufficient memory
6075 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6076 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6077 doesn't refer to a socket
6078 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6081 @returns uint64_t (converted to local endianness)
6082 @see recv_uint64_lsb
6083 @see send_uint64_msb
6085 *///=========================================================================
6086 uint64_t recv_uint64_msb(
6091 /// MSG_CMSG_CLOEXEC
6092 const int posix_flags = 0) {
6094 if (__debug) debug("recv_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6096 + ", " + std::to_string(sizeof(buf))
6097 + ", " + std::to_string(posix_flags)
6099 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6100 return __endian_is_msb ? buf : ((((uint64_t)htonl((buf) & 0xffffffffUL)) << 32) | htonl((uint32_t)((buf) >> 32)));
6101 } // -x- uint64_t recv_uint64_msb -x-
6103 /*======================================================================*//**
6105 Receive data from a specific endpoint.
6108 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6109 reception, but it's important to note that it does implement temporary
6110 blocking while waiting for data.
6113 This method is not compatible with TLS.
6115 @throws randolf::rex::xEBADF The underlying socket is not open
6116 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6118 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6119 part of the user address space
6120 @throws randolf::rex::xEINTR Interrupted by a signal
6121 @throws randolf::rex::xEINVAL Invalid argument passed
6122 @throws randolf::rex::xENOMEM Insufficient memory
6123 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6124 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6125 doesn't refer to a socket
6126 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6129 @returns Appropriately-sized vector of characters
6132 *///=========================================================================
6133 std::vector<char> recvfrom(
6134 /// Maximum number of bytes to receive
6135 const size_t nbytes,
6140 /// MSG_CMSG_CLOEXEC
6141 const int posix_flags,
6142 /// Target endpoint address structure
6143 struct sockaddr *from,
6144 /// Size of target endpoint structure
6145 socklen_t fromlen = sizeof(sockaddr)) {
6146 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6147 if (__debug) debug("recvfrom(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6149 + ", " + std::to_string(buf_size)
6150 + ", " + std::to_string(posix_flags)
6152 + ", " + std::to_string(fromlen)
6154 std::vector<char> v(buf_size);
6155 v.resize(__track_bytes_rx(__rc_check(::recvfrom(__socket_fd, v.data(), v.size(), posix_flags, from, &fromlen))));
6157 } // -x- std::vector<char> recvfrom -x-
6160 /*======================================================================*//**
6162 This is an internal function that:
6163 1. receives data from the endpoint:
6164 - via underlying socket when TLS is not enabled
6165 - via the OpenSSL socket API when TLS is enabled
6166 2. checks for a socket I/O error (and throws an exception)
6167 3. tracks number of bytes received (if there were no errors)
6170 The ring buffer will only be used if there is one. The @ref __recvline
6171 method is one method that instantiates the internal ring buffer, and there
6172 could be others in the future (although this is not strongly anticipated
6173 aside from a method that allows the software developer to explicitly choose
6174 to have a buffer active for all socket-read operations).
6176 @throws randolf::rex::xEAGAIN The underlying socket timed out
6177 @throws randolf::rex::xEBADF The underlying socket is not open
6178 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6180 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6181 part of the user address space
6182 @throws randolf::rex::xEINTR Interrupted by a signal
6183 @throws randolf::rex::xEINVAL Invalid argument passed
6184 @throws randolf::rex::xENOMEM Insufficient memory
6185 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6186 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6187 doesn't refer to a socket
6188 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6191 @returns Number of bytes that were successfully received
6194 *///=========================================================================
6198 /// Maximum number of bytes to receive into @c data array
6200 /// Flags (ignored with encrypted streams, except for the MSG_PEEK flag)
6201 const int posix_flags = 0) {
6203 // --------------------------------------------------------------------------
6204 // When internal buffering is not set up (which is the default operation), we
6205 // simply pass data directly.
6206 // --------------------------------------------------------------------------
6207 if (__buffer == nullptr) {
6208 return __tls ? (posix_flags & MSG_PEEK ? __rc_check_tls(SSL_peek(__tls_fd, data, len))
6209 : __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, data, len))))
6210 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, data, len, posix_flags)));
6211 } // -x- if !__buffer -x-
6213 // --------------------------------------------------------------------------
6214 // If the amount of data contained in the internal buffer is sufficient to
6215 // satisfy the amount of data that "len" represents, then all that's needed
6216 // is to return "len" bytes from the ring buffer without having to receive
6217 // more data from the socket.
6218 // --------------------------------------------------------------------------
6219 if (len <= __buffer->get_utilized()) {
6220 if (posix_flags & MSG_PEEK) __buffer->peek_to_array( (size_t)len, (char*)data); // Copy character(s) from ring buffer because MSG_PEEK flag is set
6221 else __buffer->remove_to_array((size_t)len, (char*)data); // Consume character(s) from ring buffer
6225 // --------------------------------------------------------------------------
6226 // Consume data from socket to make up for OpenSSL's inability to read beyond
6227 // one packet of data in its SSL_peek() and SSL_read() methods, and also some
6228 // inconsistencies with certain raw (unencrypted) socket implementations that
6229 // occasionally refuse to read beyond one packet of data (at least on a first
6230 // attempt to read with or without the MSG_PEEK flag).
6232 // Fill internal buffer. It may be more than len is set to, but this is okay
6233 // because we're committed to using internal buffering now anyway (it's far
6234 // more likely that a protocol that requires the use of recvline() even once
6235 // is going to be using it repeatedly, but as long as the ring buffer is
6236 // large enough to cover the maximum length of a line of text, including its
6237 // EoL sequence, then this will always succeed).
6238 // --------------------------------------------------------------------------
6239 __buffer->defragment(true);
6240 __buffer_bam = __buffer->data_bam(__buffer_bam); // Update ring buffer setup (will be allocated automatically if "nullptr" default)
6241 size_t available = __buffer->get_available();
6242 int n = 0; // Number of bytes
6243 if (__tls) n = __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, __buffer_bam->avail1, std::min(__buffer_bam->avail1_size, (size_t)len)))); // Not using SSL_peek() at all here because we need to consume this data as we stuff it into the internal buffer
6244 else n = __track_bytes_rx(__rc_check( ::recv(__socket_fd, __buffer_bam->avail1, std::min(__buffer_bam->avail1_size, (size_t)len), posix_flags & (~MSG_PEEK)))); // Mask the MSG_PEEK flag because we need to consume this data as we stuff it into the internal buffer
6245 __buffer->set_head(n, false); // Add number of bytes received to the ring buffer after receiving directly into the available portion of the ring buffer
6247 // --------------------------------------------------------------------------
6248 // Copy or move data depending on whether POSIX flag MSG_PEEK was set.
6249 // --------------------------------------------------------------------------
6250 n = std::min(len, (int)__buffer->get_utilized()); // Work with the lesser number of bytes
6251 if (n > 0) { // At least 1 byte is available
6252 if (posix_flags & MSG_PEEK) __buffer->peek_to_array( (size_t)n, (char*)data); // Copy character(s) from ring buffer because MSG_PEEK flag is set
6253 else __buffer->remove_to_array((size_t)n, (char*)data); // Consume character(s) from ring buffer
6255 return n; // Return the number of bytes that were copied/moved
6257 } // -x- int __recv -x-
6259 /*======================================================================*//**
6261 This is an internal function that:
6262 1. receives a line of data from the endpoint:
6263 - via underlying socket when TLS is not enabled
6264 - via the OpenSSL socket API when TLS is enabled
6265 2. checks for a socket I/O error (and throws an exception)
6266 3. tracks number of bytes received (if there were no errors)
6267 4. isolates the EoL sequence from the line of data, and records it for
6268 later reference (for those cases where it's needed)
6271 The two exceptions that need to be handled are @ref randolf::rex::xEOVERFLOW
6272 (when the endpoint sends oversized lines) and @ref randolf::rex::xETIMEDOUT
6273 (when the endpoint takes too long to respond) for a robust usage.
6275 @throws randolf::rex::xEBADF The underlying socket is not open
6276 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6278 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6279 part of the user address space
6280 @throws randolf::rex::xEINTR Interrupted by a signal
6281 @throws randolf::rex::xEINVAL Invalid argument passed
6282 @throws randolf::rex::xENOMEM Insufficient memory
6283 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6284 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6285 doesn't refer to a socket
6286 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
6287 no EoL character sequence is detected after recvline's buffer became
6288 full (whatever data is waiting may still be received using any of the
6289 recv_ methods {with or without the @c MSG_PEEK flag set}, including
6290 @c recvline() with a larger buffer {see the @c nbytes parameter})
6291 @throws randolf::rex::xERANGE if the timeout parameter is below 0
6292 @throws randolf::rex::xETIMEDOUT If a @c timeout occurs
6295 *///=========================================================================
6296 std::string __recvline(
6297 /// Maximum number of bytes to receive (including EoL character sequence)
6300 /// MSG_PEEK (with some limitations)@n
6302 /// MSG_DONTWAIT (ineffective when @c timeout is not 0)@n
6303 /// MSG_CMSG_CLOEXEC
6304 const int posix_flags,
6305 /// Line timeout (in seconds)@n
6306 /// 0 = no timeout (default), unless it was configured by way of the
6307 /// @ref timeout_recvline(long) method
6308 const ulong timeout,
6309 /// Configuration parameters
6310 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
6312 // --------------------------------------------------------------------------
6313 // Internal variables.
6314 // --------------------------------------------------------------------------
6315 std::string v; // Internal buffer (we use std::string to avoid conversion steps at return)
6316 v.resize(buf_size); // Pre-allocate data so we can write to it directly
6318 // --------------------------------------------------------------------------
6319 // If an internal buffer, which is used by __recv, hasn't been instantiated,
6320 // then instantiate it and save the pointer to it for future use by __recv().
6322 // Buffering becomes necessary with TLS connection because SSL_peek fails to
6323 // return data that spans multiple packets. What triggers this behaviour is
6324 // the command "stty -icanon && openssl s_client host_name tcp_port" followed
6325 // by manual interactive typing some text then pressing the "[Enter]" key.
6327 // The ::malloc(__buffer_size) won't be leaked because it will be freed in
6328 // in this rsocket's destructor (if it isn't freed elsewhere before this).
6329 // --------------------------------------------------------------------------
6330 if (__buffer == nullptr) __buffer = new rring(__buffer_size);
6332 // --------------------------------------------------------------------------
6333 // Calculate timeout_target for easy comparisons with time(0) in the loop,
6334 // and other internal variables.
6335 // --------------------------------------------------------------------------
6336 ulong timeout_target = timeout == 0 ? (__recvline_timeout == 0 ? ULONG_MAX : __recvline_timeout + time(0)) : timeout + time(0);
6337 int br = 0; // Number of Bytes Received
6339 int len_with_eol = 0;
6341 // --------------------------------------------------------------------------
6342 // Take advantage of RAII in C++ to ensure that the non-recvline timeout
6343 // settings will be restored upon returning normally or throwing exceptions.
6344 // --------------------------------------------------------------------------
6345 RAII_timeout raii(this, timeout);
6347 // --------------------------------------------------------------------------
6348 // Line-reading loop.
6349 // --------------------------------------------------------------------------
6352 // --------------------------------------------------------------------------
6353 // Use exceptions to handle timeouts and generate randolf::rex::xETIMEDOUT,
6354 // which is helpful because timeout errors vary on different Operating
6355 // Systems, systems configurations, and in different socket implementations.
6356 // --------------------------------------------------------------------------
6359 // --------------------------------------------------------------------------
6360 // Attempt to read data, but without consuming it. The reason for this is
6361 // that we need to scan the data for an EoL sequence, but if there isn't one
6362 // then we need to try again while waiting for more data. (The current data
6363 // will be buffered by __recv so we will actually be waiting for more data to
6364 // to arrive from the endpoint.)
6365 // --------------------------------------------------------------------------
6366 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)
6368 } catch (const randolf::rex::xEAGAIN e) { // Socket timeout derived from EAGAIN error
6369 throw randolf::rex::xETIMEDOUT("randolf::rex::xEAGAIN");
6370 } catch (const randolf::rex::xEWOULDBLOCK e) { // Socket timeout derived from EWOULDBLOCK error
6371 throw randolf::rex::xETIMEDOUT("randolf::rex::xEWOULDBLOCK");
6372 } catch (const randolf::rex::xTLS_SYSCALL e) { // Socket timeout derived from EWOULDBLOCK error
6373 throw randolf::rex::xETIMEDOUT("randolf::rex::xTLS_SYSCALL");
6374 } catch (const std::exception e) {
6375 std::cout << "Unknown error " << e.what() << std::endl;
6379 // --------------------------------------------------------------------------
6380 // If EoL sequence found, save the consumed EoL sequence and return the line.
6381 // --------------------------------------------------------------------------
6382 if ((len = eol_index(v, &len_with_eol)) >= 0) { // EoL found, so we're done now (-1 = no EoL found)
6383 if (!(posix_flags & MSG_PEEK))
6384 __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
6385 //discard(len_with_eol);
6386 __eol_consumed_seq = v.substr(len, len_with_eol - len); // Save EoL sequence
6387 v.resize(len); // Truncate string without the EoL sequence
6391 // --------------------------------------------------------------------------
6392 // If EoF wasn't found and the maximum line length has been reached, then we
6393 // need to end this loop so that we can handle any recvline_flags and/or
6394 // throw an xEOVERFLOW exception.
6395 // --------------------------------------------------------------------------
6396 } while (br != buf_size); // Loop again if buffer isn't full yet
6398 // --------------------------------------------------------------------------
6399 // If a timeout occurred, then throw the randolf::rex::xETIMEDOUT exception.
6400 // --------------------------------------------------------------------------
6402 if (time(0) > timeout_target)
6403 throw randolf::rex::xETIMEDOUT("recvline timed out"); // We reached the line timeout target
6405 // ==========================================================================
6406 // We've exited the loop, and now we're deciding how to handle a full buffer
6407 // that doesn't have an EoF, and obviously indicates line-overflow situation.
6408 // ==========================================================================
6410 // --------------------------------------------------------------------------
6411 // Caller wants partial data instead of throwing an exception, even if it has
6413 // --------------------------------------------------------------------------
6414 if (recvline_flags & RECVLINE_PARTIAL) {
6415 __eol_consumed_seq = ""; // Indicate that there's no EoL sequence
6416 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
6417 v.resize(br); // Truncate string to "br" (bytes received)
6419 } // -x- if RECVLINE_PARTIAL -x-
6421 // --------------------------------------------------------------------------
6422 // Generate xEOVERFLOW exception.
6424 // TODO: Throw a different exception for no EoL when the buffer isn't full.
6425 // --------------------------------------------------------------------------
6426 if (!(recvline_flags & RECVLINE_NO_DISCARD_ON_OVERFLOW) && !(posix_flags & MSG_PEEK))
6427 discard_line(); // Consume all that remains, or stop after first EoL sequence encountered
6428 //std::cout << "discard_line=" << discard_line() << std::endl;
6429 throw randolf::rex::xEOVERFLOW("recvline overflow (line too long or missing EoL sequence)");
6431 } // -x- std::string __recvline -x-
6434 /*======================================================================*//**
6436 Receive a line of data from the endpoint, with the EoL character(s) removed.
6437 While this is meant for ASCII and UTF-8 text, it will also work with binary
6438 data that doesn't include EoL character sequences as non-line-ending data
6439 (when receiving binary data, the @ref recv() and @ref recvz() methods tend to
6442 If @c nbytes is 0, then the internal buffer_size will be used as the default,
6443 which can also be changed from its compiled-in default of 1024 by using one
6444 of the buffer_size() methods. The total number of bytes received, including
6445 the EoL sequence, will not exceed the total number of byte specified with the
6446 @c nbytes parameter.
6448 For @c nbytes the EoL character sequence size should be included, especially
6449 for shorter lines -- if the EoL character sequence is detected automatically
6450 on-the-fly (which is the default), then provisioning 2 characters is needed
6451 since an EoL sequence could be 1 or 2 characters long (line lengths shorter
6452 than 3 characters could be particularly problematic when detecting on-the-fly
6453 EoL character sequences). In such a case, a better approach is to allow for
6454 2 additional characters and then test the length of the returned string to
6455 ensure it doesn't exceed whatever the required maximum is (and throw the same
6456 @ref randolf::rex::xEOVERFLOW exception if the length is exceeded).
6458 For more information about which characters an EoL sequence is comprised of,
6459 and how our specialized support for multiple EoL sequences works, see the
6460 documentation for the various @ref eol methods.
6463 When setting the recvline timeout (either with the @c timeout parameter with
6464 this method, or by using the @ref timeout_recvline(long) method), you may
6465 also need to set the socket timeout beforehand using the @ref timeout()
6466 method, because @c recvline doesn't do this...
6469 @ref timeout_recvline sets the overall total timeout for an entire line to
6472 @ref timeout sets the timeout between individual characters received (such
6473 as keystrokes from a live end-user)
6476 If your socket timeout is longer than then recvline timeout, this will have
6477 the effect of rendering the recvling timeout as ineffective.
6479 The @c timeout parameter can be used to override the total overall timeout
6480 for receiving a line, which is useful for specific situations where a
6481 specific timeout is desired for particular prompts, during certain data entry
6485 When a line is not found (@c randolf::rex::xEAGAIN), or a line is too long
6486 because there's too much data (longer than a line) without an EoL sequence
6487 (@c randolf::rex::xOVERFLOW), then that data will not be discarded or
6488 consumed, and will remain ready for receiving (@ref recv) or for discarding
6492 If you're searching for a readline() method for socket I/O, then this is most
6493 likely what you're wanting/needing.
6495 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
6496 @throws randolf::rex::xEBADF The underlying socket is not open
6497 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6499 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6500 part of the user address space
6501 @throws randolf::rex::xEINTR Interrupted by a signal
6502 @throws randolf::rex::xEINVAL Invalid argument passed
6503 @throws randolf::rex::xENOMEM Insufficient memory
6504 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6505 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6506 doesn't refer to a socket
6507 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
6508 no EoL character sequence is detected after recvline's buffer became
6509 full (whatever data is waiting may still be received using any of the
6510 recv_ methods {with or without the @c MSG_PEEK flag set}, including
6511 @c recvline() with a larger buffer {see the @c nbytes parameter})
6512 @throws randolf::rex::xERANGE if the timeout parameter is below 0
6513 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6516 @returns One line of text as a std::string object.
6519 @see eol_consumed_seq()
6521 @see sendline(const std::string, const int)
6523 @see timeout_recvline
6524 @see timeout_recvline(long)
6526 *///=========================================================================
6527 std::string recvline(
6528 /// Maximum number of bytes to receive (including EoL character sequence)@n
6529 /// 0 = use internal @ref buffer_size()
6530 const size_t nbytes = 0,
6535 /// MSG_CMSG_CLOEXEC
6536 const int posix_flags = 0,
6537 /// Line timeout (in seconds)@n
6538 /// 0 = no timeout (default) or whatever @ref timeout was set to, unless it
6539 /// was configured by way of the @ref timeout_recvline(long) method
6541 /// Configuration parameters
6542 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
6543 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
6544 // + (__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
6545 // Internally, the length of the @ref eol() sequence is added to the buffer size
6546 // so that the maximum line length without EoL characters matches the maximum
6547 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
6548 if (__debug) debug("recvline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6549 + ", " + std::to_string(buf_size)
6550 + ", " + std::to_string(posix_flags)
6552 return __recvline(buf_size, posix_flags, timeout, recvline_flags);
6553 } // -x- std::string recvline -x-
6555 /*======================================================================*//**
6557 Receive data in the form of a "msghdr" structure.
6559 This method is not compatible with TLS.
6561 @throws randolf::rex::xEBADF The underlying socket is not open
6562 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6564 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6565 part of the user address space
6566 @throws randolf::rex::xEINTR Interrupted by a signal
6567 @throws randolf::rex::xEINVAL Invalid argument passed
6568 @throws randolf::rex::xENOMEM Insufficient memory
6569 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6570 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6571 doesn't refer to a socket
6572 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6575 @returns pointer to "msghdr" structure
6578 *///=========================================================================
6580 /// Pointer to "msghdr" structure (or nullptr for automatic allocation)
6586 /// MSG_CMSG_CLOEXEC
6587 const int posix_flags = 0) {
6588 if (__debug) debug("recvmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6590 + ", " + std::to_string(posix_flags)
6592 if (msg == nullptr) msg = new msghdr{0};
6593 __track_bytes_rx(__rc_check(::recvmsg(__socket_fd, msg, posix_flags)));
6595 } // -x- msghdr* recvmsg -x-
6597 /*======================================================================*//**
6599 Receive data in the form of an "mmsghdr" structure.
6601 This method is not compatible with TLS.
6603 @throws randolf::rex::xEBADF The underlying socket is not open
6604 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6606 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6607 part of the user address space
6608 @throws randolf::rex::xEINTR Interrupted by a signal
6609 @throws randolf::rex::xEINVAL Invalid argument passed
6610 @throws randolf::rex::xENOMEM Insufficient memory
6611 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6612 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6613 doesn't refer to a socket
6614 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6617 @returns pointer to "mmsghdr" structure
6620 *///=========================================================================
6622 /// Pointer to "mmsghdr" structure (or nullptr for automatic allocation)
6623 struct mmsghdr* mmsg,
6624 /// Size of target endpoint structure
6625 const unsigned int vlen = sizeof(mmsghdr),
6631 const int posix_flags = 0,
6633 struct timespec* timeout = {0}) {
6634 if (__debug) debug("recvmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6636 + ", " + std::to_string(vlen)
6637 + ", " + std::to_string(posix_flags)
6638 + ", timeout{tv_sec=" + std::to_string(timeout->tv_sec)
6639 + ", tv_nsec=" + std::to_string(timeout->tv_nsec) + "}"
6641 __track_bytes_rx(__rc_check(::recvmmsg(__socket_fd, mmsg, vlen, posix_flags, timeout)));
6643 } // -x- mmsghdr* recvmmsg -x-
6645 /*======================================================================*//**
6647 Receive data from the endpoint, and add a 0 (null) onto the end. This is
6648 useful when using the resulting std::vector<char> as an ASCIIZ string.
6650 If nbytes is 0, then the internal buffer_size will be used as the default,
6651 which can also be changed from its compiled-in default of 1024 by using one
6652 of the buffer_size() methods.
6655 The resulting std::vector size is always inflated by 1. This means that
6656 relying on a comparison against 0 will result in an infinite loop:
6658 for (std::vector<char> v = r.recvz(); v.size > 0; v = recvz()) { ... }
6660 So, you'll need to compare against 1 instead of 0 to compensate fot the
6661 inflated size due to the addition of null character 0:
6663 for (std::vector<char> v = r.recvz(); v.size > 1; v = recvz()) { ... }
6667 The resulting size of std::vector<char> will be <tt>nbytes + 1</tt> which
6668 ensures that any string processing functions or presentation libraries - such
6669 as @c printf() - expecting an ASCIIZ string buffer will stop at null (0), and
6670 will reliably output no more than the maximum size specified.
6672 The way to think of this is that @c nbytes specifies the maximum number of
6673 bytes (a.k.a., characters) to recieve over the underlying socket, and the
6674 final 0 (null) being added is not included in the maximum specified by the
6675 @c nbytes parameter.
6678 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6679 reception, but it's important to note that it does implement temporary
6680 blocking while waiting for data.
6682 @throws randolf::rex::xEBADF The underlying socket is not open
6683 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6685 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6686 part of the user address space
6687 @throws randolf::rex::xEINTR Interrupted by a signal
6688 @throws randolf::rex::xEINVAL Invalid argument passed
6689 @throws randolf::rex::xENOMEM Insufficient memory
6690 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6691 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6692 doesn't refer to a socket
6693 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6696 @returns appropriately-sized vector of characters + 1 (includes additional
6697 null terminator character 0)
6698 @see recv(const size_t, const int)
6699 @see recv(std::vector<char>, const int)
6702 *///=========================================================================
6703 std::vector<char> recvz(
6704 /// Maximum number of bytes to receive
6705 const size_t nbytes = 0,
6710 /// MSG_CMSG_CLOEXEC
6711 const int posix_flags = 0) {
6712 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6713 if (__debug) debug("recvz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6715 + ", " + std::to_string(buf_size)
6716 + ", " + std::to_string(posix_flags)
6718 std::vector<char> v(buf_size); // TODO: This may need to be changed to std::vector<char>*
6719 v.resize(__recv(v.data(), v.size(), posix_flags) + 1); // Add 1 to include room for final null character 0
6720 v[v.size() - 1] = (char)0; // Set final element to 0 (null)
6722 } // -x- std::vector<char> recvz -x-
6725 /*======================================================================*//**
6727 This is an internal function that:
6728 1. sends data to the endpoint:
6729 - via underlying socket when TLS is not enabled
6730 - via the OpenSSL socket API when TLS is enabled
6731 2. checks for a socket I/O error (and throws an exception)
6732 3. tracks number of bytes transmitted (if there were no errors)
6735 This method is threadsafe because the underlying POSIX send() and OpenSSL's
6736 SSL_write() functions are threadsafe.
6738 @throws randolf::rex::xEBADF The underlying socket is not open
6739 @throws randolf::rex::xECONNRESET Connect reset by peer
6740 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6741 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6742 part of the user address space
6743 @throws randolf::rex::xEINTR Interrupted by a signal
6744 @throws randolf::rex::xEINVAL Invalid argument passed
6745 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6746 occur, but the POSIX sockets documentation lists it as one of the
6747 errors that can be returned, perhaps because some incorrectly
6748 implemented TCP/IP stacks return this error?)
6749 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6750 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6751 and 65,527 bytes for IPv6)
6752 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6753 network congestion (or, less commonly, insufficient memory)
6754 @throws randolf::rex::xENOMEM Insufficient memory
6755 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6756 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6757 doesn't refer to a socket
6758 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6759 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6760 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6762 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6764 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6765 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6766 but it really isn't)
6768 @returns Number of bytes that were successfully transmitted
6771 *///=========================================================================
6775 /// Length of message data
6777 /// Flags (ignored with encrypted streams)
6778 const int posix_flags = 0) {
6779 if (__tls) return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data, len)));
6780 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data, len, posix_flags)));
6781 } // -x- int __send -x-
6783 /*======================================================================*//**
6785 This is an internal function that:
6786 1. sends data to the endpoint:
6787 - via underlying socket when TLS is not enabled
6788 - via the OpenSSL socket API when TLS is enabled
6789 2. checks for a socket I/O error (and throws an exception)
6790 3. tracks number of bytes transmitted (if there were no errors)
6793 This method is threadsafe because the underlying POSIX send() and OpenSSL's
6794 SSL_write() functions are threadsafe.
6796 @throws randolf::rex::xEBADF The underlying socket is not open
6797 @throws randolf::rex::xECONNRESET Connect reset by peer
6798 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6799 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6800 part of the user address space
6801 @throws randolf::rex::xEINTR Interrupted by a signal
6802 @throws randolf::rex::xEINVAL Invalid argument passed
6803 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6804 occur, but the POSIX sockets documentation lists it as one of the
6805 errors that can be returned, perhaps because some incorrectly
6806 implemented TCP/IP stacks return this error?)
6807 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6808 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6809 and 65,527 bytes for IPv6)
6810 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6811 network congestion (or, less commonly, insufficient memory)
6812 @throws randolf::rex::xENOMEM Insufficient memory
6813 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6814 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6815 doesn't refer to a socket
6816 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6817 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6818 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6820 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6822 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6823 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6824 but it really isn't)
6826 @returns Number of bytes that were successfully transmitted
6829 *///=========================================================================
6833 /// Number of bytes to send
6835 /// Flags (ignored with encrypted streams)
6836 const int posix_flags = 0) {
6837 if (__tls) { // Encrypted
6838 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data , len )))
6839 + __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.data(), __eol_out.length())));
6840 } else { // Not encrypted
6841 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data , len, posix_flags | MSG_MORE))) // MSG_MORE prevents extra packets from being sent
6842 + __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.data(), __eol_out.length(), posix_flags )));
6843 } // -x- if __tls -x-
6844 } // -x- int __sendline -x-
6846 /*======================================================================*//**
6848 This is an internal function that:
6849 1. sends data to the endpoint:
6850 - via underlying socket when TLS is not enabled
6851 - via the OpenSSL socket API when TLS is enabled
6852 2. checks for a socket I/O error (and throws an exception)
6853 3. tracks number of bytes transmitted (if there were no errors)
6856 This method is threadsafe because the underlying POSIX send() and OpenSSL's
6857 SSL_write() functions are threadsafe.
6859 @throws randolf::rex::xEBADF The underlying socket is not open
6860 @throws randolf::rex::xECONNRESET Connect reset by peer
6861 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6862 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6863 part of the user address space
6864 @throws randolf::rex::xEINTR Interrupted by a signal
6865 @throws randolf::rex::xEINVAL Invalid argument passed
6866 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6867 occur, but the POSIX sockets documentation lists it as one of the
6868 errors that can be returned, perhaps because some incorrectly
6869 implemented TCP/IP stacks return this error?)
6870 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6871 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6872 and 65,527 bytes for IPv6)
6873 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6874 network congestion (or, less commonly, insufficient memory)
6875 @throws randolf::rex::xENOMEM Insufficient memory
6876 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6877 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6878 doesn't refer to a socket
6879 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6880 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6881 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6883 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6885 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6886 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6887 but it really isn't)
6889 @returns Number of bytes that were successfully transmitted
6891 *///=========================================================================
6893 /// Flags (ignored with encrypted streams)
6894 const int posix_flags = 0) {
6895 if (__tls) { // Encrypted
6896 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.data(), __eol_out.length())));
6897 } else { // Not encrypted
6898 return __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.data(), __eol_out.length(), posix_flags)));
6899 } // -x- if __tls -x-
6900 } // -x- int __send_eol -x-
6901 // TODO: Create a recv_eol() method that some people will probably consider to be evil (ha ha!)
6902 // recv_eol() will need to be able to auto-detect the CRLF sequence on-the-fly (if necessary) just like recvline() does
6905 /*======================================================================*//**
6907 Send data in the form of a std::string to the endpoint.
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
6944 *///=========================================================================
6947 const std::string msg,
6954 const int posix_flags = 0) {
6955 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6957 + ", " + std::to_string(msg.length())
6958 + ", " + std::to_string(posix_flags)
6960 __send(msg.data(), msg.length(), posix_flags);
6962 } // -x- rsocket* send -x-
6964 /*======================================================================*//**
6966 Send data in the form of a std::vector<char> to the endpoint.
6968 This method is threadsafe.
6970 @throws randolf::rex::xEBADF The underlying socket is not open
6971 @throws randolf::rex::xECONNRESET Connect reset by peer
6972 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6973 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6974 part of the user address space
6975 @throws randolf::rex::xEINTR Interrupted by a signal
6976 @throws randolf::rex::xEINVAL Invalid argument passed
6977 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6978 occur, but the POSIX sockets documentation lists it as one of the
6979 errors that can be returned, perhaps because some incorrectly
6980 implemented TCP/IP stacks return this error?)
6981 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6982 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6983 and 65,527 bytes for IPv6)
6984 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6985 network congestion (or, less commonly, insufficient memory)
6986 @throws randolf::rex::xENOMEM Insufficient memory
6987 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6988 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6989 doesn't refer to a socket
6990 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6991 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6992 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6994 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6996 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6997 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6998 but it really isn't)
7000 @returns The same rsocket object so as to facilitate stacking
7001 @see recv(std::vector<char>, const int)
7003 *///=========================================================================
7006 const std::vector<char> msg,
7013 const int posix_flags = 0) {
7014 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7016 + ", " + std::to_string(msg.size())
7017 + ", " + std::to_string(posix_flags)
7019 __send(msg.data(), msg.size(), posix_flags);
7021 } // -x- rsocket* send -x-
7023 /*======================================================================*//**
7025 Send data in the form of a C-string to the endpoint.
7027 This method is threadsafe.
7029 @throws randolf::rex::xEBADF The underlying socket is not open
7030 @throws randolf::rex::xECONNRESET Connect reset by peer
7031 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7032 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7033 part of the user address space
7034 @throws randolf::rex::xEINTR Interrupted by a signal
7035 @throws randolf::rex::xEINVAL Invalid argument passed
7036 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7037 occur, but the POSIX sockets documentation lists it as one of the
7038 errors that can be returned, perhaps because some incorrectly
7039 implemented TCP/IP stacks return this error?)
7040 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7041 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7042 and 65,527 bytes for IPv6)
7043 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7044 network congestion (or, less commonly, insufficient memory)
7045 @throws randolf::rex::xENOMEM Insufficient memory
7046 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7047 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7048 doesn't refer to a socket
7049 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7050 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7051 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7053 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7055 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7056 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7057 but it really isn't)
7059 @returns The same rsocket object so as to facilitate stacking
7060 @see recv(const size_t, const int)
7063 *///=========================================================================
7065 /// Pointer to data to send
7067 /// Number of bytes to send, or 0 to auto-detect length if message is an ASCIIZ string
7075 const int posix_flags = 0) {
7076 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7078 + ", " + std::to_string(len)
7079 + ", " + std::to_string(posix_flags)
7082 // --------------------------------------------------------------------------
7083 // Measure size of format string if an ASCIIZ string was indicated.
7084 // --------------------------------------------------------------------------
7085 if (len == 0) len = std::strlen(msg);
7087 // --------------------------------------------------------------------------
7089 // --------------------------------------------------------------------------
7090 __send(msg, len, posix_flags);
7092 } // -x- rsocket* send -x-
7094 /*======================================================================*//**
7096 Send data in the form of an ASCIIZ string to the endpoint, including the
7097 terminating NULL character.
7099 This method is threadsafe.
7101 @throws randolf::rex::xEBADF The underlying socket is not open
7102 @throws randolf::rex::xECONNRESET Connect reset by peer
7103 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7104 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7105 part of the user address space
7106 @throws randolf::rex::xEINTR Interrupted by a signal
7107 @throws randolf::rex::xEINVAL Invalid argument passed
7108 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7109 occur, but the POSIX sockets documentation lists it as one of the
7110 errors that can be returned, perhaps because some incorrectly
7111 implemented TCP/IP stacks return this error?)
7112 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7113 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7114 and 65,527 bytes for IPv6)
7115 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7116 network congestion (or, less commonly, insufficient memory)
7117 @throws randolf::rex::xENOMEM Insufficient memory
7118 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7119 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7120 doesn't refer to a socket
7121 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7122 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7123 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7125 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7127 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7128 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7129 but it really isn't)
7131 @returns The same rsocket object so as to facilitate stacking
7132 @see recv_asciiz(const size_t, const int)
7133 @see sendz(const char*, const int) which doesn't transmit the terminating
7136 *///=========================================================================
7137 rsocket* send_asciiz(
7138 /// Pointer to data to send
7146 const int posix_flags = 0) {
7147 if (__debug) debug("send_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7149 + ", " + std::to_string(std::strlen(msg) + 1)
7150 + ", " + std::to_string(posix_flags)
7152 __send(msg, std::strlen(msg) + 1, posix_flags);
7154 } // -x- rsocket* send_asciiz -x-
7156 /*======================================================================*//**
7158 Send one 8-bit byte (one unsigned character) of data to the endpoint.
7160 This method is threadsafe.
7162 @throws randolf::rex::xEBADF The underlying socket is not open
7163 @throws randolf::rex::xECONNRESET Connect reset by peer
7164 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7165 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7166 part of the user address space
7167 @throws randolf::rex::xEINTR Interrupted by a signal
7168 @throws randolf::rex::xEINVAL Invalid argument passed
7169 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7170 occur, but the POSIX sockets documentation lists it as one of the
7171 errors that can be returned, perhaps because some incorrectly
7172 implemented TCP/IP stacks return this error?)
7173 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7174 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7175 and 65,527 bytes for IPv6)
7176 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7177 network congestion (or, less commonly, insufficient memory)
7178 @throws randolf::rex::xENOMEM Insufficient memory
7179 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7180 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7181 doesn't refer to a socket
7182 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7183 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7184 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7186 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7188 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7189 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7190 but it really isn't)
7192 @returns The same rsocket object so as to facilitate stacking
7197 *///=========================================================================
7207 const int posix_flags = 0) {
7208 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7210 + ", " + std::to_string(sizeof(value))
7211 + ", " + std::to_string(posix_flags)
7213 __send(&value, sizeof(value), posix_flags);
7215 } // -x- rsocket* send_byte -x-
7217 /*======================================================================*//**
7219 Send one signed character (one 8-bit byte) of data to the endpoint.
7221 @returns The same rsocket object so as to facilitate stacking
7226 *///=========================================================================
7236 const int posix_flags = 0) {
7237 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7239 + ", " + std::to_string(sizeof(value))
7240 + ", " + std::to_string(posix_flags)
7242 __send(&value, sizeof(value), posix_flags);
7244 } // -x- rsocket* send_char -x-
7246 /*======================================================================*//**
7248 Send the EoL sequence to the endpoint.
7250 This method is threadsafe.
7252 @throws randolf::rex::xEBADF The underlying socket is not open
7253 @throws randolf::rex::xECONNRESET Connect reset by peer
7254 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7255 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7256 part of the user address space
7257 @throws randolf::rex::xEINTR Interrupted by a signal
7258 @throws randolf::rex::xEINVAL Invalid argument passed
7259 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7260 occur, but the POSIX sockets documentation lists it as one of the
7261 errors that can be returned, perhaps because some incorrectly
7262 implemented TCP/IP stacks return this error?)
7263 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7264 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7265 and 65,527 bytes for IPv6)
7266 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7267 network congestion (or, less commonly, insufficient memory)
7268 @throws randolf::rex::xENOMEM Insufficient memory
7269 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7270 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7271 doesn't refer to a socket
7272 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7273 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7274 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7276 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7278 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7279 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7280 but it really isn't)
7282 @returns The same rsocket object so as to facilitate stacking
7288 *///=========================================================================
7296 const int posix_flags = 0) {
7297 if (__debug) debug("send_eol(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7298 + ", " + std::to_string(posix_flags)
7300 __send_eol(posix_flags);
7302 } // -x- rsocket* send_eol -x-
7304 /*======================================================================*//**
7306 Send data in the form of a @ref randolf::rline to the endpoint, with an EoL
7309 This method is threadsafe.
7311 @throws randolf::rex::xEBADF The underlying socket is not open
7312 @throws randolf::rex::xECONNRESET Connect reset by peer
7313 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7314 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7315 part of the user address space
7316 @throws randolf::rex::xEINTR Interrupted by a signal
7317 @throws randolf::rex::xEINVAL Invalid argument passed
7318 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7319 occur, but the POSIX sockets documentation lists it as one of the
7320 errors that can be returned, perhaps because some incorrectly
7321 implemented TCP/IP stacks return this error?)
7322 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7323 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7324 and 65,527 bytes for IPv6)
7325 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7326 network congestion (or, less commonly, insufficient memory)
7327 @throws randolf::rex::xENOMEM Insufficient memory
7328 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7329 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7330 doesn't refer to a socket
7331 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7332 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7333 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7335 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7337 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7338 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7339 but it really isn't)
7341 @returns The same rsocket object so as to facilitate stacking
7349 *///=========================================================================
7350 rsocket* send_rline(
7352 randolf::rline line = randolf::rline(),
7359 const int posix_flags = 0) {
7360 if (__debug) debug("sendline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7361 + ", <randolf::rline>"
7362 + ", " + std::to_string(line.length())
7363 + ", " + std::to_string(posix_flags)
7365 __sendline(line.data(), line.length(), posix_flags);
7367 } // -x- rsocket* send_rline -x-
7369 /*======================================================================*//**
7371 Send a data structure to the endpoint.
7373 MSB/LSB considerations are important for any integers within your structure,
7374 so be sure to sanitize them with htons(), ntohs(), and related methods prior
7375 to sending, and then after receiving. This way, your data will be protected
7376 against corruption resulting from byte order differences when communicating
7377 between hardware architectures that differ in MSB and LSB byte ordering.
7379 This method is threadsafe.
7381 @throws randolf::rex::xEBADF The underlying socket is not open
7382 @throws randolf::rex::xECONNRESET Connect reset by peer
7383 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7384 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7385 part of the user address space
7386 @throws randolf::rex::xEINTR Interrupted by a signal
7387 @throws randolf::rex::xEINVAL Invalid argument passed
7388 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7389 occur, but the POSIX sockets documentation lists it as one of the
7390 errors that can be returned, perhaps because some incorrectly
7391 implemented TCP/IP stacks return this error?)
7392 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7393 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7394 and 65,527 bytes for IPv6)
7395 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7396 network congestion (or, less commonly, insufficient memory)
7397 @throws randolf::rex::xENOMEM Insufficient memory
7398 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7399 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7400 doesn't refer to a socket
7401 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7402 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7403 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7405 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7407 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7408 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7409 but it really isn't)
7411 @returns The same rsocket object so as to facilitate stacking
7414 *///=========================================================================
7415 template <typename T> rsocket* send_struct(
7424 const int posix_flags = 0) {
7425 if (__debug) debug("send_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7427 + ", " + std::to_string(sizeof(value))
7428 + ", " + std::to_string(posix_flags)
7430 __send(&value, sizeof(value), posix_flags);
7432 } // -x- rsocket* send_struct -x-
7434 /*======================================================================*//**
7436 Send one 16-bit unsigned integer of data in LSB (little endian) order to the
7439 This method is threadsafe.
7441 @throws randolf::rex::xEBADF The underlying socket is not open
7442 @throws randolf::rex::xECONNRESET Connect reset by peer
7443 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7444 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7445 part of the user address space
7446 @throws randolf::rex::xEINTR Interrupted by a signal
7447 @throws randolf::rex::xEINVAL Invalid argument passed
7448 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7449 occur, but the POSIX sockets documentation lists it as one of the
7450 errors that can be returned, perhaps because some incorrectly
7451 implemented TCP/IP stacks return this error?)
7452 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7453 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7454 and 65,527 bytes for IPv6)
7455 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7456 network congestion (or, less commonly, insufficient memory)
7457 @throws randolf::rex::xENOMEM Insufficient memory
7458 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7459 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7460 doesn't refer to a socket
7461 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7462 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7463 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7465 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7467 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7468 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7469 but it really isn't)
7471 @returns The same rsocket object so as to facilitate stacking
7472 @see recv_uint16_lsb
7473 @see send_uint16_msb
7475 *///=========================================================================
7476 rsocket* send_uint16_lsb(
7478 const uint16_t value,
7485 const int posix_flags = 0) {
7486 uint16_t buf = !__endian_is_msb ? value : ntohs(value);
7487 if (__debug) debug("send_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7488 + ", " + std::to_string(value)
7489 + ", " + std::to_string(sizeof(buf))
7490 + ", " + std::to_string(posix_flags)
7492 __send(&buf, sizeof(buf), posix_flags);
7494 } // -x- rsocket* send_uint16_lsb -x-
7496 /*======================================================================*//**
7498 Send one 16-bit integer of data in MSB (big endian) order to the endpoint.
7500 This method is threadsafe.
7502 @throws randolf::rex::xEBADF The underlying socket is not open
7503 @throws randolf::rex::xECONNRESET Connect reset by peer
7504 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7505 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7506 part of the user address space
7507 @throws randolf::rex::xEINTR Interrupted by a signal
7508 @throws randolf::rex::xEINVAL Invalid argument passed
7509 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7510 occur, but the POSIX sockets documentation lists it as one of the
7511 errors that can be returned, perhaps because some incorrectly
7512 implemented TCP/IP stacks return this error?)
7513 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7514 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7515 and 65,527 bytes for IPv6)
7516 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7517 network congestion (or, less commonly, insufficient memory)
7518 @throws randolf::rex::xENOMEM Insufficient memory
7519 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7520 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7521 doesn't refer to a socket
7522 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7523 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7524 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7526 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7528 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7529 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7530 but it really isn't)
7532 @returns The same rsocket object so as to facilitate stacking
7533 @see recv_uint16_msb
7534 @see send_uint16_lsb
7536 *///=========================================================================
7537 rsocket* send_uint16_msb(
7539 const uint16_t value,
7546 const int posix_flags = 0) {
7547 int16_t buf = __endian_is_msb ? value : htons(value);
7548 if (__debug) debug("send_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7549 + ", " + std::to_string(value)
7550 + ", " + std::to_string(sizeof(buf))
7551 + ", " + std::to_string(posix_flags)
7553 __send(&buf, sizeof(buf), posix_flags);
7555 } // -x- rsocket* send_int16_msb -x-
7557 /*======================================================================*//**
7559 Send one 32-bit unsigned integer of data in LSB (little endian) order to the
7562 This method is threadsafe.
7564 @throws randolf::rex::xEBADF The underlying socket is not open
7565 @throws randolf::rex::xECONNRESET Connect reset by peer
7566 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7567 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7568 part of the user address space
7569 @throws randolf::rex::xEINTR Interrupted by a signal
7570 @throws randolf::rex::xEINVAL Invalid argument passed
7571 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7572 occur, but the POSIX sockets documentation lists it as one of the
7573 errors that can be returned, perhaps because some incorrectly
7574 implemented TCP/IP stacks return this error?)
7575 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7576 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7577 and 65,527 bytes for IPv6)
7578 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7579 network congestion (or, less commonly, insufficient memory)
7580 @throws randolf::rex::xENOMEM Insufficient memory
7581 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7582 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7583 doesn't refer to a socket
7584 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7585 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7586 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7588 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7590 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7591 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7592 but it really isn't)
7594 @returns The same rsocket object so as to facilitate stacking
7595 @see recv_uint32_lsb
7596 @see send_uint32_msb
7598 *///=========================================================================
7599 rsocket* send_uint32_lsb(
7601 const uint32_t value,
7608 const int posix_flags = 0) {
7609 uint32_t buf = !__endian_is_msb ? value : ntohl(value);
7610 if (__debug) debug("send_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7611 + ", " + std::to_string(value)
7612 + ", " + std::to_string(sizeof(buf))
7613 + ", " + std::to_string(posix_flags)
7615 __send(&buf, sizeof(buf), posix_flags);
7617 } // -x- rsocket* send_uint32_lsb -x-
7619 /*======================================================================*//**
7621 Send one 32-bit unsigned integer of data in MSB (big endian) order to the
7624 This method is threadsafe.
7626 @throws randolf::rex::xEBADF The underlying socket is not open
7627 @throws randolf::rex::xECONNRESET Connect reset by peer
7628 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7629 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7630 part of the user address space
7631 @throws randolf::rex::xEINTR Interrupted by a signal
7632 @throws randolf::rex::xEINVAL Invalid argument passed
7633 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7634 occur, but the POSIX sockets documentation lists it as one of the
7635 errors that can be returned, perhaps because some incorrectly
7636 implemented TCP/IP stacks return this error?)
7637 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7638 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7639 and 65,527 bytes for IPv6)
7640 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7641 network congestion (or, less commonly, insufficient memory)
7642 @throws randolf::rex::xENOMEM Insufficient memory
7643 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7644 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7645 doesn't refer to a socket
7646 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7647 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7648 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7650 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7652 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7653 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7654 but it really isn't)
7656 @returns The same rsocket object so as to facilitate stacking
7657 @see recv_uint32_msb
7658 @see send_uint32_lsb
7660 *///=========================================================================
7661 rsocket* send_uint32_msb(
7663 const uint32_t value,
7670 const int posix_flags = 0) {
7671 uint32_t buf = __endian_is_msb ? value : htonl(value);
7672 if (__debug) debug("send_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7673 + ", " + std::to_string(value)
7674 + ", " + std::to_string(sizeof(buf))
7675 + ", " + std::to_string(posix_flags)
7677 __send(&buf, sizeof(buf), posix_flags);
7679 } // -x- rsocket* send_uint32_msb -x-
7681 /*======================================================================*//**
7683 Send one 64-bit unsigned integer of data in LSB (little endian) order to the
7686 This method is threadsafe.
7688 @throws randolf::rex::xEBADF The underlying socket is not open
7689 @throws randolf::rex::xECONNRESET Connect reset by peer
7690 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7691 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7692 part of the user address space
7693 @throws randolf::rex::xEINTR Interrupted by a signal
7694 @throws randolf::rex::xEINVAL Invalid argument passed
7695 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7696 occur, but the POSIX sockets documentation lists it as one of the
7697 errors that can be returned, perhaps because some incorrectly
7698 implemented TCP/IP stacks return this error?)
7699 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7700 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7701 and 65,527 bytes for IPv6)
7702 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7703 network congestion (or, less commonly, insufficient memory)
7704 @throws randolf::rex::xENOMEM Insufficient memory
7705 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7706 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7707 doesn't refer to a socket
7708 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7709 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7710 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7712 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7714 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7715 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7716 but it really isn't)
7718 @returns The same rsocket object so as to facilitate stacking
7719 @see recv_uint64_lsb
7720 @see send_uint64_msb
7722 *///=========================================================================
7723 rsocket* send_uint64_lsb(
7725 const uint64_t value,
7732 const int posix_flags = 0) {
7733 uint64_t buf = !__endian_is_msb ? value : ((((uint64_t)ntohl((value) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((value) >> 32)));
7734 if (__debug) debug("send_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7735 + ", " + std::to_string(value)
7736 + ", " + std::to_string(sizeof(buf))
7737 + ", " + std::to_string(posix_flags)
7739 __send(&buf, sizeof(buf), posix_flags);
7741 } // -x- rsocket* send_uint64_lsb -x-
7743 /*======================================================================*//**
7745 Send one 64-bit unsigned integer of data in MSB (big endian) order to the
7748 This method is threadsafe.
7750 @throws randolf::rex::xEBADF The underlying socket is not open
7751 @throws randolf::rex::xECONNRESET Connect reset by peer
7752 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7753 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7754 part of the user address space
7755 @throws randolf::rex::xEINTR Interrupted by a signal
7756 @throws randolf::rex::xEINVAL Invalid argument passed
7757 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7758 occur, but the POSIX sockets documentation lists it as one of the
7759 errors that can be returned, perhaps because some incorrectly
7760 implemented TCP/IP stacks return this error?)
7761 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7762 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7763 and 65,527 bytes for IPv6)
7764 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7765 network congestion (or, less commonly, insufficient memory)
7766 @throws randolf::rex::xENOMEM Insufficient memory
7767 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7768 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7769 doesn't refer to a socket
7770 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7771 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7772 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7774 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7776 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7777 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7778 but it really isn't)
7780 @returns The same rsocket object so as to facilitate stacking
7781 @see recv_uint64_msb
7782 @see send_uint64_lsb
7784 *///=========================================================================
7785 rsocket* send_uint64_msb(
7787 const uint64_t value,
7794 const int posix_flags = 0) {
7795 uint64_t buf = __endian_is_msb ? value : ((((uint64_t)htonl((value) & 0xffffffffUL)) << 32) | htonl((uint32_t)((value) >> 32)));
7796 if (__debug) debug("send_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7797 + ", " + std::to_string(value)
7798 + ", " + std::to_string(sizeof(buf))
7799 + ", " + std::to_string(posix_flags)
7801 __send(&buf, sizeof(buf), posix_flags);
7803 } // -x- rsocket* send_uint64_msb -x-
7805 /*======================================================================*//**
7807 Send data in the form of a std::string to the endpoint, with an EoL sequence
7810 This method is threadsafe.
7812 @throws randolf::rex::xEBADF The underlying socket is not open
7813 @throws randolf::rex::xECONNRESET Connect reset by peer
7814 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7815 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7816 part of the user address space
7817 @throws randolf::rex::xEINTR Interrupted by a signal
7818 @throws randolf::rex::xEINVAL Invalid argument passed
7819 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7820 occur, but the POSIX sockets documentation lists it as one of the
7821 errors that can be returned, perhaps because some incorrectly
7822 implemented TCP/IP stacks return this error?)
7823 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7824 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7825 and 65,527 bytes for IPv6)
7826 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7827 network congestion (or, less commonly, insufficient memory)
7828 @throws randolf::rex::xENOMEM Insufficient memory
7829 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7830 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7831 doesn't refer to a socket
7832 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7833 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7834 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7836 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7838 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7839 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7840 but it really isn't)
7842 @returns The same rsocket object so as to facilitate stacking
7849 *///=========================================================================
7852 const std::string msg = std::string(),
7859 const int posix_flags = 0) {
7860 if (__debug) debug("sendline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7862 + ", " + std::to_string(msg.length())
7863 + "+" + std::to_string(__eol_out.length())
7864 + ", " + std::to_string(posix_flags)
7866 __sendline(msg.data(), msg.length(), posix_flags);
7868 } // -x- rsocket* sendline -x-
7870 /*======================================================================*//**
7872 Send data in the form of a "msghdr" structure to a specific endpoint.
7874 This method is not compatible with TLS.
7876 This method is threadsafe.
7878 @throws randolf::rex::xEBADF The underlying socket is not open
7879 @throws randolf::rex::xECONNRESET Connect reset by peer
7880 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7881 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7882 part of the user address space
7883 @throws randolf::rex::xEINTR Interrupted by a signal
7884 @throws randolf::rex::xEINVAL Invalid argument passed
7885 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7886 occur, but the POSIX sockets documentation lists it as one of the
7887 errors that can be returned, perhaps because some incorrectly
7888 implemented TCP/IP stacks return this error?)
7889 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7890 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7891 and 65,527 bytes for IPv6)
7892 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7893 network congestion (or, less commonly, insufficient memory)
7894 @throws randolf::rex::xENOMEM Insufficient memory
7895 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7896 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7897 doesn't refer to a socket
7898 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7899 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7900 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7902 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7904 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7905 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7906 but it really isn't)
7908 @returns The same rsocket object so as to facilitate stacking
7911 *///=========================================================================
7913 /// Pointer to data to send
7914 const struct msghdr* msg,
7921 const int posix_flags = 0) {
7922 if (__debug) debug("sendmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7924 + ", " + std::to_string(posix_flags)
7926 __track_bytes_tx(__rc_check(::sendmsg(__socket_fd, msg, posix_flags)));
7928 } // -x- rsocket* sendmsg -x-
7930 /*======================================================================*//**
7932 Send data in the form of a "mmsghdr" structure to a specific endpoint.
7934 This method is not compatible with TLS.
7936 This method is threadsafe.
7938 @throws randolf::rex::xEBADF The underlying socket is not open
7939 @throws randolf::rex::xECONNRESET Connect reset by peer
7940 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7941 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7942 part of the user address space
7943 @throws randolf::rex::xEINTR Interrupted by a signal
7944 @throws randolf::rex::xEINVAL Invalid argument passed
7945 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7946 occur, but the POSIX sockets documentation lists it as one of the
7947 errors that can be returned, perhaps because some incorrectly
7948 implemented TCP/IP stacks return this error?)
7949 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7950 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7951 and 65,527 bytes for IPv6)
7952 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7953 network congestion (or, less commonly, insufficient memory)
7954 @throws randolf::rex::xENOMEM Insufficient memory
7955 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7956 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7957 doesn't refer to a socket
7958 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7959 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7960 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7962 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7964 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7965 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7966 but it really isn't)
7968 @returns The same rsocket object so as to facilitate stacking
7971 *///=========================================================================
7973 /// Pointer to data to send
7974 struct mmsghdr* mmsg,
7975 /// Size of target endpoint structure
7976 const unsigned int vlen = sizeof(mmsghdr),
7983 const int posix_flags = 0) {
7984 if (__debug) debug("sendmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7986 + ", " + std::to_string(vlen)
7987 + ", " + std::to_string(posix_flags)
7989 __track_bytes_tx(__rc_check(::sendmmsg(__socket_fd, mmsg, vlen, posix_flags)));
7991 } // -x- rsocket* sendmsg -x-
7993 /*======================================================================*//**
7995 Send data in the form of a std::string to a specific endpoint.
7997 This method is not compatible with TLS.
7999 This method is threadsafe.
8001 @throws randolf::rex::xEBADF The underlying socket is not open
8002 @throws randolf::rex::xECONNRESET Connect reset by peer
8003 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8004 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8005 part of the user address space
8006 @throws randolf::rex::xEINTR Interrupted by a signal
8007 @throws randolf::rex::xEINVAL Invalid argument passed
8008 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8009 occur, but the POSIX sockets documentation lists it as one of the
8010 errors that can be returned, perhaps because some incorrectly
8011 implemented TCP/IP stacks return this error?)
8012 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8013 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8014 and 65,527 bytes for IPv6)
8015 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8016 network congestion (or, less commonly, insufficient memory)
8017 @throws randolf::rex::xENOMEM Insufficient memory
8018 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8019 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8020 doesn't refer to a socket
8021 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8022 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8023 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8025 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8027 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8028 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8029 but it really isn't)
8031 @returns The same rsocket object so as to facilitate stacking
8034 *///=========================================================================
8037 const std::string msg,
8044 const int posix_flags,
8045 /// Target endpoint address structure
8046 const struct sockaddr *to,
8047 /// Size of target endpoint structure
8048 socklen_t tolen = sizeof(sockaddr)) {
8049 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8051 + ", " + std::to_string(posix_flags)
8053 + ", " + std::to_string(tolen)
8055 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg.data(), msg.length(), posix_flags, to, tolen)));
8057 } // -x- rsocket* sendto -x- // TODO: Create easier-to-use variants
8059 /*======================================================================*//**
8061 Send data in the form of a C-string to a specific endpoint.
8063 This method is not compatible with TLS.
8065 This method is threadsafe.
8067 @throws randolf::rex::xEBADF The underlying socket is not open
8068 @throws randolf::rex::xECONNRESET Connect reset by peer
8069 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8070 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8071 part of the user address space
8072 @throws randolf::rex::xEINTR Interrupted by a signal
8073 @throws randolf::rex::xEINVAL Invalid argument passed
8074 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8075 occur, but the POSIX sockets documentation lists it as one of the
8076 errors that can be returned, perhaps because some incorrectly
8077 implemented TCP/IP stacks return this error?)
8078 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8079 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8080 and 65,527 bytes for IPv6)
8081 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8082 network congestion (or, less commonly, insufficient memory)
8083 @throws randolf::rex::xENOMEM Insufficient memory
8084 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8085 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8086 doesn't refer to a socket
8087 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8088 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8089 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8091 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8093 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8094 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8095 but it really isn't)
8097 @returns The same rsocket object so as to facilitate stacking
8100 *///=========================================================================
8102 /// Pointer to data to send
8104 /// Number of bytes to send
8112 const int posix_flags,
8113 /// Target endpoint address structure
8114 const struct sockaddr *to,
8115 /// Size of target endpoint structure
8116 socklen_t tolen = sizeof(sockaddr)) {
8117 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8119 + ", " + std::to_string(std::strlen(msg))
8120 + ", " + std::to_string(posix_flags)
8122 + ", " + std::to_string(tolen)
8124 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, len, posix_flags, to, tolen)));
8126 } // -x- rsocket* sendto -x-
8128 /*======================================================================*//**
8130 Send data in the form of an ASCIIZ string to the endpoint. The terminating
8131 NULL character won't be transmitted.
8133 This method is threadsafe.
8135 @throws randolf::rex::xEBADF The underlying socket is not open
8136 @throws randolf::rex::xECONNRESET Connect reset by peer
8137 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8138 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8139 part of the user address space
8140 @throws randolf::rex::xEINTR Interrupted by a signal
8141 @throws randolf::rex::xEINVAL Invalid argument passed
8142 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8143 occur, but the POSIX sockets documentation lists it as one of the
8144 errors that can be returned, perhaps because some incorrectly
8145 implemented TCP/IP stacks return this error?)
8146 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8147 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8148 and 65,527 bytes for IPv6)
8149 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8150 network congestion (or, less commonly, insufficient memory)
8151 @throws randolf::rex::xENOMEM Insufficient memory
8152 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8153 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8154 doesn't refer to a socket
8155 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8156 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8157 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8159 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8161 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8162 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8163 but it really isn't)
8165 @returns The same rsocket object so as to facilitate stacking
8166 @see recvz(const size_t, const int)
8167 @see send_asciiz(const char*, const int) which also transmits the terminating
8170 *///=========================================================================
8172 /// Pointer to data to send
8180 const int posix_flags = 0) {
8181 if (__debug) debug("sendz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8183 + ", " + std::to_string(std::strlen(msg))
8184 + ", " + std::to_string(posix_flags)
8186 __send(msg, std::strlen(msg), posix_flags);
8188 } // -x- rsocket* sendz -x-
8190 /*======================================================================*//**
8192 Send data in the form of an ASCIIZ string to a specific endpoint. The
8193 terminating NULL character won't be transmitted.
8195 This method is not compatible with TLS.
8197 This method is threadsafe.
8199 @throws randolf::rex::xEBADF The underlying socket is not open
8200 @throws randolf::rex::xECONNRESET Connect reset by peer
8201 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8202 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8203 part of the user address space
8204 @throws randolf::rex::xEINTR Interrupted by a signal
8205 @throws randolf::rex::xEINVAL Invalid argument passed
8206 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8207 occur, but the POSIX sockets documentation lists it as one of the
8208 errors that can be returned, perhaps because some incorrectly
8209 implemented TCP/IP stacks return this error?)
8210 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8211 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8212 and 65,527 bytes for IPv6)
8213 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8214 network congestion (or, less commonly, insufficient memory)
8215 @throws randolf::rex::xENOMEM Insufficient memory
8216 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8217 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8218 doesn't refer to a socket
8219 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8220 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8221 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8223 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8225 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8226 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8227 but it really isn't)
8232 @returns The same rsocket object so as to facilitate stacking
8233 *///=========================================================================
8235 /// Pointer to data to send
8243 const int posix_flags,
8244 /// Target endpoint address structure
8245 const struct sockaddr *to,
8246 /// Size of target endpoint structure
8247 socklen_t tolen = sizeof(sockaddr)) {
8248 if (__debug) debug("sendzto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8250 + ", " + std::to_string(std::strlen(msg))
8251 + ", " + std::to_string(posix_flags)
8253 + ", " + std::to_string(tolen)
8255 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, std::strlen(msg), posix_flags, to, tolen)));
8257 } // -x- rsocket* sendzto -x-
8259 /*======================================================================*//**
8261 Set socket option with a zero-length value, which is a special case usage
8262 that will probably never be needed, but is included here for future use where
8263 the call to @c setsockopt() would include specifying @c option_value's length
8264 in the final parameter @c option_len as zero.
8266 (Internally, we still populate the pointer for @c option_value to that of an
8267 internal `static const int` {which does have the value of 0} so as to avoid
8268 triggering an unexpected error where this parameter can't be @c nullptr).
8271 These setsockopt() methods take an integer or character value directly, or a
8272 pointer to a structure, and then rsocket handles the remaining tedious
8273 technical details behind-the-scenes for you when calling the underlying
8274 socket's setsockopt() function.
8276 @throws randolf::rex::xEBADF The underlying socket is not open
8277 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8278 part of the user address space
8279 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
8280 valid for this socket's family (a.k.a., communication domain)
8281 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
8283 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8284 doesn't refer to a socket
8286 @returns The same rsocket object so as to facilitate stacking
8290 *///=========================================================================
8291 rsocket* setsockopt(
8292 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8293 /// also be @c IPPROTO_TCP, etc.
8295 /// The name of the option, such as @c TCP_CORK, etc.
8297 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option);
8298 static const int value = 0;
8299 __rc_check(::setsockopt(__socket_fd, level, option, &value, 0));
8301 } // -x- rsocket* setsockopt -x-
8303 /*======================================================================*//**
8305 Set socket option to the specific `integer`.
8308 These setsockopt() methods take an integer or character value directly, or a
8309 pointer to a structure, and then rsocket handles the remaining tedious
8310 technical details behind-the-scenes for you when calling the underlying
8311 socket's setsockopt() function.
8313 @throws randolf::rex::xEBADF The underlying socket is not open
8314 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8315 part of the user address space
8316 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
8317 valid for this socket's family (a.k.a., communication domain)
8318 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
8320 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8321 doesn't refer to a socket
8323 @returns The same rsocket object so as to facilitate stacking
8324 @see getsockopt_int(const int, const int)
8327 *///=========================================================================
8328 rsocket* setsockopt(
8329 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8330 /// also be @c IPPROTO_TCP, etc.
8332 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8334 /// The value that this socket option will be set to
8336 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
8337 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8339 } // -x- rsocket* setsockopt -x-
8341 /*======================================================================*//**
8343 Set socket option to the specific `unsigned integer`.
8344 @copydetails setsockopt(const int, const int, const int)
8347 For any values that require a u_int, you'll need to explicitly cast this type
8348 when specifying the value directly; for example: (u_int)32768
8350 @throws randolf::rex::xEBADF The underlying socket is not open
8351 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8352 part of the user address space
8353 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
8354 valid for this socket's family (a.k.a., communication domain)
8355 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
8357 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8358 doesn't refer to a socket
8360 @returns The same rsocket object so as to facilitate stacking
8361 @see getsockopt_u_int(const int, const int)
8363 *///=========================================================================
8364 rsocket* setsockopt(
8365 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8366 /// also be @c IPPROTO_TCP, etc.
8368 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8370 /// The value that this socket option will be set to
8371 const u_int value) {
8372 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
8373 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8375 } // -x- rsocket* setsockopt -x-
8377 /*======================================================================*//**
8379 Set socket option to the specific `unsigned character`.
8380 @copydetails setsockopt(const int, const int, const int)
8381 @see getsockopt_u_char(const int, const int)
8383 *///=========================================================================
8384 rsocket* setsockopt(
8385 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8386 /// also be @c IPPROTO_TCP, etc.
8388 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8390 /// The value that this socket option will be set to
8391 const u_char value) {
8392 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
8393 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8395 } // -x- rsocket* setsockopt -x-
8397 /*======================================================================*//**
8399 Set socket option to the specific `linger` structure.
8400 @copydetails setsockopt(const int, const int, const int)
8401 @see getsockopt_linger(const int, const int)
8403 *///=========================================================================
8404 rsocket* setsockopt(
8405 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8406 /// also be @c IPPROTO_TCP, etc.
8408 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8410 /// The structure that this socket option will be set to
8411 const linger& value) {
8412 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8413 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8415 } // -x- rsocket* setsockopt -x-
8417 /*======================================================================*//**
8419 Set socket option to the specific `timeval` structure.
8420 @copydetails setsockopt(const int, const int, const linger&)
8421 @see getsockopt_timeval(const int, const int)
8423 *///=========================================================================
8424 rsocket* setsockopt(
8425 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8426 /// also be @c IPPROTO_TCP, etc.
8428 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8430 /// The structure that this socket option will be set to
8431 const timeval& value) {
8432 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8433 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8435 } // -x- rsocket* setsockopt -x-
8437 /*======================================================================*//**
8439 Set socket option to the specific `in_addr` structure.
8440 @copydetails setsockopt(const int, const int, const linger&)
8441 @see getsockopt_in_addr(const int, const int)
8443 *///=========================================================================
8444 rsocket* setsockopt(
8445 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8446 /// also be @c IPPROTO_TCP, etc.
8448 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8450 /// The structure that this socket option will be set to
8451 const in_addr& value) {
8452 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8453 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8455 } // -x- rsocket* setsockopt -x-
8457 /*======================================================================*//**
8459 Set socket option to the specific `ip_mreq` structure.
8460 @copydetails setsockopt(const int, const int, const linger&)
8461 @see getsockopt_ip_mreq(const int, const int)
8463 *///=========================================================================
8464 rsocket* setsockopt(
8465 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8466 /// also be @c IPPROTO_TCP, etc.
8468 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8470 /// The structure that this socket option will be set to
8471 const ip_mreq& value) {
8472 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8473 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8475 } // -x- rsocket* setsockopt -x-
8477 /*======================================================================*//**
8479 Set socket option to the specific `ip_mreq_source` structure.
8480 @copydetails setsockopt(const int, const int, const linger&)
8481 @see getsockopt_ip_mreq_source(const int, const int)
8483 *///=========================================================================
8484 rsocket* setsockopt(
8485 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8486 /// also be @c IPPROTO_TCP, etc.
8488 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8490 /// The structure that this socket option will be set to
8491 const ip_mreq_source& value) {
8492 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8493 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8495 } // -x- rsocket* setsockopt -x-
8497 /*======================================================================*//**
8499 Set socket option to the specific `icmp6_filter` structure.
8500 @copydetails setsockopt(const int, const int, const linger&)
8501 @see getsockopt_icmp6_filter(const int, const int)
8503 *///=========================================================================
8504 rsocket* setsockopt(
8505 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8506 /// also be @c IPPROTO_TCP, etc.
8508 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8510 /// The structure that this socket option will be set to
8511 const icmp6_filter& value) {
8512 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8513 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8515 } // -x- rsocket* setsockopt -x-
8517 /*======================================================================*//**
8519 Set socket option to the specific `sockaddr_in6` structure.
8520 @copydetails setsockopt(const int, const int, const linger&)
8521 @see getsockopt_sockaddr_in6(const int, const int)
8523 *///=========================================================================
8524 rsocket* setsockopt(
8525 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8526 /// also be @c IPPROTO_TCP, etc.
8528 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8530 /// The structure that this socket option will be set to
8531 const sockaddr_in6& value) {
8532 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8533 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8535 } // -x- rsocket* setsockopt -x-
8537 /*======================================================================*//**
8539 Set socket option to the specific `ip6_mtuinfo` structure.
8540 @copydetails setsockopt(const int, const int, const linger&)
8541 @see getsockopt_ip6_mtuinfo(const int, const int)
8543 *///=========================================================================
8544 rsocket* setsockopt(
8545 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8546 /// also be @c IPPROTO_TCP, etc.
8548 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8550 /// The structure that this socket option will be set to
8551 const ip6_mtuinfo& value) {
8552 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8553 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8555 } // -x- rsocket* setsockopt -x-
8557 /*======================================================================*//**
8559 Set socket option to the specific `ipv6_mreq` structure.
8560 @copydetails setsockopt(const int, const int, const linger&)
8561 @see getsockopt_ipv6_mreq(const int, const int)
8563 *///=========================================================================
8564 rsocket* setsockopt(
8565 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8566 /// also be @c IPPROTO_TCP, etc.
8568 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8570 /// The structure that this socket option will be set to
8571 const ipv6_mreq& value) {
8572 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8573 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8575 } // -x- rsocket* setsockopt -x-
8577 /*======================================================================*//**
8579 Set socket option to the specific `group_req` structure.
8580 @copydetails setsockopt(const int, const int, const linger&)
8581 @see getsockopt_group_req(const int, const int)
8583 *///=========================================================================
8584 rsocket* setsockopt(
8585 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8586 /// also be @c IPPROTO_TCP, etc.
8588 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8590 /// The structure that this socket option will be set to
8591 const group_req& value) {
8592 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8593 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8595 } // -x- rsocket* setsockopt -x-
8597 /*======================================================================*//**
8599 Set socket option to the specific `group_source_req` structure.
8600 @copydetails setsockopt(const int, const int, const linger&)
8601 @see getsockopt_group_source_req(const int, const int)
8603 *///=========================================================================
8604 rsocket* setsockopt(
8605 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8606 /// also be @c IPPROTO_TCP, etc.
8608 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8610 /// The structure that this socket option will be set to
8611 const group_source_req& value) {
8612 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8613 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8615 } // -x- rsocket* setsockopt -x-
8617 /*======================================================================*//**
8619 Shut down the underlying socket, partially or fully.
8621 <div style=padding-left:32px;>
8624 <td valign=top>SHUT_RD:</td>
8625 <td>Further receives will be disallowed.</td>
8628 <td valign=top>SHUT_WR:</td>
8629 <td>Further sends will be disallowed (this may cause actions specific
8630 to the protocol family of the socket to occur).</td>
8633 <td valign=top>SHUT_RDWR:</td>
8634 <td>Further sends and receives will be disallowed (default).</td>
8639 @throws randolf::rex::xEBADF The underlying socket is not open
8640 @throws randolf::rex::xEINVAL Invalid argument passed
8641 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8642 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8643 doesn't refer to a socket
8645 @returns The same rsocket object so as to facilitate stacking
8651 *///=========================================================================
8655 /// SHUT_RDWR (default)
8656 const int how = SHUT_RDWR,
8657 /// Shutdown the TLS connection too (but only if it's alrady enabled), which
8658 /// is the default (disabling this will usually result in errors for the
8659 /// endpoint upon raw socket shutdown)
8660 const bool tls_shutdown = true) {
8661 if (__debug) debug("shutdown(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8662 + ", " + std::to_string(how)
8665 // --------------------------------------------------------------------------
8666 // Shut down TLS connections first, if TLS is enabled.
8668 // If SSL_shutdown returns a 0, then it means a "bidirectional shutdown" is
8669 // needed which entails calling SSL_shutdown a second time. The error codes
8670 // returned by SSL_shutdown are as follows:
8672 // 1 = Shutdown completed normally
8674 // 0 = Shutdown is not yet finished, and a second SSL_shutdown() is needed
8676 // -1 = Critical error, probably due to a connection problem, or because
8677 // the endpoint already initiated a shutdown, etc.
8679 // We don't really care what the second SSL_shutdown's return code is because
8680 // there's nothing we can do about it whatever it is. This situation is rare
8681 // though, and what follows next closes the raw socket, which is OpenSSL's
8682 // lifeline to communicating with the endpoint.
8683 // --------------------------------------------------------------------------
8684 if (__tls && tls_shutdown) {
8685 if (SSL_shutdown(__tls_fd) == 0) {
8686 SSL_shutdown(__tls_fd);
8687 } // -x- if SSL_shutdown -x-
8688 } // -x- if __tls -x-
8690 // --------------------------------------------------------------------------
8691 // Shut down the raw socket.
8692 // --------------------------------------------------------------------------
8693 __rc_check(::shutdown(__socket_fd, how));
8694 //__socket_connected = false; // TODO: Figure out when to change this to false
8696 } // -x- rsocket* shutdown -x-
8698 /*======================================================================*//**
8700 Complete the configuration of an rsocket that was previously initialized
8701 without any parameters (a.k.a., an "empty rsocket").
8702 @copydetails rsocket()
8703 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
8704 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
8705 @throws randolf::rex::xEALREADY If this socket() method was already used, or
8706 it was used after rsocket() initialized with at least one parameter
8707 @throws randolf::rex::xEINVAL Protocal family invalid or not available
8708 @throws randolf::rex::xEINVAL Invalid flags in type
8709 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
8710 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
8711 @throws randolf::rex::xENOBUFS Insufficient memory
8712 @throws randolf::rex::xENOMEM Insufficient memory
8713 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
8714 supported within the specified family (a.k.a., communication domain)
8715 @returns The same rsocket object so as to facilitate stacking
8718 @see socket_family()
8720 @see socket_protocol()
8724 *///=========================================================================
8726 /// Communication domain; usually one of:@n
8727 /// AF_INET (IPv4)@n
8728 /// AF_INET6 (IPv6)@n
8729 /// AF_UNIX (UNIX domain sockets)
8731 /// Communication semantics; usually one of:@n
8732 /// SOCK_STREAM (common for TCP)@n
8733 /// SOCK_DGRAM (common for UDP)
8734 const int type = SOCK_STREAM,
8735 /// Network protocol; usually one of:@n
8739 /// PF_UNSPEC (auto-detect)
8740 const int protocol = PF_UNSPEC) {
8741 __socket(family, type, protocol);
8743 } // -x- rsocket* socket -x-
8745 /*======================================================================*//**
8747 Get underlying socket family/domain constant (SO_DOMAIN).
8748 @returns socket family/domain constant
8752 @see socket_protocol()
8755 *///=========================================================================
8756 const int socket_family() noexcept { return __socket_addr->ss_family; } // -x- int socket_family -x-
8758 /*======================================================================*//**
8760 Get underlying socket descriptor/handle.
8761 @returns socket descriptor/handle
8762 @returns 0 = socket not yet allocated
8765 @see socket_family()
8766 @see socket_protocol()
8769 *///=========================================================================
8770 const int socket_fd() noexcept { return __socket_fd; } // -x- int socket_fd -x-
8772 /*======================================================================*//**
8774 Set underlying socket descriptor/handle (to one that is presumed to be open).
8776 This method is only available while an underlying socket has not been created
8777 or previously assigned, such as after an empty @ref rsocket instantiation.
8778 @throws randolf::rex::xEALREADY If this socket_fd() method was already used,
8779 or it was used after socket() initialized it, or if rsocket() had
8780 initialized with at least one parameter that resulted in the creation
8781 of an underlying socket
8782 @returns The same rsocket object so as to facilitate stacking
8784 @see socket_family()
8785 @see socket_protocol()
8788 *///=========================================================================
8790 /// New socket descriptor/handle
8791 const int new_socket_fd) {
8792 if (__debug) debug("socket_fd(socket{0x" + randolf::rtools::to_hex(new_socket_fd) + "}"
8794 if (__socket_fd != 0) throw randolf::rex::xEALREADY("EALREADY");
8795 __socket_fd = new_socket_fd;
8796 __socket_addr->ss_family = getsockopt_int(SOL_SOCKET, SO_DOMAIN);
8797 __socket_type = getsockopt_int(SOL_SOCKET, SO_TYPE);
8798 __socket_protocol = getsockopt_int(SOL_SOCKET, SO_PROTOCOL);
8799 __socket_open = true;
8801 } // -x- rsocket* socket_fd -x-
8803 /*======================================================================*//**
8805 Get underlying socket protocol constant (SO_PROTOCOL).
8806 @returns socket protocol constant
8809 @see socket_family()
8813 *///=========================================================================
8814 const int socket_protocol() noexcept {
8815 return __socket_protocol;
8816 } // -x- int socket_protocol -x-
8818 /*======================================================================*//**
8820 Get underlying socket type constant (SO_TYPE).
8821 @returns socket type constant
8824 @see socket_family()
8826 @see socket_protocol()
8828 *///=========================================================================
8829 const int socket_type() noexcept {
8830 return __socket_type;
8831 } // -x- int socket_type -x-
8833 /*======================================================================*//**
8835 Find out whether the underlying socket is at the out-of-band (OOB) mark.
8837 @throws randolf::rex::xEBADF The underlying socket is not open
8838 @throws randolf::rex::xEINVAL The underlying socket file descriptor is not a
8839 type to which @ref sockatmark() can be applied
8841 @returns TRUE = at OOB mark
8842 @returns FALSE = not at OOB mark
8845 *///=========================================================================
8846 const bool sockatmark() {
8847 return __rc_check(::sockatmark(__socket_fd)) ? true : false;
8848 } // -x- bool sockatmark -x-
8850 /*======================================================================*//**
8852 Obtain the receive (read) or send (write/transmit) timeout is set to on the
8855 Since getting the read timeout is such a common operation, this specialized
8856 method was created to ease software development efforts; internally we're
8857 just calling getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO).
8859 @throws randolf::rex::xEBADF The underlying socket is not open
8860 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8861 part of the user address space
8862 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
8863 valid for this socket's family (a.k.a., communication domain)
8864 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
8866 @throws randolf::rex::xENOTDIR If the @c direction parameter is supplied with
8867 an incorrect value (not @c SO_RCVTIMEO or @c SO_SNDTIMEO); this
8868 exception is normally a file-system related error, so we're using it
8869 here instead of EINVAL to make detecting this problem simpler for
8870 software developers (plus, "DIR" relates well to "direction"); this
8871 exception shouldn't need to be caught in the vast majority of uses,
8872 although one use where it should be caught is if the end-user has
8873 free reign to set the @c direction parameter to any value (e.g., a
8874 customizable software debugging interface)
8875 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8876 doesn't refer to a socket
8878 @returns @c timeval socket option structure wrapped in std::shared_ptr
8880 *///=========================================================================
8881 std::shared_ptr<timeval> timeout(
8883 /// @c SO_RCVTIMEO (default)@n
8885 const int direction = SO_RCVTIMEO) {
8887 // --------------------------------------------------------------------------
8888 // Get timeout for the specified direction.
8889 // --------------------------------------------------------------------------
8890 if (direction == SO_RCVTIMEO || direction == SO_SNDTIMEO) {
8891 return getsockopt_timeval(SOL_SOCKET, direction);
8892 } // -x- if direction -x-
8894 // --------------------------------------------------------------------------
8895 // Throw exception because direction is invalid.
8896 // --------------------------------------------------------------------------
8897 throw randolf::rex::xENOTDIR("invalid timeout direction (must be SO_RCVTIMEO or SO_SNDTIMEO)");
8899 } // -x- std::shared_ptr<timeval> timeout -x-
8901 /*======================================================================*//**
8903 Configure the receive (read) or send (write/transmit) timeout on the
8906 Since setting the read timeout is such a common operation, this specialized
8907 method was created to ease software development efforts; internally we're
8908 just calling setsockopt(SOL_SOCKET, SO_RCVTIMEO, timeval).
8910 Although a timeout of 100,000 microseconds (1/10 of one second) may suffice
8911 in healthy and efficient networks, a more conservative setting of 1 second
8912 tends to minimally yield more reliable results. Many end-user applications
8913 use defaults of 3 to 5 seconds to cover a wider variety of unpredictable
8914 network connections (such as over shared wireless connections that are slow),
8915 and this setting should ultimately be configurable by users/administrators.
8918 The default timeout for new sockets is normally 0 (no timeout).
8920 @throws randolf::rex::xEBADF The underlying socket is not open
8921 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8922 part of the user address space
8923 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
8924 valid for this socket's family (a.k.a., communication domain)
8925 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
8927 @throws randolf::rex::xENOTDIR If the @c direction parameter is supplied with
8928 an incorrect value (not @c SO_RCVTIMEO or @c SO_SNDTIMEO); this
8929 exception is normally a file-system related error, so we're using it
8930 here instead of EINVAL to make detecting this problem simpler for
8931 software developers (plus, "DIR" relates well to "direction"); this
8932 exception shouldn't need to be caught in the vast majority of uses,
8933 although one use where it should be caught is if the end-user has
8934 free reign to set the @c direction parameter to any value (e.g., a
8935 customizable software debugging interface)
8936 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8937 doesn't refer to a socket
8939 @returns The same rsocket object so as to facilitate stacking
8940 @see timeout_recvline
8942 *///=========================================================================
8944 /// timeval structure
8945 const struct timeval tv,
8947 /// @c SO_RCVTIMEO (default)@n
8949 const int direction = SO_RCVTIMEO) {
8951 // --------------------------------------------------------------------------
8952 // Set timeout for the specified direction.
8953 // --------------------------------------------------------------------------
8954 if (direction == SO_RCVTIMEO || direction == SO_SNDTIMEO) {
8955 return setsockopt(SOL_SOCKET, direction, tv);
8956 } // -x- if direction -x-
8958 // --------------------------------------------------------------------------
8959 // Throw exception because direction is invalid.
8960 // --------------------------------------------------------------------------
8961 throw randolf::rex::xENOTDIR("invalid timeout direction (must be SO_RCVTIMEO or SO_SNDTIMEO)");
8963 } // -x- rsocket* timeout -x-
8965 /*======================================================================*//**
8966 @copydoc timeout(const struct timeval, const int)
8967 @see timeout_recvline
8969 *///=========================================================================
8971 /// Timeout in seconds
8973 /// Timeout in microseconds
8974 const long tv_usec = 0,
8976 /// @c SO_RCVTIMEO (default)@n
8978 const int direction = SO_RCVTIMEO) {
8980 // --------------------------------------------------------------------------
8981 // Set timeout for the specified direction.
8982 // --------------------------------------------------------------------------
8983 if (direction == SO_RCVTIMEO || direction == SO_SNDTIMEO) {
8984 struct timeval tv{tv_sec, tv_usec};
8985 return setsockopt(SOL_SOCKET, direction, tv);
8986 } // -x- if direction -x-
8988 // --------------------------------------------------------------------------
8989 // Throw exception because direction is invalid.
8990 // --------------------------------------------------------------------------
8991 throw randolf::rex::xENOTDIR("invalid timeout direction (must be SO_RCVTIMEO or SO_SNDTIMEO)");
8993 } // -x- rsocket* timeout -x-
8995 /*======================================================================*//**
8997 Find out what the read timeout is set to when using the @ref recvline()
9000 @returns @c long value (0 = no timeout)
9003 @see timeout_recvline(long)
9005 *///=========================================================================
9006 long timeout_recvline() {
9007 return __recvline_timeout;
9008 } // -x- long timeout_recvline -x-
9010 /*======================================================================*//**
9012 Set the read timeout for the @ref recvline() method (the @ref recvline()
9013 method's @c timeout parameter can override this setting).
9016 The default timeout for this recvline_timeout setting is 0 (no timeout).
9018 @throws randolf::rex::xERANGE if the timeout parameter is below 0
9020 @returns The same rsocket object so as to facilitate stacking
9023 @see timeout_recvline
9025 *///=========================================================================
9026 rsocket* timeout_recvline(
9027 /// timeval structure
9028 const long timeout) {
9029 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
9030 __recvline_timeout = timeout;
9032 } // -x- rsocket* timeout_recvline -x-
9034 /*======================================================================*//**
9036 Enable or disable encrypted communications (from the OpenSSL library).
9039 TLS cannot be enabled before a non-encrypted rsocket is open (an rsocket is
9040 typically opened with the @ref socket() method, the @ref connect() method, or
9041 one of the @ref accept() methods). If calling @c tls(true) on an rsocket
9042 that isn't open, an exception will be thrown.
9044 If needed, a new TLS context will be instantiated and TLS will be initialized
9045 (if this hasn't already been done). TLS instantiation can be done first by
9046 calling the @ref tls_ctx() method (regardless of whether encryption is being
9047 enabled or disabled). If the default @ref TLS_FLAGS aren't sufficient for
9048 the needs of your application, then the @ref tls_ctx() method facilitates
9049 this regardless of wehther rsocket is open.
9052 The reason a TLS context is instantiated and TLS is initialized even when the
9053 status is being set to FALSE is to facilitate TLS ingress from an unencrypted
9054 connection later in the session (see the @ref TLS_NO_INGRESS flag for more
9058 If a client attempts to upgrade a TLS connection to TLS (e.g., by using a
9059 command such as @c STARTTLS, which is commonly transmitted in unencrypted
9060 form, like @c telnet-ssl does -- using `telnet-ssl -z ssl` prevents this
9061 condition), the following error that's difficult to track down may be
9062 triggered when calling any of the @c recv methods (I hope that including this
9063 information here in this documentation will be helpful):
9065 SSL_ERROR_UNKNOWN: error:0A00010B:SSL routines::wrong version number
9067 This is most likely not a programming error, but rather a problem with how
9068 users may attempt to mis-use a connection based on a misunderstanding of the
9069 communications requirements (e.g., connecting unencrypted and attempting to
9070 upgrade to TLS over a connection that's expecting TLS encrypted data from the
9071 very beginning, without involving any ingress).
9073 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9074 OpenSSL library doesn't document which errors may be returned)
9076 @returns The same rsocket object so as to facilitate stacking
9080 *///=========================================================================
9082 /// TRUE = Enable encrypted communications@n FALSE = Disable encrypted communications
9083 const bool status = true,
9084 /// Configuration parameters
9085 const int flags = TLS_FLAGS::TLS_DEFAULT) { // | TLS_FLAGS::TLS_CLIENT
9086 if (__debug) debug("tls(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9087 + ", " + (status ? "true" : "false")
9090 // --------------------------------------------------------------------------
9091 // Create default context (with "flags" passthrough), unless it was already
9092 // created (usually by one of the tls_ctx() methods).
9093 // --------------------------------------------------------------------------
9094 if (__tls_ctx == nullptr) tls_ctx((SSL_CTX*)nullptr, flags);
9096 // --------------------------------------------------------------------------
9097 // Make sure tls_fd (ssl_fd) is allocated. If not, then it needs to be
9098 // allocated and configured.
9099 // --------------------------------------------------------------------------
9100 if (status == true && __tls_fd == nullptr) {
9101 __tls_fd = SSL_new(__tls_ctx);
9102 if (__debug) debug("SSL_set_fd(<tls_fd>, socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9105 // --------------------------------------------------------------------------
9106 // Associate OpenSSL file descriptor with underlying socket file descriptor.
9107 // --------------------------------------------------------------------------
9108 __rc_check_tls(SSL_set_fd(__tls_fd, __socket_fd));
9110 // --------------------------------------------------------------------------
9111 // Enable read-ahead so that SSL_peek will work more efficiently.
9112 // --------------------------------------------------------------------------
9113 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
9114 SSL_set_read_ahead(__tls_fd, 1); // 0=disable / 1=enable
9116// SSL_CTX_set_max_pipelines(__tls_ctx, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
9117// SSL_set_max_pipelines(__tls_fd, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
9119// SSL_CTX_set_mode(__tls_ctx, SSL_MODE_AUTO_RETRY);
9120// SSL_set_mode(__tls_fd, SSL_MODE_AUTO_RETRY);
9122 // --------------------------------------------------------------------------
9123 // We're probably not going to use BIO because we don't need it, and it adds
9124 // unnecessary overhead for what we're doing. Also, BIO doesn't provide an
9125 // alternative to the SSL_peek function, and also doesn't resolve the need
9126 // for the MSG_WAITALL flag that the ::recv() function supports.
9128 // TODO: Remove this completely, unless is can solve the problem of reading
9129 // all incoming data (needed for readline, primarily)
9130 // --------------------------------------------------------------------------
9131 //__tls_rbio = SSL_get_rbio(__tls_fd);
9132 //__tls_wbio = SSL_get_wbio(__tls_fd);
9134 } // -x- if !__tls_fd -x-
9137 } // -x- rsocket* tls -x-
9139 /*======================================================================*//**
9141 Return the current TLS context (multiple TLS contexts are supported, although
9142 typically needed to support SNI with inbound connections).
9143 @returns Pointer to OpenSSL's context (normally labelled @c SSL_CTX* in the
9144 documentation for OpenSSL), or nullptr if this context was never
9145 assigned to (or created by) this rsocket
9148 *///=========================================================================
9149 SSL_CTX* tls_ctx() noexcept {
9151 } // -x- SSL_CTX* tls_ctx -x-
9153 /*======================================================================*//**
9155 Copy the source rsocket's TLS context map and add it to this rsocket's
9156 collection; or, if the source doesn't have any TLS contexts and this rsocket
9157 doesn't have any TLS contexts in its collection, then initialize TLS and
9158 instantiate a new TLS context. In either scenario, the source rsocket will
9159 be treated as a template as all TLS flags duplicated to enable encrypted
9160 socket I/O for use in this rsocket().
9163 At least one TLS context is needed to enable encrypted socket I/O for use in
9167 Encrypted socket I/O is only possible after a TLS context has been
9168 initialized (this is not a global setting as it has per-rsocket specificity).
9171 The only @ref TLS_FLAGS flag that doesn't get transferred is @ref TLS_SERVER
9172 when no flags are specified. Specifying any flag(s) will cause this method
9173 to ignore the source rsocket's TLS flags so as to defer to this override.
9175 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9176 OpenSSL library doesn't document which errors may be returned)
9178 @returns The same rsocket object so as to facilitate stacking
9181 *///=========================================================================
9183 /// OpenSSL's TLS context to use (if not provided, a new context will be
9184 /// created automatically using OpenSSL's defaults)
9186 /// Configuration parameters
9187 const int flags = TLS_FLAGS::TLS_DEFAULT) {
9188 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9190 + ", " + std::to_string(flags)
9193 // --------------------------------------------------------------------------
9194 // TLS-related variables (OpenSSL).
9196 // Note: The __tls_fd variable cannot be allocated until after __socket_fd
9197 // has been, hence the "post" note in the documentation.
9198 // --------------------------------------------------------------------------
9199 __tls = (bool)rtemplate->__tls; // Preserve TLS mode setting
9200 if (rtemplate->__tls_ctx != nullptr) { // If TLS context is defined, then do these important things:
9201 __tls_ctx = rtemplate->__tls_ctx; // 1. copy the pointer to SSL_CTX
9202 SSL_CTX_up_ref(__tls_ctx); // 2. increment SSL_CTX's internal reference count
9203 } // -x- if __tlx_ctx -x-
9205 // --------------------------------------------------------------------------
9206 // Copy or override TLS flags.
9207 // --------------------------------------------------------------------------
9208 if (flags == TLS_FLAGS::TLS_DEFAULT) {
9209 __tls_exclusive = rtemplate->__tls_exclusive; // TLS policy
9210 __tls_egress = rtemplate->__tls_egress; // TLS policy
9211 __tls_ingress = rtemplate->__tls_ingress; // TLS policy
9212 } else { // Save flags
9213 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
9214 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
9215 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
9216 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
9217 } // -x- if flags -x-
9220 } // -x- rsocket* tls_ctx -x-
9222 /*======================================================================*//**
9224 Initialize TLS and instantiate a TLS context, and add it to this rsocket's
9225 current collection of TLS contexts, and set it as the currently active TLS
9226 context (so that a certificate chain and private key may be added to it).
9228 At least one TLS context is needed to enable encrypted socket I/O for use in
9231 Encrypted socket I/O is only possible after a TLS context has been
9232 initialized (this is not a global setting as it has per-rsocket specificity).
9234 This is the default TLS context for this @c rsocket, which will also be used
9235 for non-SNI handshakes.
9237 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9238 OpenSSL library doesn't document which errors may be returned)
9240 @returns The same rsocket object so as to facilitate stacking
9243 *///=========================================================================
9245 /// OpenSSL's TLS context to use (if not provided, a new context will be
9246 /// created using OpenSSL's defaults)
9248 /// Configuration parameters
9249 const int flags = TLS_FLAGS::TLS_DEFAULT) {
9250 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9252 + ", " + std::to_string(flags)
9255 // --------------------------------------------------------------------------
9256 // Ignore repeated calls to this method.
9257 // --------------------------------------------------------------------------
9258 if (__tls_ctx != nullptr) return this;
9260 // --------------------------------------------------------------------------
9261 // Fire up OpenSSL's algorithms and pre-load its error strings.
9263 // These two functions have been deprecated since OpenSSL v1.1.0, so we don't
9264 // need to call them. If someone needs them, then they can always call them
9265 // in their own code anyway.
9266 // --------------------------------------------------------------------------
9267 //OpenSSL_add_all_algorithms(); // Load and register cryptography algorithms
9268 //SSL_load_error_strings(); // Load all error messages into memory
9270 // --------------------------------------------------------------------------
9271 // Save (ctx != nullptr) or create (ctx == nullptr) OpenSSL context.
9272 // --------------------------------------------------------------------------
9273 __tls_ctx = ctx == nullptr ? SSL_CTX_new(flags & TLS_FLAGS::TLS_SERVER ? TLS_server_method() : TLS_client_method()) // Create anew (default)
9274 : ctx; // Use OpenSSL context that was provided
9275 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);
9277 // --------------------------------------------------------------------------
9278 // Enable read-ahead so that SSL_peek will work properly.
9279 // --------------------------------------------------------------------------
9280 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
9282 // --------------------------------------------------------------------------
9284 // --------------------------------------------------------------------------
9285 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
9286 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
9287 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
9288 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
9291 } // -x- rsocket* tls_ctx -x-
9293 /*======================================================================*//**
9295 Check the private key it to ensure it's consistent with the corresponding TLS
9298 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9299 OpenSSL library doesn't document which errors may be returned)
9301 @returns The same rsocket object so as to facilitate stacking
9302 @see tls_ctx_use_privatekey_file
9303 @see tls_ctx_use_privatekey_pem
9305 *///=========================================================================
9306 rsocket* tls_ctx_check_privatekey() {
9307 if (__debug) debug("tls_ctx_check_privatekey();");
9308 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);
9310 } // -x- rsocket* tls_ctx_check_privatekey -x-
9312 /*======================================================================*//**
9314 Load a TLS certificate chain and private key in PEM format from text files
9315 and use them in the TLS context.
9317 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9318 OpenSSL library doesn't document which errors may be returned)
9320 @returns The same rsocket object so as to facilitate stacking
9321 @see tls_ctx_use_certificate_chain_and_privatekey_pems
9322 @see tls_ctx_use_certificate_chain_file
9323 @see tls_ctx_use_certificate_chain_pem
9324 @see tls_ctx_use_privatekey_file
9325 @see tls_ctx_use_privatekey_pem
9326 @see tls_ctx_check_privatekey
9328 *///=========================================================================
9329 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
9330 /// Pointer to ASCIIZ path and filename to certificate chain file (@c nullptr
9331 /// will simply be ignored)
9332 const char* chain_file,
9333 /// Pointer to ASCIIZ path and filename to private key file (@c nullptr will
9334 /// simply be ignored)
9335 const char* key_file) {
9336 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
9337 + std::string(chain_file)
9338 + ", " + std::string( key_file)
9340 if (chain_file != nullptr) tls_ctx_use_certificate_chain_file(chain_file);
9341 if ( key_file != nullptr) tls_ctx_use_privatekey_file( key_file);
9343 } // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
9345 /*======================================================================*//**
9347 Load a TLS certificate chain and private key in PEM format from text files
9348 and use them in the TLS context.
9350 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9351 OpenSSL library doesn't document which errors may be returned)
9353 @returns The same rsocket object so as to facilitate stacking
9354 @see tls_ctx_use_certificate_chain_and_privatekey_pems
9355 @see tls_ctx_use_certificate_chain_file
9356 @see tls_ctx_use_certificate_chain_pem
9357 @see tls_ctx_use_privatekey_file
9358 @see tls_ctx_use_privatekey_pem
9359 @see tls_ctx_check_privatekey
9361 *///=========================================================================
9362 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
9363 /// Pointer to ASCIIZ path and filename to certificate chain file (an empty
9364 /// string will simply be ignored)
9365 const std::string chain_file,
9366 /// Pointer to ASCIIZ path and filename to private key file (an empty string
9367 /// will simply be ignored)
9368 const std::string key_file) {
9369 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
9373 if (!chain_file.empty()) tls_ctx_use_certificate_chain_file(chain_file);
9374 if ( !key_file.empty()) tls_ctx_use_privatekey_file( key_file);
9376 } // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
9378 /*======================================================================*//**
9380 Load a TLS certificate chain and a TLS private key in PEM format from memory
9381 and use them in the TLS context.
9383 Although this functionality doesn't exist in OpenSSL (at the time of writing
9384 this method), it's provided here in a manner that has exactly the same effect
9385 as the @ref tls_ctx_use_certificate_chain_and_privatekey_files() methods, but
9386 without needing the PEM-formatted certificate chain stored in files
9390 The @c cert_pem_data and key_pem_data parameters are pointers to the memory
9391 locations that holds the PEM formatted certificate chain data and private key
9392 data, respectively. If the corresponding lengths of each of these data aren't
9393 specified or are set to zero (default), then they will be treated as multiline
9396 Behind the scenes, we're just writing the cert_pem_data and key_pem_data
9397 memory to temporary files with severely-limited permissions (), then
9398 optionally overwriting those temporary files with random data prior to
9399 deleting them (this is the default, since better security practices should be
9400 the default, but on a secured system it may not be necessary and so this
9401 option can also be disabled to save CPU cycles and reduce overall disk-write
9404 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9405 OpenSSL library doesn't document which errors may be returned)
9407 @returns The same rsocket object so as to facilitate stacking
9408 @see tls_ctx_use_certificate_chain_and_privatekey_files
9409 @see tls_ctx_use_certificate_chain_file
9410 @see tls_ctx_use_certificate_chain_pem
9411 @see tls_ctx_use_privatekey_file
9412 @see tls_ctx_use_privatekey_pem
9413 @see tls_ctx_check_privatekey
9415 *///=========================================================================
9416 rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems(
9417 /// Pointer to certificate chain data in PEM format
9418 const char* cert_pem_data,
9419 /// Pointer to private key data in PEM format
9420 const char* key_pem_data,
9421 /// Length of cert_pem_data (in bytes), or 0 to auto-detect length if cert_pem_data is an ASCIIZ string
9422 size_t cert_len = 0,
9423 /// Length of key_pem_data (in bytes), or 0 to auto-detect length if key_pem_data is an ASCIIZ string
9425 /// Whether to overwrite the temporary files with random data before deleting them
9426 const bool random_fill = true) {
9427 tls_ctx_use_certificate_chain_pem(cert_pem_data, cert_len, random_fill);
9428 tls_ctx_use_privatekey_pem( key_pem_data, key_len, random_fill);
9430 } // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems -x-
9432 /*======================================================================*//**
9434 Load a TLS certificate chain in PEM format from a text file and use it in the
9437 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9438 OpenSSL library doesn't document which errors may be returned)
9440 @returns The same rsocket object so as to facilitate stacking
9441 @see tls_ctx_use_certificate_chain_file
9442 @see tls_ctx_use_certificate_chain_pem
9443 @see tls_ctx_check_privatekey
9445 *///=========================================================================
9446 rsocket* tls_ctx_use_certificate_chain_file(
9447 /// Pointer to ASCIIZ path and filename to certificate chain file
9449 if (__debug) debug("tls_ctx_use_certificate_chain_file("
9452 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);
9454 } // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
9456 /*======================================================================*//**
9458 Load a TLS certificate chain in PEM format from a text file and use it in the
9461 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9462 OpenSSL library doesn't document which errors may be returned)
9464 @returns The same rsocket object so as to facilitate stacking
9465 @see tls_ctx_use_certificate_chain_file
9466 @see tls_ctx_use_certificate_chain_pem
9467 @see tls_ctx_check_privatekey
9469 *///=========================================================================
9470 rsocket* tls_ctx_use_certificate_chain_file(
9471 /// Path and filename to certificate chain file
9472 const std::string file) {
9473 if (__debug) debug("tls_ctx_use_certificate_chain_file("
9476 if (SSL_CTX_use_certificate_chain_file(__tls_ctx, file.data()) != 1) randolf::rex::mk_exception("Cannot use certificate chain file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
9478 } // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
9480 /*======================================================================*//**
9482 Load a TLS certificate chain in PEM format from memory and use it in the TLS
9485 Although this functionality doesn't exist in OpenSSL (at the time of writing
9486 this method), it's provided here in a manner that has exactly the same effect
9487 as the @ref tls_ctx_use_certificate_chain_file() methods, but without needing
9488 the PEM-formatted certificate chain stored in a file beforehand.
9491 The @c pem_data parameter is a pointer to the memory location that holds
9492 the PEM formatted certificate chain data. If the length of this data isn't
9493 specified or is set to zero (default), then it will be treated as a multiline
9496 Behind the scenes, we're just writing the pem_data memory to a temporary
9497 file with severely-limited permissions (), then optionally overwriting that
9498 temporary file with random data prior to deleting it (this is the default,
9499 since better security practices should be the default, but on a secured
9500 system it may not be necessary and so this option can also be disabled to
9501 save CPU cycles and reduce overall disk-write I/O operations).
9503 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9504 OpenSSL library doesn't document which errors may be returned)
9506 @returns The same rsocket object so as to facilitate stacking
9507 @see tls_ctx_use_certificate_chain_file
9508 @see tls_ctx_check_privatekey
9510 *///=========================================================================
9511 rsocket* tls_ctx_use_certificate_chain_pem(
9512 /// Pointer to certificate chain data in PEM format
9513 const char* pem_data,
9514 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
9516 /// Whether to overwrite the temporary file with random data before deleting it
9517 const bool random_fill = true) {
9518 if (__debug) debug("tls_ctx_use_certificate_chain_pem(<buf>, " + std::to_string(len)
9519 + ", " + (random_fill ? "true" : "false")
9522 // --------------------------------------------------------------------------
9523 // Measure size of certificate chain if an ASCIIZ string was indicated.
9524 // --------------------------------------------------------------------------
9525 if (len == 0) len = std::strlen(pem_data);
9527 // --------------------------------------------------------------------------
9528 // Generate filename for temporary use.
9529 // --------------------------------------------------------------------------
9530 std::string file = std::filesystem::temp_directory_path();
9531 file.append("/rsocket.")
9532 .append(std::to_string(::getpid()))
9534 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
9536 // --------------------------------------------------------------------------
9537 // Open temporary file.
9538 // --------------------------------------------------------------------------
9539 FILE* fp = fopen(file.data(), "w+");
9540 if (fp == nullptr) randolf::rex::mk_exception("Cannot open temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
9542 ioctl(fileno(fp), FS_IOC_GETFLAGS, &attr);
9543 attr |= FS_NOATIME_FL // Don't update access time attribute
9544 | FS_NODUMP_FL // Don't include in filesystem backup dumps
9545 | FS_SECRM_FL; // Mark file for secure deletion (where supported)
9546 ioctl(fileno(fp), FS_IOC_SETFLAGS, &attr);
9548 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
9549 if (fputs(pem_data, fp) == EOF) {
9551 randolf::rex::mk_exception("Cannot write to temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
9552 } // -x- if !fputs -x-
9555 // --------------------------------------------------------------------------
9556 // Attempt to load certificate chain file, but save the error code for later
9557 // because we need to clean up the temporary file before possibly throwing an
9559 // --------------------------------------------------------------------------
9560 int rc = SSL_CTX_use_certificate_chain_file(__tls_ctx, file.data());
9562 // --------------------------------------------------------------------------
9563 // Overwrite the contenst of the temporary file before deleting it so as to
9564 // sabotage a simple attempt to undelete the file and access the certificate.
9566 // We're also re-using the "len" local variable because it's not needed once
9567 // we get the loop started, and it's more optimal to not allocate yet another
9568 // local variable while "len" goes to waste. :D
9569 // --------------------------------------------------------------------------
9570 if (random_fill) { // This option is configurable
9571 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
9572 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
9573 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
9574 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
9575 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
9577 } // -x- if randfill -x-
9578 fchmod(fileno(fp), 0); // Remove all permissions
9579 fclose(fp); // Close file handle
9580 unlink(file.data()); // Delete temporary file
9582 // --------------------------------------------------------------------------
9583 // Error check ... was delayed here until after temporary file cleanup.
9584 // --------------------------------------------------------------------------
9585 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);
9588 } // -x- rsocket* tls_ctx_use_certificate_chain_pem -x-
9590 /*======================================================================*//**
9592 Load a TLS private key in PEM format from a text file and use it in the TLS
9595 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9596 OpenSSL library doesn't document which errors may be returned)
9598 @returns The same rsocket object so as to facilitate stacking
9599 @see tls_ctx_use_privatekey_file
9600 @see tls_ctx_use_privatekey_pem
9601 @see tls_ctx_check_privatekey
9603 *///=========================================================================
9604 rsocket* tls_ctx_use_privatekey_file(
9605 /// Pointer to ASCIIZ path-and-filename of private key file
9607 if (__debug) debug("tls_ctx_use_privatekey_file("
9610 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);
9613 } // -x- rsocket* tls_ctx_use_privatekey_file -x-
9615 /*======================================================================*//**
9617 Load a TLS private key in PEM format from a text file and use it in the TLS
9620 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9621 OpenSSL library doesn't document which errors may be returned)
9623 @returns The same rsocket object so as to facilitate stacking
9624 @see tls_ctx_use_privatekey_file
9625 @see tls_ctx_use_privatekey_pem
9626 @see tls_ctx_check_privatekey
9628 *///=========================================================================
9629 rsocket* tls_ctx_use_privatekey_file(
9630 /// Path and filename to private key file
9631 const std::string file) {
9632 if (__debug) debug("tls_ctx_use_privatekey_file("
9635 if (SSL_CTX_use_PrivateKey_file(__tls_ctx, file.data(), SSL_FILETYPE_PEM) != 1) randolf::rex::mk_exception("Cannot use private key file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
9638 } // -x- rsocket* tls_ctx_use_privatekey_file -x-
9640 /*======================================================================*//**
9642 Load a TLS private key in PEM format from memory and use it in the TLS
9645 Although this functionality doesn't exist in OpenSSL (at the time of writing
9646 this method), it's provided here in a manner that has exactly the same effect
9647 as the @ref tls_ctx_use_privatekey_file() methods, but without needing the
9648 PEM-formatted private key stored in a file beforehand.
9651 The @c pem_data parameter is a pointer to the memory location that holds the
9652 PEM formatted private key data. If the length of this data isn't specified
9653 or is set to zero (default), then it will be treated as a multiline ASCIIZ
9656 Behind the scenes, we're just writing the pem_data memory to a temporary
9657 file (with severely-limited permissions), then optionally overwriting that
9658 temporary file with random data prior to deleting it (this is the default,
9659 since better security practices should be the default, but on a secured
9660 system it may not be necessary and so this option can also be disabled to
9661 save CPU cycles and reduce overall disk-write I/O operations).
9663 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9664 OpenSSL library doesn't document which errors may be returned)
9666 @returns The same rsocket object so as to facilitate stacking
9667 @see tls_ctx_use_privatekey_file
9668 @see tls_ctx_check_privatekey
9670 *///=========================================================================
9671 rsocket* tls_ctx_use_privatekey_pem(
9672 /// Pointer to private key data in PEM format
9673 const char* pem_data,
9674 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
9676 /// Whether to overwrite the temporary file with random data before deleting it
9677 const bool random_fill = true) {
9678 if (__debug) debug("tls_ctx_use_privatekey_pem(<buf>, " + std::to_string(len)
9679 + ", " + (random_fill ? "true" : "false")
9682 // --------------------------------------------------------------------------
9683 // Measure size of private key if an ASCIIZ string was indicated.
9684 // --------------------------------------------------------------------------
9685 if (len == 0) len = std::strlen(pem_data);
9687 // --------------------------------------------------------------------------
9688 // Generate filename for temporary use.
9689 // --------------------------------------------------------------------------
9690 std::string file = std::filesystem::temp_directory_path();
9691 file.append("/rsocket.")
9692 .append(std::to_string(::getpid()))
9694 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
9696 // --------------------------------------------------------------------------
9697 // Open temporary file.
9698 // --------------------------------------------------------------------------
9699 FILE* fp = fopen(file.data(), "w+");
9700 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);
9701 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
9702 if (fputs(pem_data, fp) == EOF) {
9704 randolf::rex::mk_exception("Cannot cannot write to temporary file (to use private key) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
9705 } // -x- if !fputs -x-
9708 // --------------------------------------------------------------------------
9709 // Attempt to load private key file, but save the error code for later
9710 // because we need to clean up the temporary file before possibly throwing an
9712 // --------------------------------------------------------------------------
9713 int rc = SSL_CTX_use_PrivateKey_file(__tls_ctx, file.data(), SSL_FILETYPE_PEM);
9715 // --------------------------------------------------------------------------
9716 // Overwrite the contenst of the temporary file before deleting it so as to
9717 // sabotage a simple attempt to undelete the file and access the certificate.
9719 // We're also re-using the "len" local variable because it's not needed once
9720 // we get the loop started, and it's more optimal to not allocate yet another
9721 // local variable while "len" goes to waste. :D
9722 // --------------------------------------------------------------------------
9723 if (random_fill) { // This option is configurable
9724 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
9725 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
9726 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
9727 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
9728 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
9730 } // -x- if randfill -x-
9731 fchmod(fileno(fp), 0); // Remove all permissions
9732 fclose(fp); // Close file handle
9733 unlink(file.data()); // Delete temporary file
9735 // --------------------------------------------------------------------------
9736 // Error check ... was delayed here until after temporary file cleanup.
9737 // --------------------------------------------------------------------------
9738 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);
9741 } // -x- rsocket* tls_ctx_use_privatekey_pem -x-
9743 /*======================================================================*//**
9745 Initiate the TLS handshake with the endpoint (which is presumed to be a
9747 This method makes it easier to support application-level commands such as @c
9748 STARTTLS (which are implemented in protocols like SMTP, POP3, IMAP4, MEOW,
9749 FTP, NNTP, LDAP, XMPP, etc.).
9751 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9752 OpenSSL library doesn't document which errors may be returned)
9754 @returns The same rsocket object so as to facilitate stacking
9756 @see connect(std::string, int)
9759 *///=========================================================================
9760 rsocket* tls_do_handshake() {
9761 if (__debug) debug("tls_handshake(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9763 __rc_check_tls(SSL_do_handshake(__tls_fd));
9765 } // -x- rsocket* tls_do_handshake -x-
9767 /*======================================================================*//**
9769 Get OpenSSL's TLS structure.
9770 @returns TLS structure
9771 @returns nullptr = TLS structure not yet allocated
9773 *///=========================================================================
9774 const SSL* tls_fd() noexcept {
9776 } // -x- int tls_fd -x-
9778 /*======================================================================*//**
9780 Return the current @ref rsocket_sni object that this @c rsocket will use when
9781 accepting incoming encrypted connections.
9782 @returns Pointer to @c rsocket_sni object
9783 @returns nullptr = SNI is not assigned
9785 @see tls_sni(rsocket_sni*)
9787 *///=========================================================================
9788 rsocket_sni* tls_sni() noexcept {
9790 } // -x- rsocket_sni* tls_sni -x-
9792 /*======================================================================*//**
9794 Set the current @ref rsocket_sni object that this @c rsocket will use when
9795 accepting incoming encrypted connections.
9797 Use the @ref name() method to find out which server name was supplied by the
9798 endpoint that triggered the SNI callback, regardless of whether it matches
9799 any of the TLS certificates used with this rsocket object or the rsocket_sni
9800 object that's associated with this rsocket object. If an SNI callback wasn't
9801 triggered, or if the endpoint didn't provide a server name, then it will
9802 remain unaffected (and the default {empty string} will remain unchanged).
9803 @returns The same rsocket object so as to facilitate stacking
9806 @see is_tls_sni_match
9808 *///=========================================================================
9810 /// Pointer to the @ref rsocket_sni object to use, or specify @c nullptr to
9811 /// remove SNI support from this rsocket object
9812 rsocket_sni* sni) noexcept {
9814 // --------------------------------------------------------------------------
9815 // Remove SNI support.
9816 // --------------------------------------------------------------------------
9817 if (sni == nullptr) {
9819 // --------------------------------------------------------------------------
9820 // Only remove SNI support if it's configured.
9821 // --------------------------------------------------------------------------
9822 if (__tls_sni != nullptr) SSL_CTX_set_client_hello_cb(__tls_ctx, nullptr, this);
9824 // --------------------------------------------------------------------------
9825 // Add or set SNI support.
9826 // --------------------------------------------------------------------------
9829 // --------------------------------------------------------------------------
9830 // Configure SNI callbacks for TLS. This callback will occur in the early
9831 // stages, which is triggered by the SSL_accept() method.
9832 // --------------------------------------------------------------------------
9833 SSL_CTX_set_client_hello_cb(__tls_ctx, tls_sni_callback, this);
9835 } // -x- if !sni -x-
9837 // --------------------------------------------------------------------------
9838 // Update internal pointer to the SNI map, or nullptr if it's being removed.
9839 // --------------------------------------------------------------------------
9843 } // -x- rsocket_sni* tls_sni -x-
9846 /*======================================================================*//**
9848 Get OpenSSL's TLS structure.
9849 @returns TLS structure
9850 @returns nullptr = TLS structure not yet allocated
9852 *///=========================================================================
9853 int static tls_sni_callback(
9854 /// OpenSSL's socket descriptor/handle
9856 /// Where to store the @c alert value
9858 /// Context-specific argument
9859 void* arg) noexcept {
9861 // --------------------------------------------------------------------------
9862 // Internal variables.
9863 // --------------------------------------------------------------------------
9864 rsocket* r = (rsocket*)arg; // We may be updating the TLS context (this is normally the new client's rsocket object)
9865 const unsigned char* out = nullptr;
9866 size_t out_size = 0;
9868 // --------------------------------------------------------------------------
9869 // Obtain a pointer to newly-allocated ClientHello fields data. If *out is
9870 // nullptr, it means that no TLSEXT_TYPE_server_name was received from the
9871 // client, and so the default TLS context will suffice. If out_size is less
9872 // than or equal to 2, then it also won't have what we need.
9873 // --------------------------------------------------------------------------
9874 if (!SSL_client_hello_get0_ext(tls_fd, TLSEXT_TYPE_server_name, &out, &out_size)
9877 //std::cout << "no server_name / out_size=" << out_size << std::endl;
9878 r->__tls_sni_has_name = false; // No SNI name
9879 r->__tls_sni_match = false; // No SNI match
9880 return SSL_CLIENT_HELLO_SUCCESS; // 1
9882// if (!SSL_client_hello_get0_ext(tls_fd, TLSEXT_TYPE_server_name, &out, &out_size)
9883// || out == nullptr || out_size <= 2) return SSL_CLIENT_HELLO_SUCCESS; // 1
9885 // --------------------------------------------------------------------------
9886 // Scan for TLSEXT_NAMETYPE_host_name, but if we can't find it then we'll
9887 // just leave the default TLS context as is.
9888 // --------------------------------------------------------------------------
9889 unsigned char* pos = (unsigned char*)out;
9890 size_t len = (*(pos++) << 8); // Extract first MSB
9891 len += *(pos++); // Add first LSB
9892//std::cout << "scan for host_name / out_size=" << out_size << " len=" << len << std::endl;
9893 if (len + 2 != out_size) return SSL_CLIENT_HELLO_SUCCESS; // 1 // goto finish;
9896 // --------------------------------------------------------------------------
9897 // We're taking a shortcut by examining only the first element in the list,
9898 // but in the future we need to make this more robust in case other types of
9899 // elements precede what we're looking for.
9901 // Unfortunately, there's no documentation that properly-explains the format
9902 // of the list, so some deeper research into OpenSSL's source code will be
9903 // needed (a cursory look so far has not yielded the necessary insight).
9905 // TODO: Turn this into a loop that supports future clients that provide
9906 // multiple SNI server names in their requests. (Although this isn't
9907 // occuring at present with common end-user tools such as web browsers
9908 // and eMail software, it may happen in the future as client/server
9909 // software becomes more sophisticated to support users, some of whom
9910 // are gradually becoming more savvy.)
9911 // --------------------------------------------------------------------------
9912 if (out_size <= 3 || *pos++ != TLSEXT_NAMETYPE_host_name) return SSL_CLIENT_HELLO_SUCCESS; // 1 // goto finish;
9913 out_size--; // Avoid a buffer overrun caused by corrupt or prematurely-truncated data
9915//std::cout << "out_size=" << out_size << std::endl;
9917 // --------------------------------------------------------------------------
9918 // Extract and use the hostname (SNI server name) that was supplied by the
9919 // endpoint so that the correct TLS certificate can be selected and assigned.
9920 // --------------------------------------------------------------------------
9921 len = (*(pos++) << 8); // Extract second MSB
9922 len += *(pos++); // Add second LSB
9923 if (!(len + 2 > out_size)) { // Only process SNI server name string if it's at least 2 bytes long
9924 // Debug: std::cout << "Server name: " << (const char*)p << std::endl; // Debug
9926//std::cout << "out_size=" << out_size << " len=" << len << std::endl;
9928 // --------------------------------------------------------------------------
9929 // Obtain the correct TLS context (wildcards supported) that is associated
9930 // with the hostname (SNI server name) that was supplied by the endpoint.
9931 // --------------------------------------------------------------------------
9932 const char* sni_name = (const char*)pos;
9933 r->__name_sni.assign(sni_name, len);
9934 SSL_CTX* new_ctx = r->__tls_sni->get_ctx(r->__name_sni.data(), true); // Third parameter (nullptr by default) is the default context (e.g., r->tls_ctx()) if SNI does not match
9935 r->__tls_sni_has_name = sni_name[0] != 0; // If the ASCIIZ string is empty, then SNI didn't receive a hostname or the hostname entry was empty
9936 r->__tls_sni_match = new_ctx != nullptr; // Track whether sni_name matches any of the certificates that are stored in rsocket_sni
9938 // --------------------------------------------------------------------------
9939 // Change TLS context so that encryption with the endpoint uses the correct
9940 // TLS certificate (otherwise the endpoint will indicate security risk errors
9941 // to end users, log files, etc., and may {should} reject the connection).
9942 // --------------------------------------------------------------------------
9943 new_ctx = SSL_set_SSL_CTX(tls_fd, new_ctx); // Returns new_ctx or, if new_ctx is nullptr then it returns the original CTX
9944 r->tls_ctx(new_ctx); // If nullptr, then tls_ctx() will get OpenSSL to create a new SSL_CTX
9949 // --------------------------------------------------------------------------
9950 // Free the ClientHello fields data, then return SSL_CLIENT_HELLO_SUCCESS.
9951 // Even if we encountered a problem with the ClientHello fields data, we
9952 // still return SSL_CLIENT_HELLO_SUCCESS so that the TLS context will be
9953 // accepted as valid.
9955 // If we return SSL_CLIENT_HELLO_ERROR the connection will fail unnecessarily
9956 // for valid TLS certificates. If the ClientHello fields data is malformed
9957 // to a degree that doesn't satisfy OpenSSL, then OpenSSL will reject it, so
9958 // there's really no point in duplicating what OpenSSL already does properly,
9959 // which OpenSSL will pass through the standard error channels with normal
9960 // error details-and-diagnostics anyway.
9962 // OpenSSL documentation says to do the following...
9963 // OPENSSL_free((char*)out);
9964 // ...but it's not necessary here because we'd be freeing a local variable,
9965 // which will fail and cause a crash.
9966 // --------------------------------------------------------------------------
9967 //std::cout << "out=" << randolf::rtools::to_hex(out[0], 8) << std::endl; // Debug
9968 //OPENSSL_free((unsigned char*)out);
9970 return SSL_CLIENT_HELLO_SUCCESS; // 1
9971 } // -x- int tls_sni_callback -x-
9974 /*======================================================================*//**
9976 Convert a 48-bit integer to a machine address in the form of @c
9977 xx:xx:xx:xx:xx:xx where every instance of @c xx is a hexadecimal
9978 representation of each respective 8-bit byte portion.
9980 This method is needed because we don't want to bring in the heavy fmt::format
9981 class as a dependency.
9982 @returns Mac address as 17-character in the typical format expected by system
9986 *///=========================================================================
9987 static std::string to_mac(
9988 /// Pointer to 48-bit integer
9989 const void* addr) noexcept {
9991 h.resize(18); // 48-bit mac address needs 6 hexadecimal pairs of nybbles delimited by colons plus a NULL terminator
9992 h.resize(snprintf(h.data(),
9994 "%02x:%02x:%02x:%02x:%02x:%02x",
10000 ((u_char*)addr)[5])); // Convert, and truncate NULL terminator
10002 } // -x- std::string to_mac -x-
10004 /*======================================================================*//**
10006 Convert a 48-bit integer to a node address in the form of @c xxxx:xxxx:xxxx
10007 where every instance of @c xxxx is a hexadecimal representation of each
10008 respective 16-bit word portion.
10010 This method is needed because we don't want to bring in the heavy fmt::format
10011 class as a dependency.
10012 @returns Node address as 14-character in the typical format expected by
10013 network administrators
10016 *///=========================================================================
10017 static std::string to_node(
10018 /// Pointer to 48-bit integer
10019 const void* addr) noexcept {
10021 h.resize(15); // 48-bit node address needs 3 hexadecimal sets of words delimited by colons plus a NULL terminator
10022 h.resize(snprintf(h.data(),
10025 ((u_int16_t*)addr)[0],
10026 ((u_int16_t*)addr)[1],
10027 ((u_int16_t*)addr)[2])); // Convert, and truncate NULL terminator
10029 } // -x- std::string to_node -x-
10031 /*======================================================================*//**
10033 Send a formatted string to the @ref rsocket endpoint.
10035 The @c format is described in the documentation for the POSIX or Standard C
10036 Library @c printf() function.
10037 @throws randolf::rex::xEBADF The underlying socket is not open
10038 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
10039 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
10040 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
10041 @throws randolf::rex::xENOMEM Insufficient memory
10042 @returns The same rsocket object so as to facilitate stacking
10043 @see eol_fix_printf
10044 @see is_eol_fix_printf
10051 *///=========================================================================
10053 /// Format string to use
10054 const char* format,
10055 /// Variadic arguments in @c va_list format
10058 int rc = ::vasprintf(&buf, format, args);
10059 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
10060 if (__eol_fix_printf && !__eol.empty()) { // We need to edit the string and then send it
10061 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
10063 __send(str.data(), str.length());
10064 } else { // No need to edit the string, so just send it as is
10068 } catch (std::exception& e) { // Free buf then re-throw the exception
10069 ::free(buf); // Prevent memory leak when an exception is thrown
10072 } // -x- if __eol_fix_printf -x-
10074 } // -x- rsocket* vprintf -x-
10076 /*======================================================================*//**
10078 Send a formatted string to the @ref rsocket endpoint, and append an EoL
10081 The @c format is described in the documentation for the POSIX or Standard C
10082 Library @c printf() function.
10083 @throws randolf::rex::xEBADF The underlying socket is not open
10084 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
10085 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
10086 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
10087 @throws randolf::rex::xENOMEM Insufficient memory
10088 @returns The same rsocket object so as to facilitate stacking
10090 @see eol_fix_printf
10091 @see is_eol_fix_printf
10098 *///=========================================================================
10099 rsocket* vprintfline(
10100 /// Format string to use
10101 const char* format,
10102 /// Variadic arguments in @c va_list format
10105 int rc = ::vasprintf(&buf, format, args);
10106 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
10107 if (__eol_fix_printf && !__eol.empty()) {
10108 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
10111 __send(str.data(), str.length());
10114 __sendline(buf, rc);
10116 } catch (std::exception& e) { // Free buf then re-throw the exception
10117 ::free(buf); // Prevent memory leak when an exception is thrown
10120 } // -x- if __eol_fix_printf -x-
10122 } // -x- rsocket* vprintfline -x-
10125 /*======================================================================*//**
10126 Track unencrypted bytes received. When the number of bytes is negative or
10127 zero, it isn't recorded.
10128 This is an internal function.
10129 @returns same value provided in @ref n
10131 *///=========================================================================
10132 int __track_bytes_rx(
10133 /// Number of bytes transferred
10135 if (n > 0) __bytes_rx.fetch_add(n, std::memory_order_relaxed);
10137 } // -x- __track_bytes_rx -x-
10139 /*======================================================================*//**
10140 Track unencrypted bytes transmitted. When the number of bytes is negative or
10141 zero, it isn't recorded.
10142 This is an internal function.
10143 @returns same value provided in @ref n
10145 *///=========================================================================
10146 int __track_bytes_tx(
10147 /// Number of bytes transferred
10149 if (n > 0) __bytes_tx.fetch_add(n, std::memory_order_relaxed);
10151 } // -x- __track_bytes_tx -x-
10153 /*======================================================================*//**
10154 Track encrypted bytes received. When the number of bytes is negative or
10155 zero, it isn't recorded.
10156 This is an internal function.
10157 @returns same value provided in @ref n
10159 *///=========================================================================
10160 int __track_crypt_rx(
10161 /// Number of bytes transferred
10163 if (n > 0) __crypt_rx.fetch_add(n, std::memory_order_relaxed);
10165 } // -x- __track_crypt_rx -x-
10167 /*======================================================================*//**
10168 Track encrypted bytes transmitted. When the number of bytes is negative or
10169 zero, it isn't recorded.
10170 This is an internal function.
10171 @returns same value provided in @ref n
10173 *///=========================================================================
10174 int __track_crypt_tx(
10175 /// Number of bytes transferred
10177 if (n > 0) __crypt_tx.fetch_add(n, std::memory_order_relaxed);
10179 } // -x- __track_crypt_tx -x-
10181 /*======================================================================*//**
10182 Specialized private timeout-handling class that sets a new timeout on the
10183 specified socket, then relies on RAII to activate the destructor to restore
10184 the original timeout (which was acquired in the constructor).
10185 *///=========================================================================
10186 class RAII_timeout {
10189 // --------------------------------------------------------------------------
10190 // Prepare timeout structures so that we can override it (if timeout != 0)
10191 // and also restore it to what it used to be once we're finished. We need to
10192 // set up the "timeout_target" variable and few timeval structures, backup
10193 // the current non-recvline timeout settings, and set the recvline timeout.
10194 // --------------------------------------------------------------------------
10195 randolf::rsocket* r; // TODO: Add support for debug() method
10196 struct timeval old_tv{0, 0}; // {tv_sec, tv_usec};
10197 struct timeval new_tv{0, 0}; // {tv_sec, tv_usec};
10200 /*======================================================================*//**
10202 Change the specified rsocket's @c timeout, after first saving the current
10203 timeout settings for later restoration.
10205 If both @c sec or @c usec are set to 0, then this class will not make any
10206 changes to the underlying socket's timeout.
10207 *///=========================================================================
10209 /// The rsocket class to which to apply the timeout
10210 randolf::rsocket* r,
10211 /// Timeout in seconds
10213 /// Timeout in microseconds
10214 const int usec = 0) {
10215 if (sec == 0 && usec == 0) return; // Do nothing if timeout is 0
10217 if (r->__debug) r->debug("RAII_timeout(socket{0x" + randolf::rtools::to_hex(r->__socket_fd) + "}"
10218 + ", " + std::to_string(sec)
10219 + ", " + std::to_string(usec)
10221 socklen_t otv{sizeof(old_tv)};
10222 r->__rc_check(::getsockopt(r->__socket_fd, SOL_SOCKET, SO_RCVTIMEO, &old_tv, &otv)); // getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO);
10223 new_tv.tv_sec = sec;
10224 new_tv.tv_usec = usec;
10225 r->__rc_check(::setsockopt(r->__socket_fd, SOL_SOCKET, SO_RCVTIMEO, &new_tv, sizeof(new_tv))); // setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
10226//std::cout << "Set timeout to " << sec << std::endl; // Debug
10227 } // -x- consturctor __temporary_timeout -x-
10229 /*======================================================================*//**
10231 Restore the underlying socket's original @c timeout setting.
10232 *///=========================================================================
10233 ~RAII_timeout() noexcept {
10234 if (new_tv.tv_sec == 0 && new_tv.tv_usec == 0) return; // Do nothing if timeout is 0
10235 if (r->__debug) r->debug("~RAII_timeout(socket{0x" + randolf::rtools::to_hex(r->__socket_fd) + "}"
10236 + ", " + std::to_string(old_tv.tv_sec)
10237 + ", " + std::to_string(old_tv.tv_usec)
10239 // This shouldn't fail, but if it does it won't matter since it can only mean that the socket is screwed
10240 ::setsockopt(r->__socket_fd, SOL_SOCKET, SO_RCVTIMEO, &new_tv, sizeof(new_tv)); // setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
10241//std::cout << "Restore timeout" << std::endl; // Debug
10242 } // -x- destructor __temporary_timeout -x-
10244 }; // -x- class __RAII_timeout -x-
10246 }; // -x- class rsocket -x-
10248}; // -x- namespace randolf -x-
10250// Save this for a future sendlines() methods.
10251// const void* msg_ptr = msg.data(); // Prevent repeated calls to data() method
10252// const int len = msg.length(); // Prevent repeated calls to length() method