randolf.ca  1.00
Randolf Richardson's C++ classes
Loading...
Searching...
No Matches
rsocket
1#pragma once
2
3#include <randolf/rex>
4#include <randolf/rline>
5#include <randolf/rsocket_io>
6#include <randolf/rsocket_sni>
7#include <randolf/rtools>
8#include <randolf/sockaddr_dl.h>
9
10#include <atomic>
11#include <bit> // std::endian
12#include <cstdarg> // std::va_list
13#include <cstring> // std::strlen
14#include <ctime> // std::strftime
15#include <exception> // std::exception
16#include <filesystem> // std::filesystem::temp_directory_path
17#include <initializer_list>
18#include <iostream> // std::put_date
19#include <map> // std::map
20#include <memory> // std::shared_ptr
21#include <mutex> // std::mutex
22#include <regex>
23#include <set>
24//#include <sstream> // std::ostringstream
25#include <unordered_map> // std::unordered_map
26#include <vector>
27
28#include <ifaddrs.h>
29#include <netdb.h>
30#include <poll.h>
31#include <string.h> // strerror()
32#include <unistd.h>
33
34#include <arpa/inet.h>
35
36#include <linux/fs.h> // Flags for ioctl()
37
38#include <net/if.h> // ifreq structure used by bind()
39
40#include <netinet/icmp6.h>
41#include <netinet/in.h>
42
43#include <netpacket/packet.h> // struct sockaddr_ll
44
45#include <openssl/err.h>
46#include <openssl/ossl_typ.h>
47#include <openssl/ssl.h>
48
49#include <sys/ioctl.h>
50#include <sys/socket.h>
51#include <sys/stat.h> // fchmod()
52#include <sys/time.h> // TIMEVAL_TO_TIMESPEC macro
53#include <sys/types.h>
54#include <sys/un.h>
55
56static_assert((sizeof(__time_t) * CHAR_BIT) >= 64, "64-bit __time_t is required");
57
58namespace randolf {
59
60 /*======================================================================*//**
61 @brief
62 This @ref rsocket class provides an easy-to-use and thoroughy-implemented
63 object-oriented socket I/O interface for C++, intended to make socket I/O
64 programming (with or without TLS encryption) easier and more enjoyable.
65
66 Here's a short list of benefits that are helpful in developing high quality
67 code that's consistent-and-reliable, and improves overall productivity:
68
69 - eliminating the need to repeatedly write blocks of code that check for
70 errors, by throwing exceptions instead (see @ref randolf::rex::rex class
71 for details and the long list of exceptions that are supported)
72 - eliminating the need to track socket descriptors
73 - eliminating the need to handle encrypted I/O separately (most functions)
74 - eliminating the need to manage memory for many common structures used to
75 interface with socket options, etc.
76 - eliminating the need to record socket I/O statistics with every call to
77 underlying socket I/O functions (see @ref randolf::rsocket_io for
78 details)
79 - text-line reading/writing with an adapative approach (invented by
80 Randolf Richardson in the 1980s for a custom BBS software project) to
81 automatically detect EoL (End-of-Line) character sequences (unless the
82 developer provides a specific sequence via an @ref eol method) that can
83 determine whether an endpoint is sending Linux/UNIX (standard), MacOS,
84 DOS CR/LF, or even broken LF/CR (reverse of DOS) EoL sequences
85 - transparent support for encryption with many additional features,
86 including STARTTLS, ingress/egress policy enforcement, and SNI
87 - eliminating the complexity of handling events with poll(), select(), and
88 related functions (see the @ref randolf::rsocket_mux class for details)
89 - providing a variety of other useful features that make it easier to
90 communicate with socket endpoints, such as receiving/sending an entire
91 structure via a single call to the new-and-specialized @ref recv_struct
92 or @ref send_struct methods, respectively
93
94 An rsocket is either the endpoint that our underlying socket will connect to,
95 or it's the server daemon that our underying socket will listen() to and
96 accept() [inbound] connections from.
97
98 @par Use case
99
100 Using the C interface, the programming must check for errors by testing the
101 response codes (most of which are consistent, with a few subtle outliers),
102 which leads to a lot of additional error-checking code with the potential for
103 unintended errors (a.k.a., bugs). This style is necessary in C, but with C++
104 the way to handle errors is with exceptions, so I created this rsocket class
105 to handle all these tedious details behind-the-scenes and, for any socket
106 errors, to generate exceptions so that source code can be greatly simplified
107 (and, as a result, also easier to read and review).
108
109 Pre-allocating buffers is also handled internally, which is particularly
110 useful when making repeated calls to recv() and related methods. These
111 methods return std::shared_ptr<@c structure > (a C++ smart pointer that aids
112 in the prevention of resource leaks) or std::vector<char> (resized to the
113 actual number of bytes received) as appropriate which eliminates the need to
114 track @c size_t separately.
115
116 @par Conventions
117 Lower-case letter "r" is regularly used in partial example code to represent
118 an instantiated rsocket object.
119
120 An ASCIIZ string is a C-string (char* array) that includes a terminating null
121 (0) character at the end.
122
123 The following custom qualifiers are incorporated into headings by Doxygen
124 alongside method titles throughout the documentation:
125 - @c POSIX denotes a method that is based on POSIX functions by the same
126 name and don't deviate significantly from the POSIX function arguments
127 (intended to be helpful to developers transitioning to/from rsocket or
128 working on source code that utilizes @ref rsocket and POSIX functions)
129 - @c TLS denotes that a method works properly with TLS-encrypted sockets
130 (most of the POSIX functions have been made to work properly with TLS,
131 but for the few rare cases of functions that can't be made to work with
132 TLS an effort has also been made to mention this using Doxygen's
133 "warning" sections in addition to omitting the TLS qualifier)
134
135 @par Getting started with a few simple examples
136
137 This is an example of connecting to an HTTP server, using the "GET" command
138 to request the home page (using HTTP/1.0), then receiving-and-displaying the
139 resulting web page's contents via STDOUT (or sending an error message to
140 STDERR). Finally, we exit with an EXIT_SUCCESS (or EXIT_FAILURE) code.
141
142 @code{.cpp}
143 #include <iostream> // std::cout, std::cerr, std::endl, etc.
144 #include <randolf/rex>
145 #include <randolf/rsocket>
146
147 int main(int argc, char *argv[]) {
148 try {
149 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
150 r.connect("www.example.com", 80);
151 r.sendline("GET / HTTP/1.0");
152 r.sendline("Host: www.example.com");
153 r.sendline("Connection: close");
154 r.send_eol();
155 while (r.is_open()) {
156 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
157 } // -x- while data -x-
158 r.close();
159 } catch (const randolf::rex::xALL e) {
160 std::cerr << "Socket exception: " << e.what() << std::endl;
161 return EXIT_FAILURE;
162 } catch (const std::exception e) {
163 std::cerr << "Other exception: " << e.what() << std::endl;
164 return EXIT_FAILURE;
165 }
166 return EXIT_SUCCESS;
167 } // -x- int main -x-
168 @endcode
169
170 Parameter stacking is supported (with methods that return @c rsocket*); in
171 this example, notice that semicolons (";") and "r." references are omittted
172 (when compared with the above):
173
174 @code{.cpp}
175 #include <iostream> // std::cout, std::cerr, std::endl, etc.
176 #include <randolf/rex>
177 #include <randolf/rsocket>
178
179 int main(int argc, char *argv[]) {
180 try {
181 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
182 r.connect("www.example.com", 80)
183 ->sendline("GET / HTTP/1.0")
184 ->sendline("Host: www.example.com")
185 ->sendline("Connection: close")
186 ->send_eol();
187 while (r.is_open()) {
188 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
189 } // -x- while data -x-
190 r.close();
191 } catch (const randolf::rex::xALL e) {
192 std::cerr << "Socket exception: " << e.what() << std::endl;
193 return EXIT_FAILURE;
194 } catch (const std::exception e) {
195 std::cerr << "Other exception: " << e.what() << std::endl;
196 return EXIT_FAILURE;
197 }
198 return EXIT_SUCCESS;
199 } // -x- int main -x-
200 @endcode
201
202 @par Features
203
204 This is meant to be a comprehensive socket class for C++, which is intended
205 to make socket I/O coding easier for developers with some key features:
206
207 - easy conversion from C-style POSIX sockets due to API consistency
208 - transparent TLS support (OpenSSL dependency)
209 - keys and certificate chains can also be loaded from memory
210 - SNI support (see the @ref rsocket_sni class for more information)
211 - underlying socket handle is accessible (via the @ref socket_fd() method)
212 - socket options are easier to get and to set
213 - sensible support for future or unknown socket options
214 - errors are presented as ~100 separate exception classes: @ref rex::rex
215 - one parent exception class makes it easier to catch all socket errors
216 - a few exceptions groups are also provided to catch groups of errors
217 - new buffers are returned as std::shared_ptr's to eliminate memory leaks
218 - constructors with sensible defaults help to simplify coding
219 - documentation includes code samples (with @c \#include lines as needed)
220 - debug output with helpful output (and option to set output file handle)
221 - low-overhead is considered (this is why there's a bit more overloading)
222 - thread-safety is noted where it is absolutely available (if any caution
223 is warranted, it will also be noted)
224 - can send ASCIIZ (C-strings) without needing to specify string length
225 - can send @c std::string (which tracks its own string length)
226 - each socket can optionally have a @ref name arbitrarily assigned (e.g.,
227 by an algorithm, or even populated with the connecting user's name, or
228 whatever purpose the development goals find it useful for)
229
230 Additional features that are not part of the typical POSIX standard, but
231 deserve special mention because they are needed so often:
232
233 - easy access to internal I/O counters (see @ref rsocket_io for details)
234 - your rsocket_io structure can be updated automatically by rsocket's
235 destructor after underlying socket is closed (see @c net_io_final()
236 method for details)
237 - printf(), printfline(), vprintf(), vprintfline() // Formatted strings
238 - with automatic EoL sequence substitution (and/or the addition of)
239 - recv_asciiz(), send_asciiz() // ASCIIZ string I/O operations
240 - recv_byte() , send_byte() // 8-bit byte operations (LSB/MSB is N/A)
241 - recv_struct(), send_struct() // Multi-byte operations
242 - recv_uint16(), send_uint16() // 16-bit operations with LSB/MSB variants
243 - recv_uint32(), send_uint32() // 32-bit operations with LSB/MSB variants
244 - recv_uint64(), send_uint64() // 64-bit operations with LSB/MSB variants
245 - recvline(), recv_rline(), sendline() // ASCII text line I/O operations
246 - class-wide configurable newline sequence (defaults to @e autodetect)
247 - @ref rsocket_group class for grouping rsockets (and automatic inclusion
248 by accept() and accept4() methods; this may be a different group from
249 whichever group the parent rsocket is in, if it's even in one)
250 - option to send data to all rsocket objects in the rsocket_group
251 - with support from the rsocket_mux class (for multiplexing operations)
252 - automatic naming policies (possibly like net_io() formatting style)
253
254 Some advanced features are planned that exceed what the basic socket I/O
255 functions provide, but are also needed:
256
257 - recv_uint128()/send_uint128() // 128-bit operations w/ LSB/MSB variants
258 - recv_uint(n)/send_uint(n) where "n" specifies the number of bits (which
259 must be a multiple of 8), with LSB/MSB variants
260 - auto-detection of inbound TLS connection (this is turned off by default)
261 - This is not the same as STARTTLS (an application-level command, for
262 which the @ref tls_do_handshake() method will likely be used)
263 - simple timing tracking options using timing_start() and timing_stop()
264 methods, the results of which can be retrieved with timing_get() or a
265 similarly-named group of methods
266
267 Other features that are not a high priority:
268
269 - internal support for portability to Microsoft Windows, which is a major
270 undertaking that I know will be time-consuming since Windows Sockets
271 exhibit some nuanced behaviours and are not consistent with POSIX
272 sockets APIs that are used by Linux, UNIX, MacOS, and pretty much all
273 other Operating Systems. Because of this, MS-Windows portability just
274 isn't a high priority for me (without sufficient demand and sufficient
275 funding so I can commit my time without missing mortgage payments,
276 student loan payments {for my kids}, various living expenses, etc.).
277
278 @par Notes
279
280 I use the term "ASCIIZ string" to indicate an array of characters that's
281 terminated by a 0 (a.k.a., null). Although this is very much the same as a
282 C-string, the difference is that in many API functions a C-string must often
283 be accompanied by its length value. When referring to an ASCIIZ string, I'm
284 intentionally indicating that the length of the string is not needed because
285 the string is null-terminated. (This term was also commonly used in assembly
286 language programming in the 1970s, 1980s, and 1990s, and as far as I know is
287 still used by machine language programmers today.)
288
289 UTF-8 works without any problems as it is backward-compatible to 8-bit ASCII,
290 and because @c std::string uses 8-bit bytes to store strings internally. Do
291 keep in mind that the manipulation of UTF-8 substrings will require working
292 with UTF-8 codepoints that may consume one or more bytes, which is beyond the
293 scope of the impartial (to UTF-8 and ASCII) functionality that this rsocket
294 class provides.
295
296 UTF-8 newline codepoints NEL (c2 85), LS (e2 80 a8), and PS (e2 80 a9) garner
297 no special handling at this time - unlike CR/LF (0d/0a) - and are merely
298 treated as non-newline codepoints. There is a possibility of adding support
299 for this in the future, but additional research and planning is required to
300 make sure this works properly. What is most likely is that some UTF-8 flags
301 will be added to support each of these (which will probably be disabled by
302 default) that will be integrated into the readline() methods. This also
303 depends on how widely used these particular codepoints are, and pending
304 further research to determine whether these really are supposed to be used
305 functionally as newlines...
306
307 So far, there are two UTF-8 codepoints that absolutely are not functional,
308 yet which a small number of people have mistakenly assumed are:
309 - <sup>C</sup><sub>R</sub> = superscript "C" with subscript "R" (e2 90 9d)
310 - <sup>N</sup><sub>L</sub> = superscript "N" with subscript "L" (e2 90 a4)
311
312 The special characters above are intended to represent the Carriage-Return
313 and New-Line respectively in documentation such as ASCII character reference
314 charts, which were used mostly by IBM in the 1970s and 1980s for instruction
315 manuals (and in other documentation), and also on a few keyboard overlays.
316
317 @par Background
318
319 I created this class to make it easier to write internet server daemons. I
320 started out using C-style socket functions (because C++ doesn't come with a
321 socket class), but I found that I didn't enjoy mixing something as important
322 and detailed as socket I/O in a procedural way into the object-oriented
323 paradigm that C++ provides.
324
325 After looking for existing solutions (none of which served as comprehensive
326 replacements for socket I/O), I embarked on creating the rsocket class, and
327 then I began to understand why this probably hadn't been done -- it's a
328 massive undertaking, primarily because there are a lot of functions that are
329 needed to handle socket I/O. Further, [at the time of this writing] the @c
330 sockaddr_storage structure wasn't as widely used as it should be, and so
331 information about it tended to be scarce, incomplete, or incorrect (further
332 research, and diving down into some pretty deep "rabbit holes," was required
333 to understand this properly, which was worthwhile because it resulted in
334 having transparent support for IPv4 and IPv6 without breaking backward
335 compatibility for code expecting specific structures).
336
337 Moving error codes into exceptions is also a major effort because they are
338 diverse and plentiful, and there are so many errors that can occur at various
339 stages for many different reasons. There are also a few outlier functions
340 that require slightly different approaches to error handling due to subtly
341 different rules for handling their errors, and so the exception-generation
342 wasn't as straight-forward as one might optimistically expect, but this is
343 one of the many benefits of the object-oriented programming pardigm because
344 handling edge cases internally results in a consistent error-handling
345 interface using exceptions that also simplifies the source code. (Need to
346 handle a specific set of conditions? Catch the relevant exceptions for those
347 cases in an inner set of exceptions, and just catch all the others in a more
348 general way without the added complexity of repeatedly checking for errors
349 every step along the way.)
350
351 So, I dedicated time to make this work, and with the intention of making it
352 an open source project once I got it into a state that's ready for the
353 general public. This required putting my other C++ projects on hold, which
354 was fine because they didn't have strict deadlines and using this socket
355 class in them will speed up development in the long-term anyway, so it's
356 clearly worth the effort to me ... and I sincerely hope that my efforts will
357 be helpful to others too.
358
359 My background in programming began when I was a young child, teaching myself
360 BASIC and then machine language (when I found BASIC to be too limited) before
361 moving on to other languages like Perl and Java many years later. Eventually
362 I circled around to C (which I chose to learn the hard way by writing some
363 PostgreSQL extensions) and then C++ a few years after that. I have a lot of
364 experience with socket communications, including fully-featured DNS resolver
365 library code in machine language for Novell's NetWare that used C calling
366 conventions and supported varargs, which worked well for the few developers
367 who needed or wanted it.
368
369 @par History
370 - 2022-Nov-09 v1.00 Initial version
371 - 2022-Nov-26 v1.00 Moved exception handling to a separate class
372 - 2022-Nov-28 v1.00 Completed readline/send functionality
373 - 2022-Dec-03 v1.00 Added endianness transparency
374 - 2022-Dec-04 v1.00 Added printf() support
375 - 2022-Dec-24 v1.00 Added socket MUXing
376 - 2023-Feb-22 v1.00 Added TLS/SSL support
377 - 2023-Mar-10 v1.00 Added TLS-SNI support
378 - 2023-Apr-19 v1.00 Added TLS certificate loading from memory
379 - 2023-May-24 v1.00 Added support for clang++ compilation
380 - 2023-Jun-09 v1.00 Improvements to dynamic internal read buffering
381 - 2023-Oct-31 v1.00 Improvements to various classes
382 - 2024-Feb-21 v1.00 Added is_buffered() method
383 - 2024-Mar-31 v1.00 Completed @ref recvline (implemented a work-around for
384 a subtle SSL_peek failure to extract additional data
385 when a user at an end-point is communicating with
386 "icanon" mode enabled)
387 - 2024-May-24 v1.00 Changed source code to accomodate @c clang++ compiler
388 - 2024-Jun-04 v1.00 Added @c POSIX and @c TLS qualifiers to all applicable
389 methods (Doxygen incorporates into the documentation)
390 - 2024-Oct-23 v1.00 Various minor improvements to the documentation since
391 the previous update
392 - 2024-Nov-05 v1.00 Added eol_consumed_seq() method
393 - 2024-Nov-10 v1.00 Added discard() method
394 - 2024-Nov-17 v1.00 Added recv_rline() method
395 - 2024-Nov-19 v1.00 Added recv_as_string() method
396 @version 1.00
397 @author Randolf Richardson
398 *///=========================================================================
399 class rsocket {
400
401 // --------------------------------------------------------------------------
402 // The rsocket_group class needs access to some of our internal variables.
403 // --------------------------------------------------------------------------
404 friend class rsocket_group;
405
406 // --------------------------------------------------------------------------
407 // Socket variables.
408 // --------------------------------------------------------------------------
409 int __socket_fd = 0;
410 int __socket_type = 0;
411 int __socket_protocol = 0;
412 struct sockaddr_storage __socket_addr = {}; // Initialize to all elements to their default values
413 socklen_t __socket_addr_size = sizeof(__socket_addr); // We need to point to this (and it might be modified)
414 int __socket_backlog = SOMAXCONN; // Default backlog is 4096 on Linux, and 128 on older systems
415
416 // --------------------------------------------------------------------------
417 // Socket flags (internal, but with get-methods to provide read-only access).
418 // --------------------------------------------------------------------------
419 std::atomic_bool __socket_open = false; // Socket is open by way of socket() function
420 std::atomic_bool __socket_connected = false; // Socket is connected
421
422 // --------------------------------------------------------------------------
423 // TLS flags and variables.
424 // --------------------------------------------------------------------------
425 std::atomic_bool __tls = false; // Indicates whether socket I/O is encrypted with OpenSSL
426 SSL_CTX* __tls_ctx = nullptr; // TLS context (nullptr == no encryption initialized)
427 rsocket_sni* __tls_sni = nullptr; // SNI maps
428 bool __tls_sni_match = false; // Set to TRUE only if SNI matched in the SNI callback
429 SSL* __tls_fd = nullptr; // TLS connection structure (nullptr == no connection)
430 BIO* __tls_rbio = nullptr; // TLS BIO input stream
431 BIO* __tls_wbio = nullptr; // TLS BIO output stream
432 bool __tls_exclusive = false; // See enum TLS_FLAGS::TLS_EXCLUSIVE
433 bool __tls_egress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_EGRESS
434 bool __tls_ingress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_INGRESS
435 bool __tls_server_mode = true; // See enum TLS_FLAGS::TLS_SERVER
436 inline static std::atomic_int __fn_counter = 0; // Used in generating unique-and-threadsafe temporary filenames
437 rsocket* __tls_new_endpoint = nullptr; // Weak pointer to newly-spawned endpoint's rsocket object
438
439 // --------------------------------------------------------------------------
440 // Keep track of test for whether this host stores integers using big endian.
441 // --------------------------------------------------------------------------
442 inline static const bool __endian_is_msb = htons(42) == 42; // TRUE==MSB / FALSE==LSB
443
444 // --------------------------------------------------------------------------
445 // General variables. Atomic variables are used for variables as needed to
446 // support operations in a thread-safe manner.
447 // --------------------------------------------------------------------------
448 std::atomic_bool __debug = false;
449 std::atomic<std::FILE*> __debug_fd = stderr;
450 std::string __debug_prefix = "rsocket-debug";
451 #define RSOCKET_BUFFER_SIZE 1024 // Default buffer size
452 size_t __buffer_size = RSOCKET_BUFFER_SIZE; // Buffer size used by recv() when a buffer size isn't provided
453 char* __buffer = nullptr; // Initialized automatically when recvline() is used
454 char* __buffer_head = nullptr; // Newest data in buffer (a pointer is faster than calculating offsets)
455 char* __buffer_tail = nullptr; // Oldest data in buffer (a pointer is faster than calculating offsets)
456 char* __buffer_boundary = nullptr; // Size-wrapped boundary (a pointer is faster than calculating lengths)
457 std::string __name; // Arbitrary name of this rsocket (used by some applications)
458 std::string __name_sni; // SNI hostname (when tls_sni is configured)
459
460 // --------------------------------------------------------------------------
461 // EoL sequence variables.
462 // --------------------------------------------------------------------------
463 static const char __CR = (char)13; // CTRL-M / ASCII 13 (0Dh) / "\r" / Used by eol() methods
464 static const char __LF = (char)10; // CTRL-J / ASCII 10 (0Ah) / "\n" / Used by eol() methods
465 static constexpr const char* __CRLF = "\x0d\x0a"; // Used by eol() methods
466 std::string __eol; // Used by recvline() method; maintained by eol() methods
467 std::string __eol_consumed_seq; // The EoL sequence that was successfully received by the most recent use of the recvline() method
468 bool __eol_adoption = true; // Whether to adopt the dynamically detected EoL sequence when __eol is empty (the default)
469 std::string __eol_out = std::string(__CRLF); // Used by sendline() method; maintained by eol() methods; also used by sendline() method
470 bool __eol_fix_printf = true; // Whether to replace "\n" sequence in printf() methods to EoL sequence
471
472 // --------------------------------------------------------------------------
473 // Fine-tuning for recvline(). This is used with nanosleep().
474 // --------------------------------------------------------------------------
475 struct timespec __recvline_idle = {0, 999999999/3}; // Used by recvline() method; time to sleep between keystrokes (default to 1/8 of a second) to mitigate tight CPU high utilization loops
476 long __recvline_timeout = 0; // Number of seconds (0 = unlimited; maximum for "long" equates to approximately 292 billion years since the UNIX/Linux epoch)
477
478 // --------------------------------------------------------------------------
479 // Statistical variables.
480 // --------------------------------------------------------------------------
481 rsocket_io* __io_final_addr = nullptr; // Used by ~rsocket() destructor, net_io_final(), and net_io_update()
482 std::atomic_ulong __bytes_rx = 0; // Total number of bytes received
483 std::atomic_ulong __bytes_tx = 0; // Total number of bytes transmitted
484 std::atomic_ulong __crypt_rx = 0; // Total number of encrypted bytes recevied
485 std::atomic_ulong __crypt_tx = 0; // Total number of encrypted bytes transmitted
486
487 public:
488 /*======================================================================*//**
489 @brief
490 Optional flags used with various methods to determine whether they will throw
491 an @ref randolf::rex::xETIMEDOUT exception or merely return a @c nullptr, an
492 empty set, or a 0 (zero) when a timeout duration elapses.
493
494 If you're not sure of whether to use @ref TIMEOUT_EMPTY, @ref TIMEOUT_NULL,
495 or @ref TIMEOUT_ZERO in your code, then I suggest using @ref TIMEOUT_NULL
496 since @c NULL will likely be the most recognizable in most code reviews.
497
498 @note
499 You'll know when this is an option because the method will support this.
500 *///=========================================================================
501 enum TIMEOUT_BEHAVIOUR: bool {
502
503 /*----------------------------------------------------------------------*//**
504 Indicate that an @ref randolf::rex::xETIMEDOUT exception should be thrown
505 when the timeout duration elapses.
506 *///-------------------------------------------------------------------------
507 TIMEOUT_EXCEPTION = true,
508
509 /*----------------------------------------------------------------------*//**
510 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
511 when the timeout duration elapses.
512 *///-------------------------------------------------------------------------
513 TIMEOUT_EMPTY = false,
514
515 /*----------------------------------------------------------------------*//**
516 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
517 when the timeout duration elapses.
518 *///-------------------------------------------------------------------------
519 TIMEOUT_NULL = false,
520
521 /*----------------------------------------------------------------------*//**
522 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
523 when the timeout duration elapses.
524 *///-------------------------------------------------------------------------
525 TIMEOUT_ZERO = false,
526
527 }; // -x- enum TIMEOUT_BEHAVIOUR -x-
528
529 /*======================================================================*//**
530 @brief
531 Optional flags used with rsocket's @ref tls() and @ref tls_ctx() methods to
532 specify relevant policies/semantics.
533 *///=========================================================================
534 enum TLS_FLAGS: int {
535
536 /*----------------------------------------------------------------------*//**
537 The TLS_DEFAULT flag isn't necessary, but it's included here for completeness
538 as it accomodates programming styles that prefer to emphasize when defaults
539 are being relied upon.
540 *///-------------------------------------------------------------------------
541 TLS_DEFAULT = 0,
542
543 /*----------------------------------------------------------------------*//**
544 Only encrypted connections are permitted, initially, and all attempts to
545 begin with unencrypted connections will consistently fail.
546
547 Encrypted connections must begin with a cryptographic handshake packet, or
548 else the connection will be rejected as due to being reasonably assumed to be
549 "not encrypted."
550 @post
551 If this flag isn't set, an application-level mechanism can be used to upgrade
552 to TLS encryption (this is common with connections that begin unencrypted,
553 and issue a proprietary command {such as the @c STARTTLS command in SMTP} to
554 upgrade to TLS later on {of which the @ref tls() method is used to affect an
555 ingress; see the @ref TLS_NO_INGRESS flag for additional information}).
556 @note
557 Creating an exclusively @c unencrypted connection is accomplished by not
558 initializing TLS.
559 @see tls()
560 @see TLS_NO_EGRESS to prevent support for application-level downgrades to
561 unencrypted connections
562 *///-------------------------------------------------------------------------
563 TLS_EXCLUSIVE = 1,
564
565 /*----------------------------------------------------------------------*//**
566 Mid-stream upgrades to encrypted connections are not permitted (e.g., via
567 application-level initiations like the @c STARTTLS command as seen in SMTP),
568 which will also cause calls to the @ref tls() method to fail fast after plain
569 non-encrypted data has already been sent or received.
570 @pre
571 This flag is not necessary when the @ref TLS_EXCLUSIVE flag is set.
572 @see is_tls_ingress_okay()
573 @see tls_do_handshake()
574 @see TLS_NO_EGRESS
575 *///-------------------------------------------------------------------------
576 TLS_NO_INGRESS = 2,
577
578 /*----------------------------------------------------------------------*//**
579 Mid-stream downgrades to unencrypted connections are not permitted (e.g., via
580 application-level initiations like a hypothetical @c STOPTLS command as seen
581 in FTPS), which will also cause calls to the @ref tls() method to fail fast
582 after encrypted data has already been sent or received.
583 @pre
584 This flag may be combined with the @ref TLS_EXCLUSIVE flag to prevent a
585 connection from being downgraded programatically.
586 @note
587 Although egress to an unencrypted connection doesn't occur automatically
588 (since egress can only be affected programatically to support commands at the
589 application level), this flag is useful to prevent third-party code from
590 downgrading an encrypted @ref rsocket to unencrypted.
591 @warning
592 Supporting unencrypted communications is strongly discouraged over public
593 networks (e.g., the internet) because unencrypted streams are trivially
594 susceptible to man-in-the-middle attacks that can alter the contents of the
595 data in both directions (which is a particularly dangerous prospect for
596 sending/receiving sensitive information).
597 @see is_tls_egress_okay()
598 @see TLS_NO_INGRESS
599 *///-------------------------------------------------------------------------
600 TLS_NO_EGRESS = 4,
601
602 /*----------------------------------------------------------------------*//**
603 This is a convenience flag that provides an option for developers to be more
604 clear in their use of the @ref tls() and @ref tls_ctx() methods to indicate
605 intent to rely on what is already the default.
606 @see tls_do_handshake()
607 @see TLS_SERVER
608 *///-------------------------------------------------------------------------
609 TLS_CLIENT = 0,
610
611 /*----------------------------------------------------------------------*//**
612 Indicates that this rsocket will be for a server daemon, and to initialize a
613 new TLS context (when one isn't being provided) using OpenSSL's
614 @c TLS_server_method() function instead of OpenSSL's @c TLS_client_method()
615 function (the latter is the default because most code is anticipated to be
616 client-oriented).
617
618 @attention
619 Setting the CLIENT/SERVER flag incorrectly will typically cause the following
620 error that's difficult to track down, which is usually triggered by calling
621 @ref accept, @ref accept4(), or @ref connect(), and so I hope that including
622 this information here in this documentation will be helpful:
623 @verbatim
624 error:140C5042:SSL routines:ssl_undefined_function:called a function you should not call
625 @endverbatim
626
627 The absence of this flag has the same effect as specifying the @ref
628 TLS_CLIENT flag.
629 @see TLS_CLIENT
630 *///-------------------------------------------------------------------------
631 TLS_SERVER = 8,
632
633 }; // -x- enum TLS_FLAGS -x-
634
635 private:
636 /*======================================================================*//**
637 Return Code check, and throws an rsocket-specific exception if an error
638 occurred, which is indicated by @c -1 (a lot of example code shows @c < @c 1
639 comparisons, but we specifically test for @c -1 because the documentation
640 clearly states @c -1. This is important because a system that can support an
641 extremely high number of socket handles might, in theory, assign handles with
642 values that get interpreted as negative integers, and so less-than-zero tests
643 would result in dropped packets or dropped sockets (any such socket code that
644 allocates such handles obviously must not ever allocate @c -1 since this
645 would definitely be misinterpreted as an error).
646
647 If rc is not @c -1, then it is simply returned as is.
648
649 If n is nonzero and rc is 0, then n will override errno. (This option is
650 provided to accomodate the few socket library functions that return values
651 that are never errors, and expect the developer to rely on other means of
652 detecting whether an error occurred. This is an example of where Object
653 Oriented Programming is helpful in making things better.)
654 @returns Original value of @c rc (if no exceptions were thrown)
655 *///=========================================================================
656 const int __rc_check(
657 /// Return code
658 const int rc,
659 /// Override @c errno (if not 0)
660 const int n = 0,
661 /// Exception handling flags (see @ref rex::REX_FLAGS for details)
662 const int flags = rex::rex::REX_FLAGS::REX_DEFAULT) {
663 if (rc == -1 || (rc == 0 && n != 0)) {
664 const int socket_errno = n == 0 ? errno : n; // If "n" is not zero, use it instead
665 if (__debug) debug("__rc_check(" + std::to_string(rc) + ", " + std::to_string(n) + ", " + std::to_string(flags) + ") throwing exception (errno=" + std::to_string(socket_errno) + ")"); // TODO: Remove this
666 randolf::rex::mk_exception("", socket_errno, flags); // This function doesn't return (this code ends here)
667 } // -x- if rc -x-
668 return rc;
669 }; // -x- int __rc_check -x-
670
671 /*======================================================================*//**
672 Similar to @ref __rc_check(), but specialized for handling OpenSSL return
673 codes.
674 *///=========================================================================
675 const int __rc_check_tls(
676 /// Return code (from OpenSSL's API functions)
677 const int rc) {
678 if (rc <= 0) {
679 if (__debug) debug("__rc_check_tls(" + std::to_string(rc) + ") throwing exception"); // TODO: Remove this
680 randolf::rex::mk_exception("", SSL_get_error(__tls_fd, rc), rex::rex::REX_FLAGS::REX_TLS); // This function doesn't return (this code ends here)
681 } // -x- if rc -x-
682 return rc;
683 }; // -x- int __rc_check -x-
684
685 /*======================================================================*//**
686 Internal function that opens the socket. This is used by the constructors
687 and their accompanying socket() methods.
688
689 Throws randolf::rex::xEALREADY exception if the socket is already open to
690 prevent multiple calls to @c open, which can be particularly problematic if
691 any of the additional function calls specify different parameters -- this
692 helps developer(s) to avoid unexpected results if they're inadvertently using
693 the same rsocket object when they intend to use different rsocket objects.
694 *///=========================================================================
695 void __socket(
696 /// Communication domain; usually one of:@n
697 /// AF_INET (IPv4)@n
698 /// AF_INET6 (IPv6)@n
699 /// AF_UNIX (UNIX domain sockets)
700 const int family,
701 /// Communication semantics; usually one of:@n
702 /// SOCK_STREAM (common for TCP)@n
703 /// SOCK_DGRAM (common for UDP)
704 const int type,
705 /// Network protocol; usually one of:@n
706 /// IPPROTO_TCP@n
707 /// IPPROTO_UDP@n
708 /// IPPROTO_IP
709 const int protocol) {
710 if (__debug) debug("socket(" + std::to_string(family)
711 + ", " + std::to_string(type)
712 + ", " + std::to_string(protocol)
713 + ");");
714
715 // --------------------------------------------------------------------------
716 // Syntax checks.
717 // --------------------------------------------------------------------------
718 if (__socket_open) throw randolf::rex::xEALREADY("EALREADY: Socket is already open"); // Socket has already been opened
719 if (family == AF_UNSPEC) throw randolf::rex::xEAFNOSUPPORT("EAFNOSUPPORT: AF_UNSPEC family (SO_DOMAIN) is not supported by socket()");
720
721 // --------------------------------------------------------------------------
722 // Build minimum parts of __socket_addr structure.
723 // --------------------------------------------------------------------------
724 __socket_addr.ss_family = family;
725 __socket_type = type;
726 __socket_protocol = protocol;
727
728 // --------------------------------------------------------------------------
729 // Create new socket handle, and save it, then set internal variable to
730 // indicate that the socket is open.
731 // --------------------------------------------------------------------------
732 __socket_fd = __rc_check(::socket(__socket_addr.ss_family, __socket_type, __socket_protocol));
733 __socket_open = true;
734
735 }; // -x- void __socket -x-
736
737 public:
738 /*======================================================================*//**
739 @brief
740 Instantiate an empty rsocket without actually opening a socket, and therefore
741 also without throwing any exceptions (useful in header-file definitions).
742
743 @details
744 Instantiating an empty rsocket is particularly useful for header-file
745 definitions since exceptions can't be handled outside of subroutines, and
746 it's also useful for enabling debug() mode @em before setting the socket's
747 configuration with one of the socket() methods; for example:
748 @code{.cpp}
749 randolf::rsocket r; // Empty rsocket (empty / incomplete initialization)
750 r.debug(true); // Enable debug mode
751 r.socket(...); // Required to complete rsocket initialization
752 @endcode
753
754 @par Notes
755
756 The built-in defaults, when not provided, are as follows ("family" is also
757 known as the "communication domain"):
758 - @c family = AF_INET
759 - @c type = SOCK_STREAM
760 - @c protocol = PF_UNSPEC
761
762 You will need to use one of the socket(...) methods to specify socket details
763 after defining rsocket objects with empty constructors so that you can catch
764 runtime exceptions. (This also provides you with an option to enable debug
765 mode during runtime prior to attempting to open an rsocket.)
766
767 @par Examples
768
769 @code{.cpp}
770 #include <iostream> // std::cout, std::cerr, std::endl, etc.
771 #include <randolf/rex>
772 #include <randolf/rsocket>
773
774 randolf::rsocket r; // <-- Empty rsocket initialization (no exceptions)
775
776 int main(int argc, char *argv[]) {
777 try {
778 r.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Required to complete rsocket initialization
779 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
780 // ... other socket I/O operations
781 r.close();
782 } catch (const randolf::rex::xALL e) {
783 std::cerr << "Socket exception: " << e.what() << std::endl;
784 return EXIT_FAILURE;
785 } catch (const std::exception e) {
786 std::cerr << "Other exception: " << e.what() << std::endl;
787 return EXIT_FAILURE;
788 }
789 return EXIT_SUCCESS;
790 } // -x- int main -x-
791 @endcode
792 @see rsocket()
793 @see socket()
794 @see socket_family()
795 @see socket_fd()
796 @see socket_protocol()
797 @see socket_type()
798 @qualifier TLS
799 *///=========================================================================
800 rsocket() noexcept {}; // -x- constructor rsocket -x-
801
802 /*======================================================================*//**
803 @brief
804 Instantiate an rsocket based on a minimal subset of the settings in the
805 specified rsocket (using it as a template), without actually opening a
806 socket, and therefore also without throwing any exceptions.
807
808 @note
809 This constructor does not suffice as a full clone()-like operation, and is
810 minimal because it's used internally by the @ref accept() and @ref accept4()
811 methods.
812
813 Details that are absorbed from the template/source rsocket (which eliminates
814 the need to assign, set, and configure various parameters (TLS and TLS SNI
815 parameters will be copied in a passive way by default):
816 - Socket family (SO_DOMAIN)
817 - Socket type (SO_TYPE)
818 - Socket protocol (SO_PROTOCOL)
819 - TLS details (status, context {which includes loaded certificates},
820 policies specified with @ref TLS_FLAGS, etc.)
821 - EoL details
822 - TLS context (you'll still need to call `tls_ctx(r->tls_ctx())`)
823 - TLS SNI map (you'll still need to call `tls_sni(r->tls_sni())`)
824
825 @post
826 The TLS Context will not be initialized because it needs a real socket to
827 draw from. If using TLS, you'll need to use the @ref tls() method after
828 the underlying socket has been initiated.
829
830 When @c flag_create_socket is set to FALSE, no exceptions will be thrown.
831
832 @see rsocket()
833 @see socket()
834 @see socket_family()
835 @see socket_fd()
836 @see socket_protocol()
837 @see socket_type()
838 @qualifier TLS
839 *///=========================================================================
840 rsocket(
841 /// Source rsocket object to use as a template to absorb settings from
842 const rsocket* rtemplate,
843 /// TRUE = create a new socket handle (default)@n
844 /// FALSE = don't create a new socket because a new one will be assigned or
845 /// created later (all variants of the @ref accept() methods do this)
846 const bool flag_create_socket = true) {
847
848 // --------------------------------------------------------------------------
849 // General socket variables.
850 // --------------------------------------------------------------------------
851 if (flag_create_socket) {
852 __socket(rtemplate->__socket_addr.ss_family,
853 rtemplate->__socket_type,
854 rtemplate->__socket_protocol);
855 } else { // !flag_create_socket
856 __socket_addr.ss_family = rtemplate->__socket_addr.ss_family;
857 __socket_type = rtemplate->__socket_type;
858 __socket_protocol = rtemplate->__socket_protocol;
859 } // -x- if flag_create_socket -x-
860 __name = rtemplate->__name;
861
862 // --------------------------------------------------------------------------
863 // TLS and SNI settings, but not whether TLS is enabled.
864 // --------------------------------------------------------------------------
865 __tls_ctx = rtemplate->__tls_ctx;
866 __tls_sni = rtemplate->__tls_sni;
867
868 // --------------------------------------------------------------------------
869 // EoL variables.
870 // --------------------------------------------------------------------------
871 __eol.assign( rtemplate->__eol); // Copy source std::string's contents (not a reference)
872 __eol_fix_printf = rtemplate->__eol_fix_printf;
873
874 }; // -x- constructor rsocket -x-
875
876 /*======================================================================*//**
877 @brief
878 Instantiate an rsocket with IP/host address and [optional] port number.
879
880 This is either the endpoint that our underlying socket will be connecting to,
881 or it's the local address of the server daemon that our socket will listen()
882 to and accept() inbound connections from.
883
884 @par Notes
885
886 The built-in defaults, when not provided, are as follows ("family" is also
887 known as the "communication domain"):
888 - @c family = AF_INET
889 - @c type = SOCK_STREAM
890 - @c protocol = PF_UNSPEC
891
892 The socket() methods do the same work as the constructors with matching
893 arguments, and are provided as convenience methods intended to augment
894 empty rsocket constructors used in header files, but do require an address to
895 be specified (for protocols that need port numbers, such as TCP or UDP, a
896 "port" number also needs to be specified since the default port 0 will result
897 in the dynamic allocation of a port number by the system).
898
899 For UNIX domain sockets use family AF_UNIX, type SOCK_STREAM, and protocol
900 IPPROTO_IP when instantiating or opening an rsocket.
901
902 @par Examples
903
904 @code{.cpp}
905 #include <iostream> // std::cout, std::cerr, std::endl, etc.
906 #include <randolf/rex>
907 #include <randolf/rsocket>
908
909 int main(int argc, char *argv[]) {
910 try {
911 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Same as socket() method
912 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
913 r.bind("127.0.0.1", 32768);
914 // ... other socket I/O operations
915 r.close();
916 } catch (const randolf::rex::xALL e) {
917 std::cerr << "Socket exception: " << e.what() << std::endl;
918 return EXIT_FAILURE;
919 } catch (const std::exception e) {
920 std::cerr << "Other exception: " << e.what() << std::endl;
921 return EXIT_FAILURE;
922 }
923 return EXIT_SUCCESS;
924 } // -x- int main -x-
925 @endcode
926
927 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
928 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
929 @throws randolf::rex::xEINVAL Protocal family invalid or not available
930 @throws randolf::rex::xEINVAL Invalid flags in type
931 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
932 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
933 @throws randolf::rex::xENOBUFS Insufficient memory
934 @throws randolf::rex::xENOMEM Insufficient memory
935 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
936 supported within the specified family (a.k.a., communication domain)
937 @see rsocket()
938 @see socket()
939 @qualifier TLS
940 *///=========================================================================
941 rsocket(
942 /// Communication domain; usually one of:@n
943 /// AF_INET (IPv4)@n
944 /// AF_INET6 (IPv6)@n
945 /// AF_UNIX (UNIX domain sockets)
946 const int family,
947 /// Communication semantics; usually one of:@n
948 /// SOCK_STREAM (common for TCP)@n
949 /// SOCK_DGRAM (common for UDP)
950 const int type = SOCK_STREAM,
951 /// Network protocol; usually one of:@n
952 /// IPPROTO_TCP@n
953 /// IPPROTO_UDP@n
954 /// IPPROTO_IP@n
955 /// PF_UNSPEC (auto-detect)
956 const int protocol = PF_UNSPEC) {
957 __socket(family, type, protocol);
958 }; // -x- constructor rsocket -x-
959
960 /*======================================================================*//**
961 @brief
962 Destructor, which closes any underlying sockets, frees any TLS structures
963 that were allocated by OpenSSL, and performs any other necessary clean-up
964 before finally copying the I/O statistics to a designated structure (if one
965 was specified with the @ref net_io_final() method).
966 @attention
967 Developers should take care to check that the @ref rsocket_io::is_final flag
968 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
969 since there's no guarantee that the destructor will necessarily be executed
970 in a timely manner (this flag is set last, after all other statistics are
971 copied into the structure while in a mutex-locked state).
972 @see net_io_final()
973 @qualifier TLS
974 *///=========================================================================
975 ~rsocket() noexcept {
976
977 // --------------------------------------------------------------------------
978 // Debug.
979 // --------------------------------------------------------------------------
980 if (__debug) {
981 debug("destructor(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
982 + ");");
983 debug(false); // Disable debug mode (might trigger extra clean-up in future)
984 } // -x- if _debug -x-
985
986 // --------------------------------------------------------------------------
987 // Free memory and resources and close handles that need to be freed/closed.
988 // --------------------------------------------------------------------------
989 if (__tls_fd != nullptr) SSL_free(__tls_fd);
990// if (__tls_sni != nullptr) tls_sni(nullptr); // Remove any SNI callbacks // WE DON'T NEED THIS
991
992// if (__tls_ctx != nullptr) SSL_CTX_free(__tls_ctx); // TODO: Research using SSL_up_ref(__tls_ctx)
993 if (__socket_fd != 0) ::close(__socket_fd);
994 if (__buffer != nullptr) ::free(__buffer);
995// if (__buffer != nullptr) delete []__buffer;
996
997 // --------------------------------------------------------------------------
998 // Copy statistics to final location. (We do this last in case there's an
999 // issue with locking; there shouldn't be, but if there is then at least we
1000 // already we're not inadvertently hanging on to resources at this point that
1001 // need to be freed/closed anyway.)
1002 // --------------------------------------------------------------------------
1003 if (__io_final_addr != nullptr) {
1004 __io_final_addr->lock();
1005 __io_final_addr->bytes_rx = __bytes_rx;
1006 __io_final_addr->bytes_tx = __bytes_tx;
1007 // ->bytes_xx = <bytes in buffer; these are lost bytes that should be subtracted from __bytes_rx>
1008 __io_final_addr->crypt_rx = __crypt_rx;
1009 __io_final_addr->crypt_tx = __crypt_tx;
1010 // ->crypt_xx = <bytes in buffer; these are lost bytes that should be subtracted from __crypt_rx>
1011 __io_final_addr->is_final = true;
1012 __io_final_addr->unlock();
1013 } // -x- if __io_final_addr -x-
1014
1015 }; // -x- destructor ~rsocket -x-
1016
1017 /*======================================================================*//**
1018 @brief
1019 Accept new [inbound] socket connetions. (This is typically used in a loop.)
1020
1021 @pre
1022 The resulting rsocket object is created before the actual call to the @c
1023 accept() function.
1024
1025 @note
1026 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1027 select(), and accept()/accept4() when using multiple sockets.
1028
1029 @post
1030 To prevent resource leaks, the resulting rsocket needs to be deleted after
1031 it's no longer needed.
1032
1033 @throws randolf::rex::xEBADF The underlying socket is not open
1034 @throws randolf::rex::xECONNABORTED The connection was aborted
1035 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1036 part of the user address space
1037 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1038 @throws randolf::rex::xEHOSTUNREACH No route to host
1039 @throws randolf::rex::xEINTR Interrupted by a signal
1040 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1041 length of the address is invalid
1042 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1043 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1044 @throws randolf::rex::xENETUNREACH No route to network
1045 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1046 @throws randolf::rex::xENOBUFS Insufficient memory
1047 @throws randolf::rex::xENOMEM Insufficient memory
1048 @throws randolf::rex::xENONET Requested host is not reachable on the network
1049 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1050 is not supported
1051 @throws randolf::rex::xENOSR System ran out of stream resources
1052 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1053 doesn't refer to a socket
1054 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1055 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1056 @throws randolf::rex::xEPROTO Protocol error
1057 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1058 supported within the specified family (a.k.a., communication domain)
1059 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1060 a system call is restartable and can be intercepted-and-redirected
1061 (there is no need to catch this exception unless you are an advanced
1062 developer, in which case you likely still won't need to catch it in
1063 code at this level)
1064 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1065 supported within the specified family (a.k.a., communication domain)
1066 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1067 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1068 no inbound connections are waiting
1069
1070 @returns Newly-created rsocket object representing the connection received
1071 @see accept_sp()
1072 @see accept4()
1073 @see accept4_sp()
1074 @see listen
1075 @qualifier POSIX
1076 @qualifier TLS
1077 *///=========================================================================
1078 rsocket* accept() {
1079 if (__debug) debug("accept(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1080 + ");");
1081
1082 // --------------------------------------------------------------------------
1083 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1084 // new incoming connection.
1085 // --------------------------------------------------------------------------
1086 __tls_new_endpoint = new rsocket(this, false);
1087
1088 // --------------------------------------------------------------------------
1089 // Wait for incoming connection, and update internal flags in client rsocket.
1090 // --------------------------------------------------------------------------
1091 __tls_new_endpoint->__socket_fd = ::accept(__socket_fd, (sockaddr*)&__tls_new_endpoint->__socket_addr, &__tls_new_endpoint->__socket_addr_size);
1092 if (__tls_new_endpoint->__socket_fd == -1) {
1093 delete __tls_new_endpoint; // Memory management
1094 //__tls_new_endpoint = nullptr;
1095 __rc_check(-1); // This part throws the exception
1096 } // -x- if rc -x-
1097 __tls_new_endpoint->__socket_open = true;
1098 __tls_new_endpoint->__socket_connected = true;
1099
1100 // --------------------------------------------------------------------------
1101 // Perform TLS accept() as well, but only if TLS is enabled.
1102 //
1103 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1104 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1105 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1106 // --------------------------------------------------------------------------
1107 if (__tls) {
1108 __tls_new_endpoint->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1109 __tls_new_endpoint->tls(true); // Make sure __tls_fd is configured correctly
1110 __rc_check_tls(SSL_accept(__tls_new_endpoint->__tls_fd));
1111 } // -x- if __tls -x-
1112
1113 return __tls_new_endpoint;
1114 }; // -x- rsocket* accept -x-
1115
1116 /*======================================================================*//**
1117 @brief
1118 Accept new [inbound] socket connetions. (This is typically used in a loop.)
1119
1120 @pre
1121 The resulting rsocket object is created before the actual call to the @c
1122 accept() function.
1123
1124 @note
1125 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1126 select(), and accept()/accept4() when using multiple sockets.
1127
1128 @post
1129 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1130 aids in the prevention of resource leaks).
1131
1132 @throws randolf::rex::xEBADF The underlying socket is not open
1133 @throws randolf::rex::xECONNABORTED The connection was aborted
1134 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1135 part of the user address space
1136 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1137 @throws randolf::rex::xEHOSTUNREACH No route to host
1138 @throws randolf::rex::xEINTR Interrupted by a signal
1139 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1140 length of the address is invalid
1141 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1142 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1143 @throws randolf::rex::xENETUNREACH No route to network
1144 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1145 @throws randolf::rex::xENOBUFS Insufficient memory
1146 @throws randolf::rex::xENOMEM Insufficient memory
1147 @throws randolf::rex::xENONET Requested host is not reachable on the network
1148 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1149 is not supported
1150 @throws randolf::rex::xENOSR System ran out of stream resources
1151 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1152 doesn't refer to a socket
1153 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1154 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1155 @throws randolf::rex::xEPROTO Protocol error
1156 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1157 supported within the specified family (a.k.a., communication domain)
1158 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1159 a system call is restartable and can be intercepted-and-redirected
1160 (there is no need to catch this exception unless you are an advanced
1161 developer, in which case you likely still won't need to catch it in
1162 code at this level)
1163 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1164 supported within the specified family (a.k.a., communication domain)
1165 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1166 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1167 no inbound connections are waiting
1168
1169 @returns Newly-created rsocket object representing the connection received,
1170 wrapped in std::shared_ptr (a C++ smart pointer)
1171 @see accept()
1172 @see accept4()
1173 @see accept4_sp()
1174 @see listen
1175 @qualifier TLS
1176 *///=========================================================================
1177 std::shared_ptr<rsocket> accept_sp() {
1178 if (__debug) debug("accept_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1179 + ");");
1180
1181 // --------------------------------------------------------------------------
1182 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1183 // new incoming connection.
1184 // --------------------------------------------------------------------------
1185 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1186 __tls_new_endpoint = r.get(); // The SNI callback needs this
1187
1188 // --------------------------------------------------------------------------
1189 // Wait for incoming connection, and update internal flags in client rsocket.
1190 // --------------------------------------------------------------------------
1191 r->__socket_fd = __rc_check(::accept(__socket_fd, (sockaddr*)&r->__socket_addr, &r->__socket_addr_size));
1192 r->__socket_open = true;
1193 r->__socket_connected = true;
1194
1195 // --------------------------------------------------------------------------
1196 // Perform TLS accept() as well, but only if TLS is enabled.
1197 //
1198 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1199 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1200 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1201 // --------------------------------------------------------------------------
1202 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1203 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1204 r->tls(true); // Make sure __tls_fd is configured correctly
1205 __rc_check_tls(SSL_accept(r->__tls_fd));
1206 } // -x- if __tls -x-
1207
1208 return r;
1209 }; // -x- std::shared_ptr<rsocket> accept_sp -x-
1210
1211 /*======================================================================*//**
1212 @brief
1213 Accept new [inbound] socket connetions, with socket flags specified. (This
1214 is typically used in a loop.)
1215
1216 @pre
1217 The resulting rsocket object is created before the actual call to the @c
1218 accept4() function.
1219
1220 @note
1221 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1222 select(), and accept()/accept4() when using multiple sockets.
1223
1224 @post
1225 To prevent resource leaks, the resulting rsocket needs to be deleted after
1226 it's no longer needed.
1227
1228 @throws randolf::rex::xEBADF The underlying socket is not open
1229 @throws randolf::rex::xECONNABORTED The connection was aborted
1230 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1231 part of the user address space
1232 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1233 @throws randolf::rex::xEHOSTUNREACH No route to host
1234 @throws randolf::rex::xEINTR Interrupted by a signal
1235 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1236 length of the address is invalid
1237 @throws randolf::rex::xEINVAL Invalid value in flags
1238 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1239 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1240 @throws randolf::rex::xENETUNREACH No route to network
1241 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1242 @throws randolf::rex::xENOBUFS Insufficient memory
1243 @throws randolf::rex::xENOMEM Insufficient memory
1244 @throws randolf::rex::xENONET Requested host is not reachable on the network
1245 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1246 is not supported
1247 @throws randolf::rex::xENOSR System ran out of stream resources
1248 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1249 doesn't refer to a socket
1250 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1251 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1252 @throws randolf::rex::xEPROTO Protocol error
1253 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1254 supported within the specified family (a.k.a., communication domain)
1255 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1256 a system call is restartable and can be intercepted-and-redirected
1257 (there is no need to catch this exception unless you are an advanced
1258 developer, in which case you likely still won't need to catch it in
1259 code at this level)
1260 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1261 supported within the specified family (a.k.a., communication domain)
1262 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1263 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1264 no inbound connections are waiting
1265
1266 @returns Newly-created rsocket object representing the connection received
1267 @see accept()
1268 @see accept_sp()
1269 @see accept4_sp()
1270 @see listen
1271 @qualifier POSIX
1272 @qualifier TLS
1273 *///=========================================================================
1274 rsocket* accept4(
1275 /// SOCK_NONBLOCK@n SOCK_CLOEXEC
1276 const int flags = 0) {
1277 if (__debug) debug("accept4(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1278 + ", " + std::to_string(flags)
1279 + ");");
1280
1281 // --------------------------------------------------------------------------
1282 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1283 // new incoming connection.
1284 // --------------------------------------------------------------------------
1285 rsocket* __tls_new_endpoint = new rsocket(this, false);
1286
1287 // --------------------------------------------------------------------------
1288 // Wait for incoming connection, and update internal flags in client rsocket.
1289 // --------------------------------------------------------------------------
1290 __tls_new_endpoint->__socket_fd = ::accept4(__socket_fd, (sockaddr*)&__tls_new_endpoint->__socket_addr, &__tls_new_endpoint->__socket_addr_size, flags);
1291 if (__tls_new_endpoint->__socket_fd == -1) {
1292 delete __tls_new_endpoint; // Memory management
1293 __rc_check(-1); // This part throws the exception
1294 } // -x- if rc -x-
1295 __tls_new_endpoint->__socket_open = true;
1296 __tls_new_endpoint->__socket_connected = true;
1297
1298 // --------------------------------------------------------------------------
1299 // Perform TLS accept() as well, but only if TLS is enabled.
1300 //
1301 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1302 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1303 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1304 // --------------------------------------------------------------------------
1305 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1306 __tls_new_endpoint->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1307 __tls_new_endpoint->tls(true); // Make sure __tls_fd is configured correctly
1308 __rc_check_tls(SSL_accept(__tls_new_endpoint->__tls_fd));
1309 } // -x- if __tls -x-
1310
1311 return __tls_new_endpoint;
1312 }; // -x- rsocket* accept4 -x-
1313
1314 /*======================================================================*//**
1315 @brief
1316 Accept new [inbound] socket connetions, with socket flags specified. (This
1317 is typically used in a loop.)
1318
1319 @pre
1320 The resulting rsocket object is created before the actual call to the @c
1321 accept4() function.
1322
1323 @note
1324 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1325 select(), and accept()/accept4() when using multiple sockets.
1326
1327 @post
1328 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1329 aids in the prevention of resource leaks).
1330
1331 @throws randolf::rex::xEBADF The underlying socket is not open
1332 @throws randolf::rex::xECONNABORTED The connection was aborted
1333 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1334 part of the user address space
1335 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1336 @throws randolf::rex::xEHOSTUNREACH No route to host
1337 @throws randolf::rex::xEINTR Interrupted by a signal
1338 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1339 length of the address is invalid
1340 @throws randolf::rex::xEINVAL Invalid value in flags
1341 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1342 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1343 @throws randolf::rex::xENETUNREACH No route to network
1344 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1345 @throws randolf::rex::xENOBUFS Insufficient memory
1346 @throws randolf::rex::xENOMEM Insufficient memory
1347 @throws randolf::rex::xENONET Requested host is not reachable on the network
1348 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1349 is not supported
1350 @throws randolf::rex::xENOSR System ran out of stream resources
1351 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1352 doesn't refer to a socket
1353 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1354 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1355 @throws randolf::rex::xEPROTO Protocol error
1356 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1357 supported within the specified family (a.k.a., communication domain)
1358 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1359 a system call is restartable and can be intercepted-and-redirected
1360 (there is no need to catch this exception unless you are an advanced
1361 developer, in which case you likely still won't need to catch it in
1362 code at this level)
1363 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1364 supported within the specified family (a.k.a., communication domain)
1365 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1366 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1367 no inbound connections are waiting
1368
1369 @returns Newly-created rsocket object representing the connection received,
1370 wrapped in std::shared_ptr (a C++ smart pointer)
1371 @see accept()
1372 @see accept_sp()
1373 @see accept4()
1374 @see listen
1375 @qualifier TLS
1376 *///=========================================================================
1377 std::shared_ptr<rsocket> accept4_sp(
1378 /// SOCK_NONBLOCK@n SOCK_CLOEXEC
1379 const int flags = 0) {
1380 if (__debug) debug("accept4_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1381 + ", " + std::to_string(flags)
1382 + ");");
1383
1384 // --------------------------------------------------------------------------
1385 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1386 // new incoming connection.
1387 // --------------------------------------------------------------------------
1388 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1389 __tls_new_endpoint = r.get(); // The SNI callback needs this
1390
1391 // --------------------------------------------------------------------------
1392 // Wait for incoming connection, and update internal flags in client rsocket.
1393 // --------------------------------------------------------------------------
1394 r->__socket_fd = __rc_check(::accept4(__socket_fd, (sockaddr*)&r->__socket_addr, &r->__socket_addr_size, flags));
1395 r->__socket_open = true;
1396 r->__socket_connected = true;
1397
1398 // --------------------------------------------------------------------------
1399 // Perform TLS accept() as well, but only if TLS is enabled.
1400 //
1401 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1402 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1403 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1404 // --------------------------------------------------------------------------
1405 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1406 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1407 r->tls(true); // Make sure __tls_fd is configured correctly
1408 __rc_check_tls(SSL_accept(r->__tls_fd));
1409 } // -x- if __tls -x-
1410// if (__tls_ctx != nullptr) { r->tls(true); r->__tls = (bool)__tls; } // Make sure __tls_fd is allocated appropriately
1411
1412 return r;
1413 }; // -x- std::shared_ptr<rsocket> accept4_sp -x-
1414
1415 /*======================================================================*//**
1416 @brief
1417 Override the default @ref listen backlog for this rsocket.
1418
1419 @note
1420 The default backlog is SOMAXCONN (4096 on Linux, and 128 on older systems).
1421
1422 @returns The same rsocket object so as to facilitate stacking
1423 @see listen
1424 @qualifier TLS
1425 *///=========================================================================
1426 rsocket* backlog(
1427 /// Backlog queue size (0 = use SOMAXCONN, which is the system default of
1428 /// 4096 on Linux, and 128 on older systems)
1429 int backlog = 0) noexcept {
1430 __socket_backlog = backlog == 0 ? SOMAXCONN : backlog;
1431 return this;
1432 }; // -x- rsocket* backlog -x-
1433
1434 /*======================================================================*//**
1435 @brief
1436 Find out what this rsocket's default listen backlog is.
1437 @returns The default listen backlog
1438 @see listen
1439 @qualifier TLS
1440 *///=========================================================================
1441 int backlog() noexcept {
1442 return __socket_backlog;
1443 }; // -x- int backlog -x-
1444
1445 /*======================================================================*//**
1446 @brief
1447 Bind this socket to the specified network address (and port number if the
1448 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1449 used for server deamons, but can also be used to control the address from
1450 which the local host will make an outbound connection via the @ref connect()
1451 method (this bound address is the address from which the endpoint will
1452 recieve your connection).
1453
1454 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
1455 (or 1023 on some Operating Systems that test only for below 1024) in
1456 the absence of elevated access or the absence of a capability flag
1457 having been set
1458 @throws randolf::rex::xEACCES If binding to an interface on systems that
1459 require elevated access for direct interface binding in absence of
1460 said elevated access
1461 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1462 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
1463 address is not available on any local interface
1464 @throws randolf::rex::xEBADF The underlying socket is not open
1465 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1466 part of the user address space
1467 @throws randolf::rex::xEINVAL Socket is already bound
1468 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
1469 valid for this socket's family (a.k.a., communication domain)
1470 @throws randolf::rex::xENOMEM Insufficient memory
1471 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1472 doesn't refer to a socket
1473 @n@n
1474 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1475 @throws randolf::rex::xEACCES Write permission or search permission denied
1476 on a component of the path prefix (@c AF_UNIX family)
1477 @throws randolf::rex::xELOOP Too many symbolic links encountered while
1478 resolving address (@c AF_UNIX family)
1479 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
1480 @throws randolf::rex::xENOENT One of the path's directory components doesn't
1481 exist (@c AF_UNIX family)
1482 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
1483 directory (@c AF_UNIX family)
1484 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
1485
1486 @returns The same rsocket object so as to facilitate stacking
1487 @see bind(std::string, int)
1488 @see connect
1489 @see listen
1490 @qualifier POSIX
1491 @qualifier TLS
1492 *///=========================================================================
1493 rsocket* bind(
1494 /// Socket address structure
1495 const struct sockaddr* addr,
1496 /// Length of socket structure
1497 const socklen_t addrlen = sizeof(sockaddr)) {
1498 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1499 + ", <sockaddr*>"
1500 + ", size=" + std::to_string(addrlen)
1501 + ");");
1502 if (addrlen > sizeof(sockaddr_storage)) randolf::rex::mk_exception("addlen is too large for bind()", EINVAL);
1503 __rc_check(::bind(__socket_fd, addr, addrlen));
1504 std::memcpy(&__socket_addr, addr, addrlen); // No errors were returned by bind(), so now we'll copy addr to __socket_addr
1505 return this;
1506 } // -x- rsocket* bind -x-
1507
1508 //===========================================================================
1509 //
1510 // TODO: Document usage of IP_BIND_ADDRESS_NO_PORT to support connecting
1511 // from a specific IP address without losing ephemeral port selection.
1512 // Notes: https://idea.popcount.org/2014-04-03-bind-before-connect/
1513 //
1514 // TODO: Support AF_BLUETOOTH addresses
1515 // Reference:
1516 // https://man7.org/linux/man-pages/man7/address_families.7.html
1517 //
1518 // TODO: Support AF_IPX addresses
1519 // Reference:
1520 // https://man7.org/linux/man-pages/man7/address_families.7.html
1521 //
1522 // TODO: Support AF_APPLETALK addresses
1523 // Reference: https://man7.org/linux/man-pages/man7/ddp.7.html
1524 //
1525 // TODO: Support AF_PACKET addresses
1526 // Reference: https://man7.org/linux/man-pages/man7/packet.7.html
1527 //
1528 // TODO: Support AF_X25 addresses
1529 // Reference: https://man7.org/linux/man-pages/man7/x25.7.html
1530 //
1531 // TODO: Support AF_NETLINK addresses
1532 // Reference: https://man7.org/linux/man-pages/man7/netlink.7.html
1533 //
1534 /*======================================================================*//**
1535 @brief
1536 Bind this socket to the specified network address (and port number if the
1537 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1538 used for server deamons, but can also be used to control the address from
1539 which the local host will make an outbound connection via the @ref connect()
1540 method (this bound address is address from which the endpoint will recieve
1541 your connection).
1542
1543 In addition to the standard IPv4 (AF_INET family) and IPv6 (AF_INET6 family)
1544 support, rsocket also supports a few other binding options in a manner that
1545 makes it easy to utilize, without having to handle special implementation
1546 details (because they're handled behind-the-scenese by this rsocket class).
1547
1548 The address string supports a number of different notations and formats,
1549 which are documented, hereunder, with examples:
1550 - IPv4 addresses
1551 - IPv6 addresses
1552 - Network interfaces
1553 - UNIX domain sockets
1554
1555 <hr>
1556 @par IPv4 address notations (address family @c AF_INET)
1557 Takes the form of @c x.x.x.x where each "x" represents an octet in the range
1558 of @c 0 through @c 255 (e.g., @c 127.0.0.1).
1559
1560 Under a proper implenetation of TCP/IP, an IPv4 address can be abbreviated
1561 to fewer octets. The following examples demonstrate this (an unabbreviated
1562 IPv4 address is included for completeness):
1563 - @c 0.0.0.1 may be abbreviated to @c 1
1564 - @c 4.0.0.1 may be abbrevaited to @c 4.1
1565 - @c 4.3.0.1 may be abbreviated to @c 4.3.1
1566 - @c 4.3.2.1 is not abbreviated (this is the most common usage)
1567
1568 @par Example of binding to IPv4 localhost
1569
1570 @code{.cpp}
1571 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1572 #include <randolf/rex>
1573 #include <randolf/rsocket>
1574
1575 int main(int argc, char *argv[]) {
1576 try {
1577 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1578 r.bind("127.0.0.1", 32768); // <-- You are here
1579 // ... other socket I/O operations
1580 r.close();
1581 } catch (const randolf::rex::xEACCES e) {
1582 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1583 return EXIT_FAILURE;
1584 } catch (const randolf::rex::xALL e) {
1585 std::cerr << "Socket exception: " << e.what() << std::endl;
1586 return EXIT_FAILURE;
1587 } catch (const std::exception e) {
1588 std::cerr << "Other exception: " << e.what() << std::endl;
1589 return EXIT_FAILURE;
1590 }
1591 return EXIT_SUCCESS;
1592 } // -x- int main -x-
1593 @endcode
1594
1595 @note
1596 Specifying the IP address of @c 0.0.0.0 will bind to all IPv4 addresses that
1597 are assigned to all of the host machine's network interfaces with IPv4
1598 bindings.
1599 @n@n
1600 Specifying the IP address of @c 127.0.0.1 (localhost) is useful for serving
1601 only those applications that are running on the local host and use an IPv4
1602 socket to communicate.
1603
1604 <hr>
1605 @par IPv6 address notations (address family @c AF_INET6)
1606 Takes the form of @c x:x:x:x:x:x:x:x where each "x" represents a segment in
1607 the range of @c 0 through @c ffff in hexadecimal (e.g., `0:0:0:0:0:0:0:1`),
1608 or `x:x:x:x:x:x:y.y.y.y` where `y.y.y.y` represents an [unabbreviated] IPv4
1609 address (which merely replaces the last two IPv6 segments).
1610
1611 Under a proper implenetation of TCP/IP, an IPv6 address can be abbreviated
1612 to fewer segments by using a sequence of two colons (`::`) to indicate that
1613 the undefined segments are @c 0 (this abbreviation can only be used once,
1614 and may represent segments at the beginning or end, or anywhere in between).
1615 The following examples demonstrate this (an unabbreviated IPv6 address is
1616 included for completeness):
1617 - `0:0:0:0:0:0:0:1` may be abbreviated to `::1`
1618 - `0:0:0:0:0:0:2:1` may be abbreviated to `::2:1`
1619 - `8:0:0:0:0:0:2:1` may be abbreviated to `8::2:1`
1620 - `8:7:0:0:0:0:2:1` may be abbreviated to `8:7::2:1`
1621 - `8:7:0:0:0:0:0:0` may be abbreviated to `8:7::`
1622 - `8:0:0:0:0:0:0:0` may be abbreviated to `8::`
1623 - `8:7:6:5:4:3:2:1` is not abbreviated
1624
1625 @par Example of binding to IPv6 localhost
1626
1627 @code{.cpp}
1628 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1629 #include <randolf/rex>
1630 #include <randolf/rsocket>
1631
1632 int main(int argc, char *argv[]) {
1633 try {
1634 randolf::rsocket r(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1635 r.bind("::1", 32768); // <-- You are here
1636 // ... other socket I/O operations
1637 r.close();
1638 } catch (const randolf::rex::xEACCES e) {
1639 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1640 return EXIT_FAILURE;
1641 } catch (const randolf::rex::xALL e) {
1642 std::cerr << "Socket exception: " << e.what() << std::endl;
1643 return EXIT_FAILURE;
1644 } catch (const std::exception e) {
1645 std::cerr << "Other exception: " << e.what() << std::endl;
1646 return EXIT_FAILURE;
1647 }
1648 return EXIT_SUCCESS;
1649 } // -x- int main -x-
1650 @endcode
1651
1652 @note
1653 Specifying the IP address of @c :: will bind to all IPv6 addresses that are
1654 assigned to all of the host machine's network interfaces with IPv6 bindings.
1655 @n@n
1656 Specifying the IP address of @c ::1 (localhost) is useful for serving only
1657 those applications that are running on the local host and use an IPv6 socket
1658 to communicate.
1659
1660 <hr>
1661 @par Interface address notation (address families @c AF_INET and @c AF_INET6)
1662 Takes the form of @c if=name where "name" represents the name of a local
1663 network interface.
1664
1665 Interface binding is useful when binding to interfaces that aren't configured
1666 with a static IP address because they were dymamically configured via the
1667 Dynamic Host Configuration Protocol (DHCP) or Stateless Address Configuration
1668 (SLAAC), or the configuration was changed manually by an administrator and
1669 you don't want your software to handle such changes gracefully, even if the
1670 new IP address is within the scope of an entirely different CIDR. (Changing
1671 between the IPv4 and IPv6 addresses is not supported, however, due to the
1672 fundamental differences between these two address families that includes
1673 differences beyond that of IP address format, although under a proper
1674 implementation of TCP/IP the binding should survive such changes when the IP
1675 address is reverted to the initial IP address family of the bound interface.)
1676
1677 Examples of interfaces include:
1678 - `if=lo` typical for localhost virtual network adapter
1679 - `if=bond0` typical for the first bonded virtual network adapter (used in
1680 failover and load-balancing network configurations)
1681 - `if=br0` typical for the first bridge virtual network adapter
1682 - `if=eth0` typical for the first ethernet network adapter
1683 - `if=tap0` typical for the first virtual layer 2 ethernet switch (used by
1684 VPNs to extend a remote network into the local network stack)
1685 - `if=tun0` typical for the first virtual layer 3 ethernet tunnel (used by
1686 VPNs to provide access to a remote network)
1687 - `if=wlo1` typical for the first wireless network adapter
1688
1689 @par Example of binding to interface localhost
1690
1691 @code{.cpp}
1692 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1693 #include <randolf/rex>
1694 #include <randolf/rsocket>
1695
1696 int main(int argc, char *argv[]) {
1697 try {
1698 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1699 r.bind("if=lo", 32768); // <-- You are here
1700 // ... other socket I/O operations
1701 r.close();
1702 } catch (const randolf::rex::xEACCES e) {
1703 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1704 return EXIT_FAILURE;
1705 } catch (const randolf::rex::xALL e) {
1706 std::cerr << "Socket exception: " << e.what() << std::endl;
1707 return EXIT_FAILURE;
1708 } catch (const std::exception e) {
1709 std::cerr << "Other exception: " << e.what() << std::endl;
1710 return EXIT_FAILURE;
1711 }
1712 return EXIT_SUCCESS;
1713 } // -x- int main -x-
1714 @endcode
1715
1716 @note
1717 This is not a standard feature of the POSIX bind() function. This rsocket
1718 class uses the setsockopt() function behind-the-scenes to configure (enable)
1719 the @c SO_BINDTODEVICE option, and then the bind() function is called with
1720 the IPv4 address @c 0.0.0.0 if the underlying socket was initialized to the
1721 @c AF_INET family, or the IPv6 address @c :: if the underlying socket was
1722 initialized to the @c AF_INET6 family.
1723 @n@n
1724 Specifying the interface address of `if=lo` (localhost) is useful for serving
1725 only those applications that are running on the local host and use an IPv4 or
1726 IPv6 socket to communicate.
1727
1728 <hr>
1729 @par UNIX Domain Sockets address-notation (address family @c AF_UNIX)
1730 Takes the form of @c /path where "/path" represents an absolute path on the
1731 local file system (the path can also be relative, in which case it doesn't
1732 begin with a slash (`/`) character, but extra care is needed to ensure that
1733 the path will actually be at its expected location).
1734
1735 @par Example of binding to UNIX domain sockets
1736
1737 @code{.cpp}
1738 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1739 #include <randolf/rex>
1740 #include <randolf/rsocket>
1741
1742 int main(int argc, char *argv[]) {
1743 try {
1744 randolf::rsocket r(AF_UNIX, SOCK_STREAM);
1745 r.bind("/var/run/rsocket-test-socket.tmp"); // <-- You are here
1746 // ... other socket I/O operations
1747 r.close();
1748 } catch (const randolf::rex::xEACCES e) {
1749 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1750 return EXIT_FAILURE;
1751 } catch (const randolf::rex::xALL e) {
1752 std::cerr << "Socket exception: " << e.what() << std::endl;
1753 return EXIT_FAILURE;
1754 } catch (const std::exception e) {
1755 std::cerr << "Other exception: " << e.what() << std::endl;
1756 return EXIT_FAILURE;
1757 }
1758 return EXIT_SUCCESS;
1759 } // -x- int main -x-
1760 @endcode
1761
1762 @note
1763 Normal socket I/O functionality is used to exchange data with another process
1764 that can open the same UNIX domain socket (normally on the same host,
1765 although it may not be outside the realm of possibility for future Linux
1766 kernels to support UNIX domain sockets on remote hosts).
1767
1768 <hr>
1769 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
1770 (or 1023 on some Operating Systems that test only for below 1024) in
1771 the absence of elevated access or the absence of a capability flag
1772 having been set
1773 @throws randolf::rex::xEACCES If binding to an interface on systems that
1774 require elevated access for direct interface binding in absence of
1775 said elevated access
1776 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1777 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
1778 address is not available on any local interface
1779 @throws randolf::rex::xEBADF The underlying socket is not open
1780 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1781 part of the user address space
1782 @throws randolf::rex::xEINVAL Socket is already bound
1783 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
1784 valid for this socket's family (a.k.a., communication domain)
1785 @throws randolf::rex::xENOMEM Insufficient memory
1786 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1787 doesn't refer to a socket
1788 @n@n
1789 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1790 @throws randolf::rex::xEACCES Write permission or search permission denied
1791 on a component of the path prefix (@c AF_UNIX family)
1792 @throws randolf::rex::xELOOP Too many symbolic links encountered while
1793 resolving address (@c AF_UNIX family)
1794 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
1795 @throws randolf::rex::xENOENT One of the path's directory components doesn't
1796 exist (@c AF_UNIX family)
1797 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
1798 directory (@c AF_UNIX family)
1799 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
1800
1801 @returns The same rsocket object so as to facilitate stacking
1802 @see bind(const struct sockaddr*, const socklen_t)
1803 @see connect
1804 @see listen
1805 @qualifier TLS
1806 *///=========================================================================
1807 rsocket* bind(
1808 /// IPv4 address, IPv6 address, hostname, UNIX domain sockets path, or specialized variant (see notes, above)
1809 const std::string address,
1810 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
1811 const int port = 0) {
1812 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1813 + ", " + address
1814 + " port=" + std::to_string(port) + (port == 0 ? " (emphemeral or not-applicable)" : "")
1815 + ", size=" + std::to_string(__socket_addr_size)
1816 + ");");
1817
1818//std::cout << " address: " << address << std::endl;
1819 if (address.starts_with("if=")) { // Bind to interface
1820// TODO: Use family() to simplify this section of code
1821 struct ifreq ifr{};
1822 if (address.size() - 3 >= sizeof(ifr.ifr_name)) {} // Throw exception because ASCIIZ name will be too long
1823 address.copy(ifr.ifr_name, address.size() - 3, 3); // Copy address, excluding the "if=" portion
1824 __rc_check(::setsockopt(__socket_fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)));
1825 // TODO: Figure out how to use INADDR_ANY instead of 0 or :: (assuming this is even possible)
1826 // TODO: Test IPv6 more (telnet doesn't seem to be able to connect, and continues to work on IPv4 address)
1827 __socket_addr = *mk_sockaddr_storage(__socket_addr.ss_family == AF_INET6 ? "::" : "0", port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
1828 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
1829
1830// } else if (address.starts_with("if_cidr=")) { // Bind to interface based on matching CIDR
1831// // REMINDER: Update static family() method as well
1832// getifaddrs: https://man7.org/linux/man-pages/man3/getifaddrs.3.html
1833// } else if (address.starts_with("if_mac=")) { // Bind to interface based on its physical/hardware machine address
1834// // REMINDER: Update static family() method as well
1835// // Resolve interface name, and then just do what if= does (above)
1836
1837 } else {
1838 __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
1839 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
1840// __rc_check(::bind(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
1841 }
1842 return this;
1843 } // -x- rsocket* bind -x-
1844
1845 /*======================================================================*//**
1846 @brief
1847 Find out what buffer size is used by the various recv() methods.
1848 @returns Buffer size (in bytes)
1849 @see buffer_size(const size_t nbytes)
1850 @see is_buffered
1851 @qualifier TLS
1852 *///=========================================================================
1853 const int buffer_size() noexcept {
1854 return __buffer_size;
1855 }; // -x- int buffer_size -x-
1856
1857 /*======================================================================*//**
1858 @brief
1859 Override the default buffer size (typically 1024) used by the various recv()
1860 methods.
1861
1862 If resetting to the compiled-in default, use the buffer_size_reset() method
1863 instead of setting the value directly. This ensures that future versions,
1864 with a different compiled-in default, will be reset to the compiled-in value.
1865 @returns The same rsocket object so as to facilitate stacking
1866 @see buffer_size
1867 @see buffer_size_reset
1868 @qualifier TLS
1869 *///=========================================================================
1870 rsocket* buffer_size(
1871 /// Size of the new buffer (in bytes)
1872 const size_t nbytes) noexcept {
1873 __buffer_size = nbytes;
1874 if (__debug) debug("buffer_size(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1875 + " nbytes=" + std::to_string(nbytes)
1876 + ");");
1877 return this;
1878 }; // -x- rsocket* buffer_size -x-
1879
1880 /*======================================================================*//**
1881 @brief
1882 Reset the default buffer size (typically 1024) used by the various recv()
1883 methods.
1884
1885 This method is preferred for resetting to the compiled-in default instead of
1886 setting the value directly. This ensures that future versions, with a
1887 different compiled-in default, will be reset to the compiled-in value.
1888 @returns The same rsocket object so as to facilitate stacking
1889 @see buffer_size(const size_t nbytes)
1890 @qualifier TLS
1891 *///=========================================================================
1892 rsocket* buffer_size_reset() noexcept {
1893 __buffer_size = RSOCKET_BUFFER_SIZE;
1894 if (__debug) debug("buffer_size_reset(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1895 + " nbytes=" + std::to_string(RSOCKET_BUFFER_SIZE)
1896 + ");");
1897 return this;
1898 }; // -x- rsocket* buffer_size_reset -x-
1899
1900 //===========================================================================
1901 //
1902 // TODO: If family is AF_UNIX then also unlink the domain socket file.
1903 //
1904 /*======================================================================*//**
1905 @brief
1906 Close this rsocket. (If this rsocket was already closed, then calling this
1907 method additional times will have no effect, and will not cause exceptions to
1908 be thrown.)
1909 @warning
1910 This method may throw exceptions in some circumstances, which is extremely
1911 rare. If you prefer to close without having to contend with any exceptions
1912 (e.g., while calling close() from within a destructor), the close_passive()
1913 method will return an integer indicating success/failure instead of throwing
1914 an exception, and then you'll have to handle errno manually (which is useful
1915 in destructors because any error can merely be handled procedurally).
1916
1917 @throws randolf::rex::xEBADF The underlying socket is not open
1918 @throws randolf::rex::xEINTR Interrupted by a signal
1919 @throws randolf::rex::xEIO An I/O error occurred
1920
1921 @returns The same rsocket object so as to facilitate stacking
1922 @see is_closed()
1923 @qualifier POSIX
1924 @qualifier TLS
1925 *///=========================================================================
1926 rsocket* close() {
1927 if (__socket_open) {
1928 __rc_check(::close(__socket_fd));
1929 __socket_open = false;
1930 } // -x- if __socket_open -x-
1931 return this;
1932 }; // -x- rsocket* close -x-
1933
1934 /*======================================================================*//**
1935 @brief
1936 Close this rsocket without throwing any exceptions (an error code is returned
1937 instead, which is useful while calling close_passive() from within a
1938 destructor).
1939 @returns 0 = success
1940 @returns -1 = error (errno will be set accordingly)
1941 @see is_closed()
1942 @qualifier TLS
1943 *///=========================================================================
1944 int close_passive() noexcept {
1945 if (__socket_open) {
1946 int rc = ::close(__socket_fd);
1947 if (rc == 0) __socket_open = false;
1948 return rc;
1949 } // -x- if __socket_open -x-
1950 return 0; // Indicate success (because the socket was previously closed successfully)
1951 }; // -x- int close_passive -x-
1952
1953 /*======================================================================*//**
1954 @brief
1955 Connect this socket to a specific endpoint (which may differ from this
1956 rsocket's address that was previously configured by the @ref bind() method).
1957
1958 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1959 @throws randolf::rex::xEADDRNOTAVAIL No ephemeral ports are available for
1960 assignment to unbound socket
1961 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
1962 @throws randolf::rex::xEAGAIN Insufficient entries in the routing cache
1963 @throws randolf::rex::xEALREADY Previous connection attempt not completed on
1964 nonblocking socket
1965 @throws randolf::rex::xEBADF The underlying socket is not open
1966 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
1967 connections
1968 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1969 part of the user address space
1970 @throws randolf::rex::xEINPROGRESS Connection cannot be completed immediately
1971 on nonblocking socket (@c AF_UNIX family fails with xEAGAIN instead)
1972 @throws randolf::rex::xEINTR Interrupted by a signal
1973 @throws randolf::rex::xEISCONN Socket is already connected
1974 @throws randolf::rex::xENETUNREACH No route to network
1975 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1976 doesn't refer to a socket
1977 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1978 @throws randolf::rex::xEPERM Can't connect to a broadcast address when the
1979 socket's broadcast flag isn't enabled
1980 @throws randolf::rex::xEPROTOTYPE Socket type doesn't support the requested
1981 communications protocol (e.g., connecting a UNIX domain socket of
1982 type @c SOCK_STREAM to an endpoint expecting type @c SOCK_DGRAM)
1983 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1984 @n@n
1985 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1986 @throws randolf::rex::xEACCES Write permission or search permission denied
1987 on a component of the path prefix (@c AF_UNIX family)
1988 @throws randolf::rex::xEAGAIN Connection cannot be completed immediately on
1989 nonblocking socket (@c AF_UNIX family)
1990
1991 @returns The same rsocket object so as to facilitate stacking
1992 @see tls_do_handshake()
1993 @qualifier POSIX
1994 @qualifier TLS
1995 *///=========================================================================
1996 rsocket* connect(
1997 /// IPv4 address, IPv6 address, hostname, or UNIX domain sockets path
1998 const std::string address,
1999 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
2000 const int port = 0) {
2001 if (__debug) debug("connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2002 + ", " + address
2003 + " (port=" + std::to_string(port) + ")"
2004 + ", " + std::to_string(__socket_addr_size)
2005 + ");");
2006
2007 __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
2008 __rc_check(::connect(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size), 0, rex::rex::REX_FLAGS::REX_ALT_EAGAIN);
2009
2010// __rc_check(::connect(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
2011 __socket_connected = true;
2012
2013 // --------------------------------------------------------------------------
2014 // TLS.
2015 // --------------------------------------------------------------------------
2016 if (__tls) {
2017 if (__debug) debug("tls_connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2018 + ");");
2019 __rc_check_tls(SSL_connect(__tls_fd));
2020 } // -x- if __tls -x-
2021
2022 return this;
2023 }; // -x- rsocket* connect -x-
2024
2025 /*======================================================================*//**
2026 @brief
2027 Find out whether debug mode is enabled.
2028
2029 @par Threads
2030 This method is threadsafe.
2031 @returns TRUE = enabled
2032 @returns FALSE = not enabled
2033 @qualifier TLS
2034 *///=========================================================================
2035 const bool debug() noexcept { return __debug; };
2036
2037 /*======================================================================*//**
2038 @brief
2039 Debug mode. When debug mode is enabled, output is sent to stderr by default,
2040 unless a second parameter specifies a different file handle (e.g., stdout, or
2041 even a socket).
2042
2043 debug(...) returns -1 if fd can't be written to (errno will be set in
2044 accordance with std::fprintf's behaviour since std::fprintf is used for
2045 writing debug output). Normally we'd throw an exception for such errors, but
2046 with debug is a special case because debugging needs to be as quick and
2047 convenient as possible for developers.
2048
2049 debug(...) returns -2 if writing to stderr failed when attempting to announce
2050 that fd can't be written to (as described above).
2051
2052 debug(...) returns -3 if some other error occurs (e.g., internal formatting
2053 error {which should not happen} or memory allocation failed, in which case
2054 there's a more serious problem that will be causing other problems elsewhere
2055 anyway).
2056
2057 Developers may add their own debug messages by using debug(std::string),
2058 which will only be written out if debug mode is enabled. This same method is
2059 used internally, so messages will be indistinguishable from developer's
2060 messages.
2061
2062 @par Threads
2063 This method is thread-safe.
2064 @returns 0 = success
2065 @returns -1 = error writing to stream (errno will be set accordingly)
2066 @returns -2 = error writing to stderr (errno will be set accordingly)
2067 @returns -3 = error (errno will be set accordingly)
2068 @qualifier TLS
2069 *///=========================================================================
2070 const int debug(
2071 /// TRUE = enable debug mode@n
2072 /// FALSE = disable debug mode (does not close any file handles)
2073 const bool debug_flag,
2074 /// File descriptor/handle to use for debug output
2075 std::FILE* fd = stderr) noexcept {
2076
2077 int rc = debug_fd(fd); // Attempt to change debug handle
2078 if (rc != 0) return rc; // Return error id new debug handle failed
2079 time_t tm = std::time(nullptr);
2080 std::string dt(27, 0);
2081 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2082 try {
2083 if (debug_flag || __debug) std::fprintf(__debug_fd,
2084 "[%s] %s: Debug mode is %s\n",
2085 dt.c_str(),
2086 __debug_prefix.c_str(),
2087 debug_flag ? "ON" : "OFF");
2088 } catch (const std::system_error& e) { // Error writing to fd
2089 try {
2090 std::fprintf(stderr,
2091 "[%s] %s: error writing to stream\n",
2092 dt.c_str(),
2093 __debug_prefix.c_str());
2094 } catch (const std::exception& e) {
2095 return -2;
2096 }
2097 return -1;
2098 } catch (const std::exception& e) {
2099 return -3;
2100 }
2101 __debug = debug_flag; // Save debug flag
2102
2103 return 0; // Indicate success (no errors)
2104 }; // -x- int debug -x-
2105
2106 /*======================================================================*//**
2107 @brief
2108 Send the specified message as debug output (as long as debug mode is enabled;
2109 if disabled, no debug output will be sent).
2110 @returns 0 = success
2111 @returns -1 = error writing to stream (errno will be set accordingly)
2112 @returns -2 = error writing to stderr (errno will be set accordingly)
2113 @returns -3 = error (errno will be set accordingly)
2114 @qualifier TLS
2115 *///=========================================================================
2116 const int debug(
2117 /// Debug message as an ASCIIZ string
2118 const char* msg) noexcept {
2119 return __debug ? __debug_out(std::string(msg)) : 0;
2120 }; // -x- int debug -x-
2121
2122 /*======================================================================*//**
2123 @copydoc debug(const char*)
2124 @qualifier TLS
2125 *///=========================================================================
2126 const int debug(
2127 /// Debug message as an std::string object
2128 const std::string msg) noexcept {
2129 return __debug ? __debug_out(msg) : 0;
2130 }; // -x- int debug -x-
2131
2132 /*======================================================================*//**
2133 @brief
2134 Find out which file descriptor/handle is used for debug output.
2135 @returns file handle/descriptor currently used for debug output
2136 *///=========================================================================
2137 const std::FILE* debug_fd() noexcept { return __debug_fd; }; // Get debug-output file handle
2138
2139 /*======================================================================*//**
2140 @brief
2141 Specify a different file descriptor/handle to use for debug output
2142 @returns 0 = success
2143 @returns -1 = error writing to stream (errno will be set accordingly)
2144 @returns -2 = error writing to stderr (errno will be set accordingly)
2145 @returns -3 = error (errno will be set accordingly)
2146 *///=========================================================================
2147 const int debug_fd(
2148 /// File descriptor/handle to use for debug output
2149 std::FILE* fd) noexcept { // Set debug-output file handle
2150 if (__debug_fd != fd) {
2151 debug("Current debug-output file handle unset"); // For this message, we only want to send this out if debug mode is enabled
2152 __debug_fd = fd; // Save new debug-output file handle
2153 return debug("New debug-output file handle set"); // For this message, we only want to send this out if debug mode is enabled
2154 } // -x- if ~fd -x-
2155 return 0; // Indicate success (no errors)
2156 }; // -x- int debug_fd -x-
2157
2158 /*======================================================================*//**
2159 @brief
2160 Find out what the current prefix is set to that's used in debug output.
2161
2162 This may be set at any time, including before or after enabling or disabling
2163 debug mode.
2164 @returns debug prefix string
2165 @qualifier TLS
2166 *///=========================================================================
2167 const std::string debug_prefix() noexcept { return __debug_prefix; }; // -x- std::string debug_prefix -x-
2168
2169 /*======================================================================*//**
2170 @brief
2171 Change the prefix used in debug output.
2172 @returns The same rsocket object so as to facilitate stacking
2173 @qualifier TLS
2174 *///=========================================================================
2175 rsocket* debug_prefix(
2176 /// New debug prefix string
2177 const std::string prefix) noexcept {
2178 __debug_prefix = prefix;
2179 return this;
2180 }; // -x- std::string debug_prefix -x-
2181
2182 private:
2183 /*----------------------------------------------------------------------*//**
2184 @brief
2185 Internal debug-output function. This doesn't check __debug flag because it's
2186 expected that the normal debug() methods are taking care of this.
2187 @returns 0 = success
2188 @returns -1 = error writing to stream (errno will be set accordingly)
2189 @returns -2 = error writing to stderr (errno will be set accordingly)
2190 @returns -3 = error (errno will be set accordingly)
2191 *///-------------------------------------------------------------------------
2192 const int __debug_out(
2193 /// Debugging message
2194 const std::string msg) noexcept {
2195
2196 time_t tm = std::time(nullptr);
2197 std::string dt(27, 0);
2198 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2199 try {
2200 std::fprintf(__debug_fd,
2201 "[%s] %s: %s\n",
2202 dt.c_str(),
2203 __debug_prefix.c_str(),
2204 msg.c_str());
2205 } catch (const std::system_error& e) { // Error writing to fd
2206 try {
2207 std::fprintf(stderr,
2208 "[%s] %s: error writing to stream\n",
2209 dt.c_str(),
2210 __debug_prefix.c_str());
2211 } catch (const std::exception& e) {
2212 return -2;
2213 }
2214 return -1;
2215 } catch (const std::exception& e) {
2216 return -3;
2217 }
2218
2219 return 0; // Indicate success (no errors)
2220 }; // -x- int __debug_out -x-
2221
2222 // --------------------------------------------------------------------------
2223 // These are specialized internal debug output methods for presenting socket
2224 // options when they are get or set. These methods are marked as "private"
2225 // because they are really aren't useful outside of internal-use contexts.
2226 //
2227 // @par Threads
2228 // Some of these methods are thread-safe.
2229 //
2230 // These methods are thread-safe when they don't reference structures (e.g.,
2231 // integer {int}, unsigned integer {u_int}, and unsigned character {u_char}).
2232 //
2233 // These methods are not necessarily thread-safe if they reference structures
2234 // unless those structures are defined with std::atomic, or they were
2235 // constructed on-the-fly as anonymous parameters.
2236 // --------------------------------------------------------------------------
2237 const int __debug_sockopt(
2238 /// Name of function or method
2239 const std::string func,
2240 /// The level at which the option resides; typically @c SOL_SOCKET
2241 const int level,
2242 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2243 const int option,
2244 /// The structure that this socket option will be set to
2245 const int value) {
2246 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2247 + ", " + std::to_string(level)
2248 + ", " + std::to_string(option)
2249 + ", " + std::to_string(value)
2250 + ", size=" + std::to_string(sizeof(value))
2251 + ");");
2252 }; // -x- int __debug_sockopt -x-
2253 const int __debug_sockopt(
2254 /// Name of function or method
2255 const std::string func,
2256 /// The level at which the option resides; typically @c SOL_SOCKET
2257 const int level,
2258 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2259 const int option,
2260 /// The structure that this socket option will be set to
2261 const u_int value) {
2262 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2263 + ", " + std::to_string(level)
2264 + ", " + std::to_string(option)
2265 + ", u_int{" + std::to_string(value) + "}"
2266 + ", size=" + std::to_string(sizeof(value))
2267 + ");");
2268 }; // -x- int __debug_sockopt -x-
2269 const int __debug_sockopt(
2270 /// Name of function or method
2271 const std::string func,
2272 /// The level at which the option resides; typically @c SOL_SOCKET
2273 const int level,
2274 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2275 const int option,
2276 /// The structure that this socket option will be set to
2277 const u_char value) {
2278 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2279 + ", " + std::to_string(level)
2280 + ", " + std::to_string(option)
2281 + ", u_char{" + std::to_string(value) + "}"
2282 + ", size=" + std::to_string(sizeof(value))
2283 + ");");
2284 }; // -x- int __debug_sockopt -x-
2285 const int __debug_sockopt(
2286 /// Name of function or method
2287 const std::string func,
2288 /// The level at which the option resides; typically @c SOL_SOCKET
2289 const int level,
2290 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2291 const int option,
2292 /// The structure that this socket option will be set to
2293 const linger* value) {
2294 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2295 + ", " + std::to_string(level)
2296 + ", " + std::to_string(option)
2297 + ", linger{l_onoff=" + std::to_string(value->l_onoff)
2298 + ", l_linger=" + std::to_string(value->l_linger) + "}"
2299 + ", " + std::to_string(sizeof(value))
2300 + ");");
2301 }; // -x- int __debug_sockopt -x-
2302 const int __debug_sockopt(
2303 /// Name of function or method
2304 const std::string func,
2305 /// The level at which the option resides; typically @c SOL_SOCKET
2306 const int level,
2307 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2308 const int option,
2309 /// The structure that this socket option will be set to
2310 const timeval* value) {
2311 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2312 + ", " + std::to_string(level)
2313 + ", " + std::to_string(option)
2314 + ", timeval{tv_sec=" + std::to_string(value->tv_sec)
2315 + ", tv_usec=" + std::to_string(value->tv_usec) + "}"
2316 + ", " + std::to_string(sizeof(value))
2317 + ");");
2318 }; // -x- int __debug_sockopt -x-
2319 const int __debug_sockopt(
2320 /// Name of function or method
2321 const std::string func,
2322 /// The level at which the option resides; typically @c SOL_SOCKET
2323 const int level,
2324 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2325 const int option,
2326 /// The structure that this socket option will be set to
2327 const in_addr* value) {
2328 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2329 + ", " + std::to_string(level)
2330 + ", " + std::to_string(option)
2331 + ", in_addr{" + std::to_string(value->s_addr) + "}"
2332 + ", " + std::to_string(sizeof(value))
2333 + ");");
2334 }; // -x- int __debug_sockopt -x-
2335 const int __debug_sockopt(
2336 /// Name of function or method
2337 const std::string func,
2338 /// The level at which the option resides; typically @c SOL_SOCKET
2339 const int level,
2340 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2341 const int option,
2342 /// The structure that this socket option will be set to
2343 const ip_mreq* value) {
2344 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2345 + ", " + std::to_string(level)
2346 + ", " + std::to_string(option)
2347 + ", ip_mreq{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2348 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2349 + ", " + std::to_string(sizeof(value))
2350 + ");");
2351 }; // -x- int __debug_sockopt -x-
2352 const int __debug_sockopt(
2353 /// Name of function or method
2354 const std::string func,
2355 /// The level at which the option resides; typically @c SOL_SOCKET
2356 const int level,
2357 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2358 const int option,
2359 /// The structure that this socket option will be set to
2360 const ip_mreq_source* value) {
2361 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2362 + ", " + std::to_string(level)
2363 + ", " + std::to_string(option)
2364 + ", ip_mreq_source{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2365 + ", source=" + std::to_string(value->imr_sourceaddr.s_addr)
2366 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2367 + ", " + std::to_string(sizeof(value))
2368 + ");");
2369 }; // -x- int __debug_sockopt -x-
2370 const int __debug_sockopt(
2371 /// Name of function or method
2372 const std::string func,
2373 /// The level at which the option resides; typically @c SOL_SOCKET
2374 const int level,
2375 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2376 const int option,
2377 /// The structure that this socket option will be set to
2378 const icmp6_filter* value) {
2379 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2380 + ", " + std::to_string(level)
2381 + ", " + std::to_string(option)
2382 + ", icmp6_filter{[0]=" + std::to_string(value->icmp6_filt[0])
2383 + ", [1]=" + std::to_string(value->icmp6_filt[1])
2384 + ", [2]=" + std::to_string(value->icmp6_filt[2])
2385 + ", [3]=" + std::to_string(value->icmp6_filt[3])
2386 + ", [4]=" + std::to_string(value->icmp6_filt[4])
2387 + ", [5]=" + std::to_string(value->icmp6_filt[5])
2388 + ", [6]=" + std::to_string(value->icmp6_filt[6])
2389 + ", [7]=" + std::to_string(value->icmp6_filt[7]) + "}"
2390 + ", " + std::to_string(sizeof(value))
2391 + ");");
2392 }; // -x- int __debug_sockopt -x-
2393 const int __debug_sockopt(
2394 /// Name of function or method
2395 const std::string func,
2396 /// The level at which the option resides; typically @c SOL_SOCKET
2397 const int level,
2398 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2399 const int option,
2400 /// The structure that this socket option will be set to
2401 const sockaddr_in6* value) {
2402 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2403 + ", " + std::to_string(level)
2404 + ", " + std::to_string(option)
2405 + ", sockaddr_in6{family=" + std::to_string(value->sin6_family)
2406 + ", port=" + std::to_string(value->sin6_port)
2407 + ", flowinfo=" + std::to_string(value->sin6_flowinfo)
2408 + ", addr=" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[0])
2409 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[1])
2410 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[2])
2411 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[3])
2412 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[4])
2413 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[5])
2414 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[6])
2415 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[7])
2416 + ", scope_id=" + std::to_string(value->sin6_scope_id) + "}"
2417 + ", " + std::to_string(sizeof(value))
2418 + ");");
2419 }; // -x- int __debug_sockopt -x-
2420 const int __debug_sockopt(
2421 /// Name of function or method
2422 const std::string func,
2423 /// The level at which the option resides; typically @c SOL_SOCKET
2424 const int level,
2425 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2426 const int option,
2427 /// The structure that this socket option will be set to
2428 const ip6_mtuinfo* value) {
2429 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2430 + ", " + std::to_string(level)
2431 + ", " + std::to_string(option)
2432 + ", ip6_mtuinfo{addr=" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[0])
2433 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[1])
2434 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[2])
2435 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[3])
2436 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[4])
2437 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[5])
2438 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[6])
2439 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[7])
2440 + ", mtu=" + std::to_string(value->ip6m_mtu) + "}"
2441 + ", " + std::to_string(sizeof(value))
2442 + ");");
2443 }; // -x- int __debug_sockopt -x-
2444 const int __debug_sockopt(
2445 /// Name of function or method
2446 const std::string func,
2447 /// The level at which the option resides; typically @c SOL_SOCKET
2448 const int level,
2449 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2450 const int option,
2451 /// The structure that this socket option will be set to
2452 const ipv6_mreq* value) {
2453 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2454 + ", " + std::to_string(level)
2455 + ", " + std::to_string(option)
2456 + ", ipv6_mreq{addr=" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[0])
2457 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[1])
2458 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[2])
2459 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[3])
2460 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[4])
2461 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[5])
2462 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[6])
2463 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[7])
2464 + ", iface=" + std::to_string(value->ipv6mr_interface) + "}"
2465 + ", " + std::to_string(sizeof(value))
2466 + ");");
2467 }; // -x- int __debug_sockopt -x-
2468 const int __debug_sockopt(
2469 /// Name of function or method
2470 const std::string func,
2471 /// The level at which the option resides; typically @c SOL_SOCKET
2472 const int level,
2473 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2474 const int option,
2475 /// The structure that this socket option will be set to
2476 const group_req* value) {
2477 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2478 + ", " + std::to_string(level)
2479 + ", " + std::to_string(option)
2480 + ", group_req{iface=" + std::to_string(value->gr_interface)
2481 + ", <group_req>}"
2482 + ", " + std::to_string(sizeof(value))
2483 + ");");
2484 }; // -x- int __debug_sockopt -x-
2485 const int __debug_sockopt(
2486 /// Name of function or method
2487 const std::string func,
2488 /// The level at which the option resides; typically @c SOL_SOCKET
2489 const int level,
2490 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2491 const int option,
2492 /// The structure that this socket option will be set to
2493 const group_source_req* value) {
2494 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2495 + ", " + std::to_string(level)
2496 + ", " + std::to_string(option)
2497 + ", group_source_req{iface=" + std::to_string(value->gsr_interface)
2498 + ", <group_source_req>}"
2499 + ", " + std::to_string(sizeof(value))
2500 + ");");
2501 }; // -x- int __debug_sockopt -x-
2502 const int __debug_sockopt_other(
2503 /// Name of function or method
2504 const std::string func,
2505 /// The level at which the option resides; typically @c SOL_SOCKET
2506 const int level,
2507 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2508 const int option,
2509 /// The structure that this socket option will be set to
2510 const socklen_t size) {
2511 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2512 + ", " + std::to_string(level)
2513 + ", " + std::to_string(option)
2514 + ", other=?"
2515 + ", " + std::to_string(size)
2516 + ");");
2517 }; // -x- int __debug_sockopt_other -x-
2518
2519 public:
2520 /*======================================================================*//**
2521 @brief
2522 Discards the specified number of 8-bit bytes efficiently, and without closing
2523 the stream, and without consuming excessive quantities of memory.
2524 @exception randolf::rex::xERANGE An invalid value was specified for either
2525 the @c nbytes or @c memory_size parameter (e.g., a negative value,
2526 except for -1 with the @c nbytes parameter)
2527 @returns Number of bytes that were successfully discarded
2528 @see buffer_size
2529 @see recv
2530 @qualifier TLS
2531 *///=========================================================================
2532 int discard(
2533 /// Number of bytes to discard@n
2534 /// 0 = use internal @ref buffer_size()@n
2535 /// -1 = all remaining data waiting to be received (in other words, this
2536 /// method will repeatedly consume all data until @ref eos)
2537 int nbytes,
2538 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
2539 const int flags = 0,
2540 /// Maximum internal read size@n
2541 /// 0 = default to @ref buffer_size (when @c nbytes is larger than this size,
2542 /// multiple reads into this buffer will be utilized behind-the-scenes to
2543 /// consume the quantity of data specified by the @c nbytes parameter)
2544 const size_t memory_size = 0) {
2545 int buf_size = memory_size != 0 ? memory_size : __buffer_size;
2546 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2547 + ", " + std::to_string(nbytes)
2548 + ", " + std::to_string(flags)
2549 + ", " + std::to_string(buf_size)
2550 + ");");
2551
2552 // --------------------------------------------------------------------------
2553 // Syntax checks.
2554 // --------------------------------------------------------------------------
2555 if (nbytes == 0) nbytes = __buffer_size;
2556 else if (nbytes < -1) throw randolf::rex::xERANGE( "nbytes parameter of " + std::to_string(nbytes) + " is below 0");
2557 if (memory_size < 0) throw randolf::rex::xERANGE("memory_size parameter of " + std::to_string(memory_size) + " is below 0");
2558
2559 // --------------------------------------------------------------------------
2560 // Internal variables.
2561 // --------------------------------------------------------------------------
2562 int discarded = 0; // Total number of bytes discarded
2563 std::vector<char> buf(buf_size);
2564 buf.reserve(buf_size); // Pre-allocate underlying array to prevent memory corruption when __recv writes directly into it
2565
2566 // --------------------------------------------------------------------------
2567 // Discard loop.
2568 // --------------------------------------------------------------------------
2569 try {
2570
2571 // --------------------------------------------------------------------------
2572 // Discard all remaining data waiting to be received.
2573 // --------------------------------------------------------------------------
2574 if (nbytes < 0) {
2575 int n = 0;
2576 while ((n = __recv(buf.data(), buf_size, flags)) != 0) {
2577 discarded += n;
2578 }; // -x- while n -x-
2579
2580 // --------------------------------------------------------------------------
2581 // Discard specific amount of data waiting to be received.
2582 // --------------------------------------------------------------------------
2583 } else {
2584 int inbytes = nbytes;
2585 while (inbytes > 0) {
2586 int n = __recv(buf.data(), std::min(inbytes, buf_size), flags);
2587 inbytes -= n;
2588 discarded += n;
2589 } // -x- while nbytes -x-
2590 } // -x- if nbytes -x-
2591
2592 // --------------------------------------------------------------------------
2593 // Ignore exception so that we can simply indicate how many bytes were
2594 // successfully received and discarded.
2595 // --------------------------------------------------------------------------
2596 } catch(const rex::xALL e) {}
2597
2598 // --------------------------------------------------------------------------
2599 // Erase all data to prevent data from being unexpectedly leaked to any other
2600 // memory allocations later that use some or all of the same memory.
2601 // --------------------------------------------------------------------------
2602 for (int i = nbytes <= 0 ? buf_size : std::min(nbytes, buf_size); i > 0; i--) {
2603 buf[i] = 0;
2604 } // -x- for i -x-
2605
2606 return discarded;
2607 }; // -x- int discard -x-
2608
2609 /*======================================================================*//**
2610 @brief
2611 Find out what the current EoL (End of Line) sequence is set to.
2612 @warning
2613 To send an EoL sequence do not use `send(r.eol())` because it may not be
2614 initialized yet and the endpoint you're sending to may seem unresponsive or
2615 other unexpected behaviour may occur.@n
2616 @n
2617 To send an EoL sequence properly, use @ref sendline(); although specifying no
2618 parameters is more efficient than specifying an empty string (@c ""), the
2619 specialized @ref send_eol() method is the most efficient option for sending
2620 an EoL sequence separately.
2621 @note
2622 An empty EoL sequence means that CRLF will be sent by @ref sendline(), and
2623 that @ref recvline(), and @ref recv_rline() will automatically detect from
2624 one of @c CR, @c CRLF, @c LF, and @c LFCR.
2625 @returns Current EoL sequence
2626 @see eol_adoption
2627 @see eol_fix_printf
2628 @see printfline
2629 @see recvline
2630 @see recv_rline
2631 @see sendline
2632 @see send_eol
2633 @see vprintfline
2634 @qualifier TLS
2635 *///=========================================================================
2636 const std::string eol() noexcept { return __eol; }; // -x- std::string eol -x-
2637
2638 /*======================================================================*//**
2639 @brief
2640 Set EoL (End of Line) sequence. This sequence is used by recvline(),
2641 recv_rline(), sendline(), and related functions, and it defaults to an empty
2642 string which results in the EoL sequence being detected automatically
2643 on-the-fly.
2644
2645 - @c "" (empty string) = automatically detect on-the-fly (default)
2646 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
2647 - @c \\r (CR) = Carriage Return (typical for MacOS)
2648 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
2649
2650 @note
2651 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
2652 IMAP4, FINGER, NICNAME/WHOIS, etc.).
2653 @returns The same rsocket object so as to facilitate stacking
2654 @see eol_adoption
2655 @see eol_fix_printf
2656 @see printfline
2657 @see vprintfline
2658 @see recv_rline
2659 @see recvline
2660 @see sendline
2661 @see send_eol
2662 @qualifier TLS
2663 *///=========================================================================
2664 rsocket* eol(
2665 /// EoL sequence as an ASCIIZ string
2666 const char* eol) noexcept {
2667 __eol = std::string(eol);
2668 __eol_out = __eol.empty() ? __CRLF : __eol;
2669 return this;
2670 }; // -x- rsocket* eol -x-
2671
2672 /*======================================================================*//**
2673 @brief
2674 Set EoL (End of Line) sequence. This sequence is used by recvline(),
2675 recv_rline(), sendline(), and related functions, and it defaults to an empty
2676 string which results in the EoL sequence being detected automatically
2677 on-the-fly.
2678
2679 - @c "" (empty string) = automatically detect on-the-fly (default)
2680 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
2681 - @c \\r (CR) = Carriage Return (typical for MacOS)
2682 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
2683
2684 @note
2685 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
2686 IMAP4, FINGER, NICNAME/WHOIS, etc.).
2687 @returns The same rsocket object so as to facilitate stacking
2688 @see eol_fix_printf
2689 @see printfline
2690 @see vprintfline
2691 @see recv_rline
2692 @see recvline
2693 @see sendline
2694 @see send_eol
2695 @qualifier TLS
2696 *///=========================================================================
2697 rsocket* eol(
2698 /// EoL sequence as an std::string object
2699 const std::string eol) noexcept {
2700 __eol = eol;
2701 return this;
2702 }; // -x- rsocket* eol -x-
2703
2704 /*======================================================================*//**
2705 @brief
2706 Configure EoL adoption policy for the @ref recvline() and @ref recv_rline()
2707 methods. By default, @ref rsocket is configured with the EoL adoption policy
2708 enabled alongside an empty @ref eol() sequence, which results in the default
2709 operation being that the EoL sequence automatically gets detected and updated
2710 internally upon the first use of either the @ref recvline() or
2711 @ref recv_rline method.
2712
2713 The EoL adoption policy is only effective when the @ref eol() sequence is not
2714 defined (which is indicated by an empty EoL sequence string).
2715
2716 The EoL sequence is updated only when the EoL sequence string is empty, and
2717 when this EoL adoption policy is enabled.
2718 @returns The same rsocket object so as to facilitate stacking
2719 @see eol
2720 @see eol_index
2721 @see is_eol_adoption
2722 @see recv_rline
2723 @see recvline
2724 @see sendline
2725 @see send_eol
2726 @qualifier TLS
2727 *///=========================================================================
2728 rsocket* eol_adoption(
2729 /// TRUE = enable EoL adoption (default)@n
2730 /// FALSE = disable EoL adoption
2731 const bool flag) noexcept {
2732 __eol_fix_printf = flag;
2733 return this;
2734 }; // -x- rsocket* eol_adoption -x-
2735
2736 /*======================================================================*//**
2737 @brief
2738 Returns a String containing the EoL character sequence that was consumed by
2739 the most recent successful call to the @ref recvline() or @ref recv_rline
2740 method ("successful" in this context means that the received line was
2741 terminated by a valid EoL character sequence; otherwise the
2742 previous/unmodified value is returned).
2743 @warning
2744 This method must not be used to determine whether the @ref recvline() method
2745 successfully consumed an EoL character sequence.&nbsp; The reason for this is
2746 that @ref recvline() doesn't update this string when it doesn't consume an
2747 EoL character sequence, hence interpreting its results can (and likely will)
2748 lead to false positives.
2749 @returns EoL character sequence
2750 @see eol
2751 @see eol_adoption
2752 @see recv_rline
2753 @see recvline
2754 @qualifier TLS
2755 *///=========================================================================
2756 const std::string eol_consumed_seq() noexcept { return __eol_consumed_seq; }; // -x- std::string eol_consumed_seq -x-
2757
2758 /*======================================================================*//**
2759 @brief
2760 Configure EoL substitution policy for the @ref printf(), @ref printfline(),
2761 @ref vprintf(), and @ref vprintfline() methods. By default, @ref rsocket
2762 substitutes printf's @c \\n sequence with the EoL sequence (if defined), but
2763 this method can be used to disable this behaviour.
2764 @note
2765 The @c \\n sequence used in the printf format string normally coincides with
2766 the local Operating System's newline standard, which is very likely different
2767 from the @ref rsocket endpoint's newline standard, and/or the newline
2768 standard of the protocol being implemented. This policy setting makes it
2769 possible to control whether to use the configured EoL sequence when sending a
2770 formatted string to the endpoint.
2771 @returns The same rsocket object so as to facilitate stacking
2772 @see eol
2773 @see is_eol_fix_printf
2774 @see printf
2775 @see printfline
2776 @see vprintf
2777 @see vprintfline
2778 @qualifier TLS
2779 *///=========================================================================
2780 rsocket* eol_fix_printf(
2781 /// TRUE = enable EoL substitution (default)@n
2782 /// FALSE = disable EoL substitution
2783 const bool flag) noexcept {
2784 __eol_fix_printf = flag;
2785 return this;
2786 }; // -x- rsocket* eol_fix_printf -x-
2787
2788 /*======================================================================*//**
2789 @brief
2790 Finds the first instance of the EoL sequence and returns its offset (which is
2791 effectively the same as the size of the text, not including the characters
2792 that the EoL sequence is comprised of).
2793
2794 @note
2795 This method is specialized primarily for internal use by the @ref recvline()
2796 and @ref recv_rline() methods, but is made available here in case there's a
2797 need to check in-memory text using this rsocket's EoL detection policy.
2798 @returns Size of EoL sequence
2799 @returns -1 if EoL sequence wasn't found
2800 @see eol
2801 @see eol_adoption
2802 @qualifier TLS
2803 *///=========================================================================
2804 const int eol_index(
2805 /// Buffer that probably contains at least one EoL sequence
2806 const std::string buffer,
2807 /// Size of string with EoL character sequence included (will be updated by this method)
2808 int* with_eol_size) noexcept {
2809
2810 // --------------------------------------------------------------------------
2811 // An EoL character sequence was specified, a simple search will suffice.
2812 // --------------------------------------------------------------------------
2813 if (!__eol.empty()) {
2814//std::cout << "!__eol.empty() ------------------------------ String is not empty" << std::endl;
2815 int pos = buffer.find(__eol);
2816 if (pos >= 0) *with_eol_size = pos + __eol.size(); // Update size of EoL sequence
2817//std::cout << "------1------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2818 return pos;
2819 } // -x- if !__eol.empty() -x-
2820//std::cout << "__eol.empty() ------------------------------ String is empty" << std::endl;
2821
2822 // --------------------------------------------------------------------------
2823 // Automatic detection of EoL sequence, so a more flexible approach will be
2824 // required.
2825 //
2826 // Search for all four possible EoL sequences (as indicated by an empty EoL
2827 // character sequence string):
2828 //
2829 // CRLF: Common for text lines with internet protocols such as SMTP, POP3,
2830 // IMAP4, HTTP, FINGER, WHOIS, etc. Also: DOS and OS/2 EoL sequence.
2831 //
2832 // CR: MacOS EoL character.
2833 //
2834 // LF: UNIX/Linux EoL character.
2835 //
2836 // LFCR: Extremely rare, but I've encountered multiple instances where the
2837 // intended CRLF was reversed to LFCR, possibly due to a programming
2838 // error or an artifact of inappropriate/unintended endian conversion.
2839 // --------------------------------------------------------------------------
2840 int pos = buffer.find(__CR); // CR or CRLF
2841 if (pos == (buffer.size() - 1)) { // EoL sequence not found (this handles a specific empty-buffer edge case caused by SSL background chatter that's very rare but causes problems that are nearly impossible to debug when they do occur)
2842 *with_eol_size = pos + 1;
2843// *with_eol_size = 1;
2844//std::cout << "------2------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2845 return pos;
2846 } else if (pos >= 0) { // EoL sequence found
2847 *with_eol_size = pos + (buffer[pos + 1] == __LF ? 2 : 1); // 2=CRLF, 1=CR
2848// *with_eol_size = buffer[pos + 1] == __LF ? 2 : 1; // 2=CRLF, 1=CR
2849 if (__eol_adoption) {
2850 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
2851 __eol.resize(*with_eol_size - pos); // Truncate extra characters
2852 } // -x- if __eol_adoption -x-
2853//std::cout << "------3------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2854 return pos; // Either way, we're done
2855 } // -x- if pos -x-
2856
2857 pos = buffer.find(__LF); // LF or LFCR
2858 if (pos == (buffer.size() - 1)) { // EoL sequence not found (this handles a specific empty-buffer edge case caused by SSL background chatter that's very rare but causes problems that are nearly impossible to debug when they do occur)
2859 *with_eol_size = pos + 1;
2860// *with_eol_size = 1;
2861//std::cout << "------4------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2862 return pos;
2863 } else if (pos >= 0) { // EoL sequence found
2864 *with_eol_size = pos + (buffer[pos + 1] == __CR ? 2 : 1); // 2=LFCR, 1=LF
2865// *with_eol_size = buffer[pos + 1] == __CR ? 2 : 1; // 2=LFCR, 1=LF
2866 if (__eol_adoption) {
2867 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
2868 __eol.resize(*with_eol_size - pos); // Truncate extra characters
2869 } // -x- if __eol_adoption -x-
2870//std::cout << "------5------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2871 return pos; // Either way, we're done
2872 } // -x- if pos -x-
2873
2874//std::cout << "------6------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2875 return pos;
2876 }; // -x- int eol_index -x-
2877
2878 /*======================================================================*//**
2879 @brief
2880 Find out if the stream is at its end and that this @ref rsocket's internal
2881 buffer (if one had been set up by the @ref recvline() method) is empty. This
2882 doesn't necessarily mean that the stream is closed; but rather that the
2883 endpoint just hasn't sent any more data (yet).
2884
2885 If the stream isn't open, then this method will always return @c true to
2886 implicitly indicate that there's no more data.
2887
2888 @pre
2889 For this method to work properly, you need to specify a timeout either with
2890 the timeout parameter, or by setting the @ref timeout() for this rsocket (you
2891 can still override this rsocket's timeout by specifying a timeout here).
2892
2893 It's better (more efficient) to use this method instead of the @c MSG_PEEK
2894 flag (with the various @c recv methods) to determine whether any data is
2895 waiting on the stream (e.g., data that's received by the sockets, but not by
2896 any @c recv methods yet) because this method is specialized in handling this
2897 particular condition and responds with an easy-to-use boolean flag.
2898
2899 @note
2900 EoS is an acronym for: End of Stream
2901
2902 @throws randolf::rex::xEBADF The underlying socket is not open
2903 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2904 part of the user address space
2905 @throws randolf::rex::xEINTR Interrupted by a signal
2906 @throws randolf::rex::xENOMEM Insufficient memory
2907 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2908 doesn't refer to a socket
2909 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
2910 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
2911 is a highly improbable chance that a timeout could still occur if the
2912 data is read by another thread before the `recv(..., MSG_PEEK)` call)
2913 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
2914 there's no new data
2915
2916 @returns TRUE = End of Stream (no data, or the stream @ref is_closed())
2917 @returns FALSE = More data is ready to be received
2918 @see is_closed
2919 @see is_open
2920 @qualifier TLS
2921 *///=========================================================================
2922// TODO: Include exceptions from recv() in documentation (above) method (once they're ready)
2923 const bool eos(
2924 /// Number of milliseconds to wait
2925 const int timeout = 0) {
2926
2927 // --------------------------------------------------------------------------
2928 // Check internal buffer first.
2929 // --------------------------------------------------------------------------
2930 if (__buffer != nullptr && __buffer_head != __buffer_tail) return false;
2931
2932 // --------------------------------------------------------------------------
2933 // Check for data that's not stored in internal buffers.
2934 // --------------------------------------------------------------------------
2935 if (__socket_open) {
2936
2937 // --------------------------------------------------------------------------
2938 // Timeout polling (in case a timeout was specified).
2939 // --------------------------------------------------------------------------
2940 try {
2941 if (timeout != 0) {
2942 poll(POLLIN, timeout);
2943 } else {
2944 std::shared_ptr<timeval> tv = getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO);
2945 ppoll(POLLIN, tv->tv_sec, tv->tv_usec * 1000); // 1 microsecond = 1,000 nanoseconds
2946 }
2947 } catch (const randolf::rex::xETIMEDOUT e) {
2948 return true;
2949 } // This is a specialized catch situation -- the ::recv function could still return an ETIMEDOUT error
2950
2951 // --------------------------------------------------------------------------
2952 // Non-blocking socket will return -1 and set errno == EAGAIN or EWOULDBLOCK
2953 // if there is simply no new data. In case of graceful connection shutdown
2954 // by the remote peer, recv() will return 0.
2955 // --------------------------------------------------------------------------
2956 char z; // Temporary throw-away variable
2957 if (::recv(__socket_fd, &z, 1, MSG_PEEK | MSG_DONTWAIT) == 0) return true; // No more data; this is EoS
2958
2959 } // -x- if __socket_open -x-
2960
2961 return !__socket_open; // Effectively, this is: return closed();
2962 }; // -x- bool eos -x-
2963
2964 /*======================================================================*//**
2965 @brief
2966 Find out what the specified IP address string's family (@c SO_DOMAIN) is.
2967 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
2968 address is not available on any local interface
2969 @throws randolf::rex::xEAI_NONAME If @c address is an empty string
2970 @returns @c AF_UNSPEC (if family couldn't be determined)
2971 @returns @c AF_INET (IPv4 address)
2972 @returns @c AF_INET6 (IPv6 address)
2973 @returns @c AF_UNIX (UNIX Domain address)
2974 @returns ...or other family as applicable
2975 @qualifier TLS
2976 *///=========================================================================
2977 int static family(
2978 /// Address, similar to @ref bind() addressing, including non-standard "if="
2979 /// variant that names a network interface
2980 const std::string address,
2981 /// Preferred family to return first (used only with interface mode where the
2982 /// network interface is specified after the "if=" prefix); the default value
2983 /// of @c AF_UNSPEC will return the first family interface found
2984 const int preferred_family = AF_UNSPEC) {
2985
2986 // --------------------------------------------------------------------------
2987 // Simple checks first.
2988 // --------------------------------------------------------------------------
2989 if (address.size() == 0) randolf::rex::mk_exception("", EAI_NONAME);
2990 if (address.front() == '/') return AF_UNIX;
2991
2992 // --------------------------------------------------------------------------
2993 // if=<interface-name>: Same "interface" option that we support in bind()
2994 // --------------------------------------------------------------------------
2995 if (address.starts_with("if=")) {
2996 //std::cout << "address=" << address.substr(3).c_str() << std::endl; // Debug
2997 struct ifaddrs *ifaddr; // Chained interface addresses structure
2998 if (::getifaddrs(&ifaddr) == -1) { // Error occurred, so we're done here
2999 freeifaddrs(ifaddr); // Memory management
3000 randolf::rex::mk_exception("getifaddrs() error (prequisite to searching for " + address + ")", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
3001 } // -x- if getifaddrs -x
3002
3003 // --------------------------------------------------------------------------
3004 // Iterate through interface addresses. Each address should have a different
3005 // address family/domain, and is never AF_UNSPEC (this should never happen
3006 // because it wouldn't make sense, but if the network implementation was
3007 // broken and including an AF_UNSPEC family/domain, it would be useless for
3008 // the purposes of calling the ::socket() function so our code will just
3009 // ignore it anyway.
3010 // --------------------------------------------------------------------------
3011 int first_family = AF_UNSPEC; // We're using AF_UNSPEC (0) to indicate that there isn't a family/domain
3012 for (struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
3013 if (ifa->ifa_addr == nullptr) continue; // No address structure, so this isn't useful to us at all
3014 if (ifa->ifa_addr->sa_family == AF_UNSPEC) continue; // Skip AF_UNSPEC family/domain, as we can't use this
3015 if (address.compare(3, -1, ifa->ifa_name) != 0) continue; // Not the interface name we're looking for
3016
3017 // --------------------------------------------------------------------------
3018 // Return current address's family if preferred_family is not specified (if
3019 // it's set to AF_UNSPEC {0}), or if it matches the current address.
3020 // --------------------------------------------------------------------------
3021 //std::cout << "if=" << ifa->ifa_name << " family=" << ifa->ifa_addr->sa_family << std::endl; // Debug
3022 if (preferred_family == AF_UNSPEC || preferred_family == ifa->ifa_addr->sa_family) {
3023 first_family = ifa->ifa_addr->sa_family; // Arbitrarily re-using first_family variable
3024 freeifaddrs(ifaddr); // Memory management
3025 return first_family; // Return current family/domain (re-used first_family variable)
3026 } // -x- if preferred_family -x-
3027
3028 // --------------------------------------------------------------------------
3029 // Keep track of only the first_family/domain that we found; if we can't find
3030 // the preferred_family, then we'll be able to return the first one we found.
3031 // --------------------------------------------------------------------------
3032 if (first_family == AF_UNSPEC && ifa->ifa_addr->sa_family != AF_UNSPEC) first_family = ifa->ifa_addr->sa_family;
3033 //std::cout << " first_family=" << first_family << std::endl; // Debug
3034 } // -x- for *ifa -x-
3035
3036 // --------------------------------------------------------------------------
3037 // Return first_family/domain, if there was one; otherwise, throw exception.
3038 // --------------------------------------------------------------------------
3039 freeifaddrs(ifaddr); // Memory management
3040 if (first_family != AF_UNSPEC) return first_family;
3041 randolf::rex::mk_exception("Interface (" + address.substr(3) + ") doesn't exist" + (preferred_family == AF_UNSPEC ? "" : " or the preferred family/domain (" + std::to_string(preferred_family) + ") is not supported"), EADDRNOTAVAIL);
3042 } // -x- if /^if=/ -x-
3043
3044 // --------------------------------------------------------------------------
3045 // Attempt to detect IPv4 or IPv6 using a simple shortcut with ::inet_pton(),
3046 // which requires some additional memory allocation (that the simple checks
3047 // from earlier don't need).
3048 // --------------------------------------------------------------------------
3049 char buf[sizeof(struct in6_addr)]; // Binary address storage
3050 if (::inet_pton(AF_INET, address.c_str(), buf) == 1) return AF_INET;
3051 if (::inet_pton(AF_INET6, address.c_str(), buf) == 1) return AF_INET6;
3052
3053 // --------------------------------------------------------------------------
3054 // Throw xEAI_FAMILY exception because no family/domain was found.
3055 // --------------------------------------------------------------------------
3056 randolf::rex::mk_exception("No family/domain available for: " + address, EAI_FAMILY);
3057 }; // -x- int family -x-
3058
3059 /*======================================================================*//**
3060 @brief
3061 Get peer name returns the address of the socket as a sockaddr_storage
3062 structure.
3063
3064 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
3065 that aids in the prevention of resource leaks).
3066
3067 @throws randolf::rex::xEBADF The underlying socket is not open
3068 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3069 part of the user address space
3070 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3071 valid for this socket's family (a.k.a., communication domain)
3072 @throws randolf::rex::xENOBUFS Insufficient memory
3073 @throws randolf::rex::xENOTCONN Underlying socket is not connected
3074 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3075 doesn't refer to a socket
3076
3077 @returns sockaddr_storage structure
3078 @qualifier POSIX
3079 @qualifier TLS
3080 *///=========================================================================
3081 const std::shared_ptr<sockaddr_storage> getpeername() {
3082 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3083 socklen_t len = sizeof(sockaddr_storage);
3084 __rc_check(::getpeername(__socket_fd, (struct sockaddr*)sa.get(), &len));
3085 return sa;
3086 }; // -x- std::shared_ptr<sockaddr_storage> getpeername -x-
3087
3088 /*======================================================================*//**
3089 @brief
3090 Get peer name returns the address of the socket as a std::string object.
3091
3092 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3093 (e.g., because the @c family doesn't utilize or support an address
3094 {or the format isn't known}
3095 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3096 @throws randolf::rex::xEBADF The underlying socket is not open
3097 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3098 part of the user address space
3099 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3100 valid for this socket's family (a.k.a., communication domain)
3101 @throws randolf::rex::xENOBUFS Insufficient memory
3102 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3103 @throws randolf::rex::xENOTCONN Underlying socket is not connected
3104 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3105 doesn't refer to a socket
3106
3107 @returns string representation of peer name
3108 @qualifier TLS
3109 *///=========================================================================
3110 const std::string getpeername_ntop() {
3111 sockaddr_storage sa;
3112 socklen_t len = sizeof(sockaddr_storage);
3113 __rc_check(::getpeername(__socket_fd, (sockaddr*)&sa, &len));
3114 return inet_ntop(&sa);
3115 }; // -x- std::string getpeername_ntop -x-
3116
3117 /*======================================================================*//**
3118 @brief
3119 Get specified "sockaddr_storage" structure's address as a "sockaddr"
3120 structure, for sockets in one of the supported families:
3121
3122 - AF_INET (IPv4)
3123 - AF_INET6 (IPv6)
3124 - AF_UNIX (Domain socket path)
3125 - AF_PACKET (Ethernet node/mac. address)
3126
3127 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3128 (e.g., because the @c family doesn't utilize or support an address
3129 {or the format isn't known}
3130
3131 @returns pointer to sockaddr structure within provided sockaddr_storage
3132 @see bind
3133 @see mk_sockaddr_storage
3134 @see recvfrom
3135 @see sendto
3136 @see sendzto
3137 @qualifier POSIX
3138 @qualifier TLS
3139 *///=========================================================================
3140 static sockaddr* getsockaddr(
3141 /// The @c sockaddr_storage structure wherein @c sockaddr will be referenced from
3142 const sockaddr_storage* sa) {
3143 switch (sa->ss_family) {
3144 case AF_INET: // IPv4 address
3145 return (sockaddr*)&(((struct sockaddr_in*)sa)->sin_addr);//->sin_addr);
3146 case AF_INET6: // IPv6 address
3147 return (sockaddr*)&(((struct sockaddr_in6*)sa)->sin6_addr);//->sin6_addr);
3148 case AF_UNIX: // UNIX (path) domain socket
3149 return (sockaddr*)&(((struct sockaddr_un*)sa)->sun_path);//->sun_path);
3150 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
3151 // case /*AF_LINK* /18: // Link layer interface (arp)
3152 // break;
3153 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3154 return (sockaddr*)&(((struct sockaddr_ll*)sa)->sll_addr);//->sll_addr);
3155 } // -x- switch family -x-
3156 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY"); // Everything else
3157 }; // -x- sockaddr* getsockaddr -x-
3158
3159 /*======================================================================*//**
3160 @brief
3161 Get socket name returns the address of the socket as a "sockaddr_storage"
3162 structure.
3163
3164 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
3165 that aids in the prevention of resource leaks).
3166
3167 @throws randolf::rex::xEBADF The underlying socket is not open
3168 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3169 part of the user address space
3170 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3171 valid for this socket's family (a.k.a., communication domain)
3172 @throws randolf::rex::xENOBUFS Insufficient memory
3173 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3174 doesn't refer to a socket
3175
3176 @returns sockaddr_storage structure
3177 @qualifier POSIX
3178 @qualifier TLS
3179 *///=========================================================================
3180 const std::shared_ptr<sockaddr_storage> getsockname() {
3181 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3182 socklen_t len = sizeof(sockaddr_storage);
3183 __rc_check(::getsockname(__socket_fd, (struct sockaddr*)sa.get(), &len));
3184 return sa;
3185 }; // -x- std::string getsockname -x-
3186
3187 /*======================================================================*//**
3188 @brief
3189 Get socket name returns the name of the socket as a std::string object.
3190
3191 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3192 (e.g., because the @c family doesn't utilize or support an address
3193 {or the format isn't known}
3194 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3195 @throws randolf::rex::xEBADF The underlying socket is not open
3196 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3197 part of the user address space
3198 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3199 valid for this socket's family (a.k.a., communication domain)
3200 @throws randolf::rex::xENOBUFS Insufficient memory
3201 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3202 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3203 doesn't refer to a socket
3204
3205 @returns string representation of socket name
3206 @qualifier TLS
3207 *///=========================================================================
3208 const std::string getsockname_ntop() {
3209 sockaddr_storage sa;
3210 socklen_t len = sizeof(sockaddr_storage);
3211 __rc_check(::getsockname(__socket_fd, (sockaddr*)&sa, &len));
3212 return inet_ntop(&sa);
3213 }; // -x- std::string getsockname_ntop -x-
3214
3215 /*======================================================================*//**
3216 @brief
3217 Get socket option details in the form of an integer.
3218
3219 Most options return an integer, with the remaining options returning a
3220 pointer to a structure wrapped in a std::shared_ptr (a C++ smart pointer that
3221 aids in the prevention of resource leaks); the primitive types int, u_int,
3222 and u_char are not wrapped in C++ smart pointers because returning them by
3223 value is more efficient since allocating memory for an entire structure isn't
3224 needed.
3225
3226 @post
3227 It is up to the developer to know which return type is needed according to
3228 the socket option, otherwise an exception will likely be thrown -- in some
3229 cases where the wrong type will seem to work, this is due to the wrong type
3230 providing a minimally sufficient amount of memory for the storage of the
3231 resulting structure.
3232
3233 @par Notes
3234 The returned values/structures are not marked as "const" because they may
3235 need to be modified for unforseen purposes. Modifying the returend values or
3236 structures is fine because they are intended to be independent and are
3237 expected to have no direct impact on the rsocket's internal variables and
3238 structures.
3239
3240 Templates in C++ aren't used here because they don't work properly for our
3241 needs due to neccesity to handle both fundamental types and structures; it
3242 turns out that mixing these is impossible when using the same function name,
3243 so this just doesn't work as well as we'd like it to. (We may try to work on
3244 this again in the future as time permits to provide an additional method for
3245 obtaining socket options, but with the intention of never removing this
3246 current set of methods so as to ensure backward compatibility in the future.)
3247
3248 @throws randolf::rex::xEBADF The underlying socket is not open
3249 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3250 part of the user address space
3251 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3252 valid for this socket's family (a.k.a., communication domain)
3253 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
3254 is not supported
3255 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3256 doesn't refer to a socket
3257
3258 @returns socket option value
3259 @qualifier TLS
3260 *///=========================================================================
3261 int getsockopt_int(
3262 /// The level at which the option resides; typically @c SOL_SOCKET
3263 const int level,
3264 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3265 const int option) {
3266 int value = 0;
3267 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3268 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3269 return value;
3270 }; // -x- int getsockopt_int -x-
3271
3272 /*======================================================================*//**
3273 @brief
3274 Get socket option details in the form of an unsigned integer.
3275 @copydetails getsockopt_int(const int, const int)
3276 @returns socket option value
3277 @qualifier TLS
3278 *///=========================================================================
3279 u_int getsockopt_u_int(
3280 /// The level at which the option resides; typically @c SOL_SOCKET
3281 const int level,
3282 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3283 const int option) {
3284 u_int value = 0;
3285 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3286 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3287 return value;
3288 }; // -x- u_int getsockopt_u_int -x-
3289
3290 /*======================================================================*//**
3291 @brief
3292 Get socket option details in the form of an unsigned character.
3293 @copydetails getsockopt_int(const int, const int)
3294 @returns socket option value
3295 @qualifier TLS
3296 *///=========================================================================
3297 u_char getsockopt_u_char(
3298 /// The level at which the option resides; typically @c SOL_SOCKET
3299 const int level,
3300 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3301 const int option) {
3302 u_char value = 0;
3303 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3304 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3305 return value;
3306 }; // -x- u_char getsockopt_u_char -x-
3307
3308 /*======================================================================*//**
3309 @brief
3310 Get socket option details in the form of a structure.
3311 @copydetails getsockopt_int(const int, const int);
3312 @returns socket option structure wrapped in std::shared_ptr
3313 @qualifier TLS
3314 *///=========================================================================
3315 std::shared_ptr<linger> getsockopt_linger(
3316 /// The level at which the option resides; typically @c SOL_SOCKET
3317 const int level,
3318 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3319 const int option) {
3320 std::shared_ptr value = std::make_shared<linger>();
3321 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3322 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3323 return value;
3324 }; // -x- linger getsockopt_linger -x-
3325
3326 /*======================================================================*//**
3327 @copydoc getsockopt_linger(const int, const int)
3328 @qualifier TLS
3329 *///=========================================================================
3330 std::shared_ptr<timeval> getsockopt_timeval(
3331 /// The level at which the option resides; typically @c SOL_SOCKET
3332 const int level,
3333 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3334 const int option) {
3335 std::shared_ptr value = std::make_shared<timeval>();
3336 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3337 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3338 return value;
3339 }; // -x- timeval getsockopt_timeval -x-
3340
3341 /*======================================================================*//**
3342 @copydoc getsockopt_linger(const int, const int)
3343 @qualifier TLS
3344 *///=========================================================================
3345 std::shared_ptr<in_addr> getsockopt_in_addr(
3346 /// The level at which the option resides; typically @c SOL_SOCKET
3347 const int level,
3348 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3349 const int option) {
3350 std::shared_ptr value = std::make_shared<in_addr>();
3351 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3352 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3353 return value;
3354 }; // -x- in_addr getsockopt_in_addr -x-
3355
3356 /*======================================================================*//**
3357 @copydoc getsockopt_linger(const int, const int)
3358 @qualifier TLS
3359 *///=========================================================================
3360 std::shared_ptr<ip_mreq> getsockopt_ip_mreq(
3361 /// The level at which the option resides; typically @c SOL_SOCKET
3362 const int level,
3363 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3364 const int option) {
3365 std::shared_ptr value = std::make_shared<ip_mreq>();
3366 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3367 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3368 return value;
3369 }; // -x- ip_mreq getsockopt_ip_mreq -x-
3370
3371 /*======================================================================*//**
3372 @copydoc getsockopt_linger(const int, const int)
3373 @qualifier TLS
3374 *///=========================================================================
3375 std::shared_ptr<ip_mreq_source> getsockopt_ip_mreq_source(
3376 /// The level at which the option resides; typically @c SOL_SOCKET
3377 const int level,
3378 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3379 const int option) {
3380 std::shared_ptr value = std::make_shared<ip_mreq_source>();
3381 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3382 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3383 return value;
3384 }; // -x- ip_mreq_source getsockopt_ip_mreq_source -x-
3385
3386 /*======================================================================*//**
3387 @copydoc getsockopt_linger(const int, const int)
3388 @qualifier TLS
3389 *///=========================================================================
3390 std::shared_ptr<icmp6_filter> getsockopt_icmp6_filter(
3391 /// The level at which the option resides; typically @c SOL_SOCKET
3392 const int level,
3393 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3394 const int option) {
3395 std::shared_ptr value = std::make_shared<icmp6_filter>();
3396 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3397 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3398 return value;
3399 }; // -x- icmp6_filter getsockopt_icmp6_filter -x-
3400
3401 /*======================================================================*//**
3402 @copydoc getsockopt_linger(const int, const int)
3403 @qualifier TLS
3404 *///=========================================================================
3405 std::shared_ptr<sockaddr_in6> getsockopt_sockaddr_in6(
3406 /// The level at which the option resides; typically @c SOL_SOCKET
3407 const int level,
3408 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3409 const int option) {
3410 std::shared_ptr value = std::make_shared<sockaddr_in6>();
3411 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3412 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3413 return value;
3414 }; // -x- sockaddr_in6 getsockopt_sockaddr_in6 -x-
3415
3416 /*======================================================================*//**
3417 @copydoc getsockopt_linger(const int, const int)
3418 @qualifier TLS
3419 *///=========================================================================
3420 std::shared_ptr<ip6_mtuinfo> getsockopt_ip6_mtuinfo(
3421 /// The level at which the option resides; typically @c SOL_SOCKET
3422 const int level,
3423 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3424 const int option) {
3425 std::shared_ptr value = std::make_shared<ip6_mtuinfo>();
3426 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3427 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3428 return value;
3429 }; // -x- ip6_mtuinfo getsockopt_ip6_mtuinfo -x-
3430
3431 /*======================================================================*//**
3432 @copydoc getsockopt_linger(const int, const int)
3433 @qualifier TLS
3434 *///=========================================================================
3435 std::shared_ptr<ipv6_mreq> getsockopt_ipv6_mreq(
3436 /// The level at which the option resides; typically @c SOL_SOCKET
3437 const int level,
3438 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3439 const int option) {
3440 std::shared_ptr value = std::make_shared<ipv6_mreq>();
3441 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3442 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3443 return value;
3444 }; // -x- ipv6_mreq getsockopt_ipv6_mreq -x-
3445
3446 /*======================================================================*//**
3447 @copydoc getsockopt_linger(const int, const int)
3448 @qualifier TLS
3449 *///=========================================================================
3450 std::shared_ptr<group_req> getsockopt_group_req(
3451 /// The level at which the option resides; typically @c SOL_SOCKET
3452 const int level,
3453 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3454 const int option) {
3455 std::shared_ptr value = std::make_shared<group_req>();
3456 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3457 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3458 return value;
3459 }; // -x- group_req getsockopt_group_req -x-
3460
3461 /*======================================================================*//**
3462 @copydoc getsockopt_linger(const int, const int)
3463 @qualifier TLS
3464 *///=========================================================================
3465 std::shared_ptr<group_source_req> getsockopt_group_source_req(
3466 /// The level at which the option resides; typically @c SOL_SOCKET
3467 const int level,
3468 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3469 const int option) {
3470 std::shared_ptr value = std::make_shared<group_source_req>();
3471 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3472 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3473 return value;
3474 }; // -x- group_source_req getsockopt_group_source_req -x-
3475
3476 /*======================================================================*//**
3477 @copydoc getsockopt_linger(const int, const int)
3478 @qualifier TLS
3479 *///=========================================================================
3480 template<class T> std::shared_ptr<T> getsockopt_other(
3481 /// The level at which the option resides; typically @c SOL_SOCKET
3482 const int level,
3483 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3484 const int option) {
3485 std::shared_ptr value = std::make_shared<T>();
3486 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3487 if (__debug) __debug_sockopt_other("getsockopt_other(socket{0x", level, option, socklen_t(sizeof(value)));
3488 return value;
3489 }; // -x- T getsockopt_other -x-
3490
3491 /*======================================================================*//**
3492 @brief
3493 Get underlying socket's address as a std::string, for sockets in one of the
3494 supported families:
3495
3496 - AF_INET (IPv4)
3497 - AF_INET6 (IPv6)
3498 - AF_UNIX (Domain socket path)
3499 - AF_PACKET (Ethernet node/mac. address)
3500
3501 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3502 (e.g., because the @c family doesn't utilize or support an address
3503 {or the format isn't known}
3504 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3505 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3506
3507 @returns string representation of underlying socket's address
3508 @see inet_ntop(sockaddr_storage*) static method
3509 @qualifier POSIX
3510 @qualifier TLS
3511 *///=========================================================================
3512 const std::string inet_ntop() { return inet_ntop((sockaddr_storage*)&__socket_addr); } // -x- std::string inet_ntop -x-
3513
3514 /*======================================================================*//**
3515 @brief
3516 Get specified "sockaddr_storage" structure's address as a std::string, for
3517 sockets in one of the supported families:
3518
3519 - AF_INET (IPv4)
3520 - AF_INET6 (IPv6)
3521 - AF_UNIX (Domain socket path)
3522 - AF_PACKET (Ethernet node/mac. address)
3523
3524 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3525 (e.g., because the @c family doesn't utilize or support an address
3526 {or the format isn't known}
3527 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3528 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3529
3530 @returns string representation of underlying socket's address
3531 @see inet_ntop() non-static method
3532 @qualifier POSIX
3533 @qualifier TLS
3534 *///=========================================================================
3535 static const std::string inet_ntop(
3536 /// Source structure that [should] contain address data
3537 sockaddr_storage* sa) {
3538 std::string str;
3539 switch (sa->ss_family) {
3540 case AF_INET: // IPv4 address
3541 {
3542 char ntop[sizeof(sockaddr_in)]{0};
3543 const char* rc = ::inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), ntop, sizeof(ntop));
3544 str = std::string(ntop);
3545 }
3546 break;
3547 case AF_INET6: // IPv6 address
3548 { // Debug
3549 char ntop[sizeof(sockaddr_in6)]{0};
3550 const char* rc = ::inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), ntop, sizeof(ntop));
3551 str = std::string(ntop);
3552 }
3553 break;
3554 case AF_UNIX: // UNIX (path) domain socket
3555 str = std::string(((struct sockaddr_un *)sa)->sun_path);
3556 break;
3557 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
3558 // case /*AF_LINK* /18: // Link layer interface (arp)
3559 // break;
3560 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3561 str = to_mac(((struct sockaddr_ll *)sa)->sll_addr);
3562 break;
3563 default: // Everything else
3564 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY");
3565 } // -x- switch family -x-
3566 return str;
3567 }; // -x- std::string inet_ntop -x-
3568
3569 /*======================================================================*//**
3570 @brief
3571 Find out whether an internal read buffer was allocated (this is most likely
3572 triggered by an attempt to read a line of text).
3573 @note
3574 The buffer_size() methods report on how much memory was allocated for the
3575 internal read buffer or to set its size (in bytes).
3576 @returns TRUE = an internal read buffer was allocated
3577 @returns FALSE = an internal read buffer was not allocated
3578 @see buffer_size
3579 @see buffer_size(const size_t nbytes)
3580 @qualifier TLS
3581 *///=========================================================================
3582 const bool is_buffered() noexcept { return __buffer != nullptr; }; // -x- bool is_buffered -x-
3583
3584 /*======================================================================*//**
3585 @brief
3586 Find out whether the underlying socket is not open (which may not be the same
3587 as specifically "closed" since a newly instantiated empty socket begins in a
3588 "not open" state despite the underlying socket not explicitly having been
3589 closed).
3590 @returns TRUE = not open
3591 @returns FALSE = open
3592 @see is_open()
3593 @qualifier TLS
3594 *///=========================================================================
3595 const bool is_closed() noexcept { return !__socket_open; }; // -x- bool is_closed -x-
3596
3597 /*======================================================================*//**
3598 @brief
3599 Find out whether the underlying socket is connected with/to an endpoint.
3600 @returns TRUE = open
3601 @returns FALSE = not open
3602 @qualifier TLS
3603 *///=========================================================================
3604 const bool is_connected() noexcept { return __socket_connected; }; // -x- bool is_connected -x-
3605
3606 /*======================================================================*//**
3607 @brief
3608 Find out whether the default byte order for this host is LSB (small endian).
3609 @note
3610 If you're trying to choose which endian type to use when designing a new
3611 internet protocol, then big endian is normally the better option. However,
3612 if your new protocol will only be used by hardware that all share the same
3613 endianness, then that endianness is probably the more optimal option since it
3614 will translate to an overall lesser consumption of CPU cycles by reducing or
3615 eliminating endianness conversions.
3616 @returns TRUE = LSB (little endian)
3617 @returns FALSE = MSB (big endian / network byte order)
3618 @qualifier TLS
3619 *///=========================================================================
3620 const bool is_endian_lsb() noexcept { return !__endian_is_msb; }; // -x- bool is_endian_lsb -x-
3621
3622 /*======================================================================*//**
3623 @brief
3624 Find out whether the default byte order for this host is MSB (big endian).
3625 @note
3626 Big endian is the standard known as "network byte order" that's also used in
3627 various header fields in internet packets.
3628 @n@n
3629 If you're trying to choose which endian type to use when designing a new
3630 internet protocol, then big endian is normally the better option. However,
3631 if your new protocol will only be used by hardware that all share the same
3632 endianness, then that endianness is probably the more optimal option since it
3633 will translate to an overall lesser consumption of CPU cycles by reducing or
3634 eliminating endianness conversions.
3635 @returns TRUE = MSB (big endian / network byte order)
3636 @returns FALSE = LSB (little endian)
3637 @qualifier TLS
3638 *///=========================================================================
3639 const bool is_endian_msb() noexcept { return __endian_is_msb; }; // -x- bool is_endian_msb -x-
3640
3641 /*======================================================================*//**
3642 @brief
3643 Find out if the EoL adoption policy is enabled for the @ref recvline() method
3644 (see the @ref eol_adoption method to find out how the dynamically-detected
3645 EoL sequence gets adopted, and under what conditions).
3646 @returns TRUE = EoL adoption is enabled
3647 @returns FALSE = EoL adoption is disabled
3648 @see eol
3649 @see eol_adoption
3650 @see eol_index
3651 @see recvline
3652 @see sendline
3653 @see send_eol
3654 @qualifier TLS
3655 *///=========================================================================
3656 const bool is_eol_adoption() noexcept {
3657 return __eol_adoption;
3658 }; // -x- bool is_eol_adoption -x-
3659
3660 /*======================================================================*//**
3661 @brief
3662 Find out if the EoL substitution policy is enabled for the @ref printf(),
3663 @ref printfline(), @ref vprintf(), and @ref vprintfline() methods.
3664 @returns TRUE = EoL substitution is enabled
3665 @returns FALSE = EoL substitution is disabled
3666 @see eol
3667 @see eol_fix_printf
3668 @see printf
3669 @see printfline
3670 @see sendline
3671 @see send_eol
3672 @see vprintf
3673 @see vprintfline
3674 @qualifier TLS
3675 *///=========================================================================
3676 const bool is_eol_fix_printf() noexcept {
3677 return __eol_fix_printf;
3678 }; // -x- bool is_eol_fix_printf -x-
3679
3680 /*======================================================================*//**
3681 @brief
3682 Find out whether the underlying socket is open.
3683 @returns TRUE = open
3684 @returns FALSE = not open
3685 @see is_closed()
3686 @qualifier TLS
3687 *///=========================================================================
3688 const bool is_open() noexcept { return __socket_open; }; // -x- bool is_open -x-
3689
3690 /*======================================================================*//**
3691 @brief
3692 Find out whether encrypted communications is enabled or disabled.
3693 @returns TRUE = encrypted communications is enabled
3694 @returns FALSE = encrypted communications is disabled
3695 @see tls(bool, TLS_FLAGS)
3696 @qualifier TLS
3697 *///=========================================================================
3698 const bool is_tls() noexcept { return __tls; }; // -x- bool is_tls -x-
3699
3700 /*======================================================================*//**
3701 @brief
3702 Find out whether TLS context is in TLS_CLIENT mode.
3703 @returns TRUE = TLS context is in TLS_CLIENT mode
3704 @returns FALSE = TLS context is in TLS_SERVER mode
3705 @see TLS_CLIENT
3706 @see tls()
3707 @qualifier TLS
3708 *///=========================================================================
3709 const bool is_tls_client_mode() noexcept { return !__tls_server_mode; }; // -x- bool is_tls_client_mode -x-
3710
3711 /*======================================================================*//**
3712 @brief
3713 Find out whether egress from encryption (to unencrypted mode) is allowed.
3714 @returns TRUE = egress from encrypted communications is allowed
3715 @returns FALSE = egress from encrypted communications is not allowed
3716 @see TLS_NO_EGRESS
3717 @see tls()
3718 @qualifier TLS
3719 *///=========================================================================
3720 const bool is_tls_egress_okay() noexcept { return __tls_egress; }; // -x- bool is_tls_egress_okay -x-
3721
3722 /*======================================================================*//**
3723 @brief
3724 Find out whether encrypted communications is exclusive.
3725 @returns TRUE = encrypted communications is exclusive
3726 @returns FALSE = encrypted communications is not exclusive
3727 @see tls()
3728 @qualifier TLS
3729 *///=========================================================================
3730 const bool is_tls_exclusive() noexcept { return __tls_exclusive; }; // -x- bool is_tls_exclusive -x-
3731
3732 /*======================================================================*//**
3733 @brief
3734 Find out whether ingress to encryption (from unencrypted mode) is allowed.
3735 @returns TRUE = ingress to encrypted communications is allowed
3736 @returns FALSE = ingress to encrypted communications is not allowed
3737 @see TLS_NO_INGRESS
3738 @see tls()
3739 @qualifier TLS
3740 *///=========================================================================
3741 const bool is_tls_ingress_okay() noexcept { return __tls_ingress; }; // -x- bool is_tls_ingress_okay -x-
3742
3743 /*======================================================================*//**
3744 @brief
3745 Find out whether TLS context is in TLS_SERVER mode.
3746 @returns TRUE = TLS context is in TLS_SERVER mode
3747 @returns FALSE = TLS context is in TLS_CLIENT mode
3748 @see TLS_SERVER
3749 @see tls()
3750 @qualifier TLS
3751 *///=========================================================================
3752 const bool is_tls_server_mode() noexcept { return __tls_server_mode; }; // -x- bool is_tls_server_mode -x-
3753
3754 /*======================================================================*//**
3755 @brief
3756 Find out whether SNI (Server Name Identifier) is enabled (configured, which
3757 implies that an internal callback function was also set up).
3758 @returns TRUE = SNI is enabled
3759 @returns FALSE = SNI is disabled
3760 @see tls_sni()
3761 @qualifier TLS
3762 *///=========================================================================
3763 const bool is_tls_sni() noexcept { return __tls_sni != nullptr; }; // -x- bool is_tls_sni -x-
3764
3765 /*======================================================================*//**
3766 @brief
3767 Find out whether SNI (Server Name Identifier) was matched, which means that
3768 we're using one of the supplementary TLS certificates that are included in
3769 the associated @ref rsocket_sni object as separate TLS contexts.
3770
3771 When this method returns @c TRUE, it means the @c OpenSSL callback (which
3772 occurs during the TLS handshake process) completed the TLS handshake with one
3773 of the TLS certificates that's included in this @c rsocket's @ref rsocket_sni
3774 object instead of the primary TLS certificate assigned to this @c rsocket,
3775 and this @c rsocket is using the respective TLS context instead of the
3776 primary (default) one.
3777 @returns TRUE = SNI was matched
3778 @returns FALSE = SNI wasn't matched
3779 @see name_sni
3780 @see tls_sni
3781 @qualifier TLS
3782 *///=========================================================================
3783 const bool is_tls_sni_match() noexcept { return __tls_sni_match; }; // -x- bool is_tls_sni_match -x-
3784
3785 /*======================================================================*//**
3786 @brief
3787 Enable listening mode for this rsocket to prepare it to accept() new inbound
3788 connections.
3789
3790 The backlog defaults to SOMAXCONN (4096 on Linux, and 128 on older systems),
3791 which is common on most systems. If a higher value is supplied that exceeds
3792 @c kern.somaxconn, the kernel will automatically override the backlog with
3793 the value stored in @c kern.somaxconn without generating any errors or
3794 warnings.
3795
3796 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
3797 @throws randolf::rex::xEADDRINUSE No ephemeral ports are available for
3798 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
3799 but it really isn't)
3800 @throws randolf::rex::xEBADF The underlying socket is not open
3801 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3802 doesn't refer to a socket
3803 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
3804 @returns The same rsocket object so as to facilitate stacking
3805 @see accept()
3806 @see accept_sp()
3807 @see accept4()
3808 @see accept4_sp()
3809 @see backlog()
3810 @see bind()
3811 @qualifier POSIX
3812 @qualifier TLS
3813 *///=========================================================================
3814 rsocket* listen(
3815 /// Backlog queue size (0 = uses rsocket's default; see @ref backlog for more
3816 /// details about this); specifying a non-zero backlog also updates rocket's
3817 /// internal default (SOMAXCONN is 4096 on Linux, and 128 on older systems)
3818 int backlog = 0) {
3819 if (__debug) debug("listen(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3820 + ", " + std::to_string(backlog)
3821 + ");");
3822
3823 // --------------------------------------------------------------------------
3824 // Use rsocket's default backlog if not specified, otherwise update it
3825 // with the new value specified here.
3826 // --------------------------------------------------------------------------
3827 if (backlog = 0) backlog = __socket_backlog;
3828 else __socket_backlog = backlog;
3829
3830 // --------------------------------------------------------------------------
3831 // Configure underlying socket to queue up to "backlog" inbound connections.
3832 // --------------------------------------------------------------------------
3833 __rc_check(::listen(__socket_fd, backlog));
3834
3835 return this;
3836 }; // -x- rsocket* listen -x-
3837
3838 /*======================================================================*//**
3839 @brief
3840 Convert an IPv4 address, IPv6 address, ethernet packet, or UNIX domain
3841 socket to a sockaddr_storage structure.
3842
3843 If service_name is an absolute path (that begins with a "/" charcter) and the
3844 family is set to AF_UNSPEC (the default), then the resulting family will be
3845 set to AF_UNIX.
3846
3847 @par Notes
3848 This method utilizes the results of getaddrinfo().
3849
3850 Other families like AF_LINK and AF_PACKET should work, but haven't been
3851 tested thoroughly. The additional support we provide for IPv4 and IPv6
3852 addresses is to copy the port number into the resulting structure (as a
3853 convenience).
3854
3855 @post
3856 The resulting sockaddr_storage structure is wrapped in std::shared_ptr (a C++
3857 smart pointer that aids in the prevention of resource leaks).
3858
3859 @par Threads
3860 This method is thread-safe.
3861
3862 @throws randolf::rex::xEAI_ADDRFAMILY If specified network host doesn't have
3863 any addresses in the specified address family
3864 @throws randolf::rex::xEAI_AGAIN Temporary failure code from DNS server (try
3865 again later)
3866 @throws randolf::rex::xEAI_BADFLAGS Invalid flags in @c hints.ai_flags (or
3867 `hints.ai_flags` included @c AI_CANONNAME with @c nullptr as @c name)
3868 @throws randolf::rex::xEAI_FAIL Permanent failure code from DNS server
3869 @throws randolf::rex::xEAI_FAMILY The specified family is not supported
3870 @throws randolf::rex::xEAI_MEMORY Out of memory
3871 @throws randolf::rex::xEAI_NONAME If node_name is nullptr or an empty string
3872 @throws randolf::rex::xEAI_SERVICE The specified service is not available
3873 for the specified socket type
3874 @throws randolf::rex::xEAI_SOCKTYPE The specified socket type is not
3875 supported
3876 @throws randolf::rex::xEAI_SYSTEM Other system error (use errno to determine
3877 what the error is, then run use @ref randolf::rex::rex::mk_exception
3878 to throw the correct exception)
3879
3880 @returns sockaddr_storage structure
3881 @see getsockaddr
3882 *///=========================================================================
3883 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
3884 /// IP address or UNIX domain socket address to convert
3885 const char* node_name,
3886 /// Port number (or service name used by some other families)
3887 const char* service_name = nullptr,
3888 /// Optional pointer to a helpful addrinfo structure
3889 const addrinfo* hints = nullptr) {
3890
3891 // --------------------------------------------------------------------------
3892 // Initial sanity checks.
3893 // --------------------------------------------------------------------------
3894 if (node_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME); // if (node_name == nullptr && service_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME);
3895
3896 // --------------------------------------------------------------------------
3897 // Handle any special cases.
3898 // --------------------------------------------------------------------------
3899 switch (hints == nullptr ? AF_UNSPEC : hints->ai_family) { // Default to AF_UNSPEC in the absence of hints
3900 case AF_UNSPEC:
3901 if (node_name[0] != '/') break;
3902 // Next entry MUST be "case AF_UNIX" for this fall-through to work
3903 case AF_UNIX:
3904 {
3905 // For some unknown reason, clang++ can't compile this line that
3906 // g++ has absolutely no trouble with at all:
3907 // std::shared_ptr sa = std::make_shared<sockaddr_storage>(AF_UNIX);
3908 // So, after wasting more than a year trying to figure out what the
3909 // hundreds of lines of cryptic errors meant, I eventually gave up
3910 // on clang++ for being such a dumbfuck, and used this work-around:
3911 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3912 sa->ss_family = AF_UNIX;
3913 std::strcpy(((struct sockaddr_un *)sa.get())->sun_path, node_name); // Copy path
3914 return sa;
3915 }
3916 break;
3917 } // -x- switch hints->family -x-
3918
3919 // --------------------------------------------------------------------------
3920 // Acquire addrinfo[] linked-list array.
3921 // --------------------------------------------------------------------------
3922 addrinfo* __addr_result; // This is temporary
3923//std::cout << " Making socket structure... node_name=" << node_name << " ...hints: ai_family=" << hints->ai_family << " ai_socktype=" << hints->ai_socktype << " ai_flags=" << hints->ai_flags << std::endl;
3924 randolf::rex::mk_exception("", getaddrinfo(node_name,
3925 service_name,
3926 hints,
3927 &__addr_result));
3928
3929 // --------------------------------------------------------------------------
3930 // Find first valid addrinfo[] array by trying to open a socket with it.
3931 // --------------------------------------------------------------------------
3932 for (addrinfo* ar = __addr_result; ar != nullptr; ar = ar->ai_next) {
3933//std::cout << " ... ai_family=" << ar->ai_family << " ai_socktype=" << ar->ai_socktype << " ai_flags=" << ar->ai_flags << std::endl; // *******************
3934// ***************************** TODO: Make sure this loop is working properly
3935 } // -x- for ar -x-
3936//std::cout << " !!! ai_family=" << __addr_result->ai_family << std::endl; // *******************
3937
3938 // --------------------------------------------------------------------------
3939 // Copy the address to "sa" and return it. The "addr" data for IPv4 and IPv6
3940 // addresses include the TCP/UDP port number (service) in first two bytes as
3941 // as an unsigned integer (u_int16), followed immediately by the IP address.
3942 //
3943 // We're taking the first record only. We assume that hints{} is defined on
3944 // an as-needed basis.
3945 //
3946 // Note: AF_LINK and AF_PACKET are not widely supported, but we've made an
3947 // effort to accomodate their usage.
3948 // --------------------------------------------------------------------------
3949 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3950 switch (__addr_result->ai_family) {
3951 case AF_INET: // IPv4 address
3952 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_in));
3953 break;
3954 case AF_INET6: // IPv6 address
3955 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_in6));
3956 break;
3957 /* // We're handling this earlier as a special case: TODO: Move special handling to here
3958 case AF_UNIX: // UNIX (path) domain socket
3959 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_un));
3960 break;
3961 */
3962 case /*AF_LINK*/18: // Link layer interface (arp)
3963 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_dl));
3964 break;
3965 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3966 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_ll));
3967 break;
3968 default: // Everything else
3969 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_storage));
3970 } // -x- switch family -x-
3971 freeaddrinfo(__addr_result); // We don't need this anymore
3972 return sa;
3973 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
3974
3975 /*======================================================================*//**
3976 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
3977 *///=========================================================================
3978 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
3979 /// IP address or UNIX domain socket address to convert
3980 const char* node_name,
3981 /// Port number
3982 const u_int16_t service_name,
3983 /// Optional pointer to a helpful addrinfo structure
3984 const addrinfo* hints = nullptr) {
3985 std::string port = std::to_string(service_name);
3986 return mk_sockaddr_storage(node_name, port.c_str(), hints);
3987 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
3988
3989 /*======================================================================*//**
3990 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
3991 *///=========================================================================
3992 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
3993 /// IP address or UNIX domain socket address to convert
3994 const std::string node_name,
3995 /// Port number
3996 const u_int16_t service_name,
3997 /// Optional pointer to a helpful addrinfo structure
3998 const addrinfo* hints = nullptr) {
3999 std::string port = std::to_string(service_name);
4000 return mk_sockaddr_storage(node_name.c_str(), port.c_str(), hints);
4001 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
4002
4003 /*======================================================================*//**
4004 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
4005 *///=========================================================================
4006 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
4007 /// IP address or UNIX domain socket address to convert
4008 const std::string node_name,
4009 /// Port number (or server name used by some other families)
4010 const std::string service_name,
4011 /// Optional pointer to a helpful addrinfo structure
4012 const addrinfo* hints = nullptr) {
4013 return mk_sockaddr_storage(node_name.c_str(), service_name.c_str(), hints);
4014 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
4015
4016 /*======================================================================*//**
4017 @brief
4018 Specify a name for this rsocket.
4019
4020 This is an arbitrary name that is entirely optional, and should be regarded
4021 as similar to the naming of threads.
4022 @returns The same rsocket object so as to facilitate stacking
4023 *///=========================================================================
4024 rsocket* name(
4025 /// Name to assign to this @c rsocket
4026 const std::string name) noexcept {
4027 // const std::lock_guard<std::mutex> lock(__name);
4028 __name = name;
4029 return this;
4030 }; // -x- rsocket* name -x-
4031
4032 /*======================================================================*//**
4033 @brief
4034 Find out what this rsocket's name is.
4035
4036 The built-in SNI mechanism will overwrite this data to indicate the hostname
4037 that was specified by the TLS-encrypted endpoint that triggered an internal
4038 SNI callback -- use the @ref name_sni() method to find out which hostname is
4039 actually being used by TLS.
4040 @returns The name of this rsocket (or an empty @c std::string if this rsocket
4041 doesn't have a name)
4042 @see name_sni
4043 @see tls_sni
4044 *///=========================================================================
4045 std::string name() noexcept {
4046 // const std::lock_guard<std::mutex> lock(__name);
4047 return __name;
4048 }; // -x- std::string name -x-
4049
4050 /*======================================================================*//**
4051 @brief
4052 Find out what this rsocket's actual TLS SNI hostname is.
4053
4054 This is the exact - or closest-matching (in the case of wildcards) - hostname
4055 associated with an actual TLS certificate that was provided in the configured
4056 @ref rsocket_sni object.
4057 @returns The hostname associated with the TLS certificate selected by SNI
4058 @see name
4059 @see tls_sni
4060 @qualifier TLS
4061 *///=========================================================================
4062 std::string name_sni() noexcept {
4063 return __name_sni;
4064 }; // -x- std::string name_sni -x-
4065
4066 /*======================================================================*//**
4067 @brief
4068 Get socket I/O statistics from internally-tracked socket I/O counters.
4069
4070 The number of bytes transmitted and received is tracked internally, so that
4071 the information can be used later in logging. These statistics are available
4072 at all times, but for logging purposes it makes the most sense to copy this
4073 information after the rsocket is closed, at which time the final statistics
4074 will continue to be available until the rsocket's destructor takes over.
4075
4076 @par Threads
4077 This method is threadsafe.
4078 @returns rsocket_io wrapped in a std::shared_ptr object to help ease memory
4079 management efforts
4080 @see net_io_final
4081 @see net_io_update
4082 @see printf
4083 @qualifier TLS
4084 *///=========================================================================
4085 std::shared_ptr<rsocket_io> net_io() noexcept {
4086 std::shared_ptr stats = std::make_shared<rsocket_io>();
4087 stats->bytes_rx = __bytes_rx;
4088 stats->bytes_tx = __bytes_tx;
4089 stats->crypt_rx = __crypt_rx;
4090 stats->crypt_tx = __crypt_tx;
4091 stats->is_final = false;
4092 return stats;
4093 }; // -x- std::shared_ptr<rsocket_io> net_io -x-
4094
4095 /*======================================================================*//**
4096 @brief
4097 Get socket I/O statistics from internally-tracked socket I/O counters as a
4098 pre-formatted std::string object.
4099
4100 The number of bytes transmitted and received is tracked internally, so that
4101 the information can be used later in logging. These statistics are available
4102 at all times, but for logging purposes it makes the most sense to copy this
4103 information after the rsocket is closed, at which time the final statistics
4104 will continue to be available until the rsocket's destructor takes over.
4105
4106 @par Formatting
4107 The format string may contain any characters, with only instances of the
4108 following case-sensitive command sequences to be interpolated accordingly
4109 (invalid commands will be ignored and will remain in place, unmodified):
4110
4111 <table cellpadding=8 cellspacing=0 border=1>
4112 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
4113 <tr><td>$$</td><td>One dollar sign ("$")</td><td>@e N/A</td></tr>
4114 <tr><td>$aR</td><td>Total (all) bytes received</td><td>bytes_rx @c + crypt_rx</td></tr>
4115 <tr><td>$aT</td><td>Total (all) bytes transmitted</td><td>bytes_tx @c + crypt_tx</td></tr>
4116 <tr><td>$bR</td><td>Unencrypted bytes received</td><td>bytes_rx</td></tr>
4117 <tr><td>$bT</td><td>Unencrypted bytes transmitted</td><td>bytes_tx</td></tr>
4118 <tr><td>$cR</td><td>Encrypted bytes recevied</td><td>crypt_rx</td></tr>
4119 <tr><td>$cT</td><td>Encrypted bytes transmitted</td><td>crypt_tx</td></tr>
4120 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
4121 </table>
4122
4123 Why do we use dollar signs instead of percent symbols, like other formatting
4124 functions like printf() does? So that the format string won't be in conflict
4125 with any percent-prefixed commands in printf() and similar funcions. This
4126 means that a printf() format string can be put through a first pass here to
4127 get the needed statistics interpolated into the needed positions.
4128
4129 @par Threads
4130 This method is threadsafe.
4131 @returns An interpolated format string as an std::string object.
4132 @see net_io_final
4133 @see net_io_update
4134 @see printf
4135 @qualifier TLS
4136 *///=========================================================================
4137 std::string net_io(
4138 /// Format string
4139 const char* format,
4140 /// Length of format string (in bytes), or 0 to auto-detect length if format string is an ASCIIZ string
4141 size_t len = 0,
4142 /// Pointer to an @ref rsocket_io structure to read the statistics from (nullptr = use internal copy of current statistics)
4143 // TODO: Convert this to a static method and create a new method without the "len" parameter that just calls the static method with the pointer to the internal "addr" buffer (this will make it easier to call this method in a static fashion later without incurring any of the overhead of instantiation)
4144 rsocket_io* addr = nullptr) noexcept {
4145
4146 // --------------------------------------------------------------------------
4147 // Measure size of format string if an ASCIIZ string was indicated.
4148 // --------------------------------------------------------------------------
4149 if (len == 0) len = std::strlen(format);
4150
4151 // --------------------------------------------------------------------------
4152 // Internal variables.
4153 // --------------------------------------------------------------------------
4154 rsocket_io* data = addr == nullptr ? net_io().get() : addr; // Copy current statistics so that
4155 std::string stats; // Formatted result
4156 ulong val; // Value (to be inserted)
4157 char c; // Current character
4158 std::string cmd; // Accumulated command (may be subsituted)
4159 bool flag_commas = false; // Thousands separators flag
4160 bool flag_right = false; // Flush-right flag
4161
4162 // --------------------------------------------------------------------------
4163 // Process format string and build resulting formatted stats string.
4164 //
4165 // This is designed to be fast, and I'm taking huge shortcuts here since the
4166 // commands are all the same size (except for the first one, which is only
4167 // two dollar sign characters). Processing in this loop should be quite fast
4168 // compared to using library functions to search for dollar sign characters
4169 // since data needs to be copied anyway -- why run a loop over the text twice
4170 // when can get away with doing it faster by doing it only once? This is an
4171 // optimized approach, although it probably could be even faster by not using
4172 // std::string for temporary command storage (there are other ways to do this
4173 // but I think this should suffice for now since it isn't expected to be used
4174 // very often).
4175 // --------------------------------------------------------------------------
4176 for (int i = 0; i < len; i++) {
4177
4178 // --------------------------------------------------------------------------
4179 // First character (potentially).
4180 // --------------------------------------------------------------------------
4181 if ((c = format[i]) != '$') { // Not a dollar sign -- save it and move on
4182 stats.push_back(c);
4183 continue;
4184 } // -x- if !$ -x-
4185 cmd = c; // Start building up the command string
4186
4187 // --------------------------------------------------------------------------
4188 // Second character: Part 1 of 2
4189 //
4190 // TODO: Add support for "," commas, and "r" right-alignment.
4191 // --------------------------------------------------------------------------
4192 if (++i == len) continue; // End of format string, so we're done
4193 cmd.push_back(c = format[i]);
4194 if (c == '$') { // Consume the previous dollar sign so that $$ turns into $
4195 stats.push_back(c); // Add $ to stats string (we're only keeping one dollar sign)
4196 continue;
4197 } // -x- if $ -x-
4198 flag_commas = flag_right = false; // Reset these flags for a clean start
4199
4200 // --------------------------------------------------------------------------
4201 // Flag checks:
4202 // , = Include thousands separators (commas)
4203 // r = Flush right (leading spaces will be added)
4204 // --------------------------------------------------------------------------
4205 net_io_flags_loop:
4206 if (c == ',') {
4207 if (++i == len) continue; // End of format string, so we're done
4208 cmd.push_back(c = format[i]);
4209 flag_commas = true;
4210 goto net_io_flags_loop;
4211 } else if (c == 'r') {
4212 if (++i == len) continue; // End of format string, so we're done
4213 cmd.push_back(c = format[i]);
4214 flag_right = true;
4215 goto net_io_flags_loop;
4216 } // -x- if [,r] -x-
4217 // TODO: Do more
4218
4219 // --------------------------------------------------------------------------
4220 // Second character: Part 1 of 2
4221 // --------------------------------------------------------------------------
4222 if (c < 'a' || c > 'c') { // Not a, b, or c, so treat it as a regular character
4223 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4224 continue;
4225 } // -x- if !~[abc] -x-
4226
4227 // --------------------------------------------------------------------------
4228 // Third character.
4229 // --------------------------------------------------------------------------
4230 if (++i == len) continue; // End of format string, so we're done
4231 cmd.push_back(c = format[i]);
4232 if (c != 'R' && c != 'T') { // Not R or T, so treat it as a regular character
4233 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4234 continue;
4235 } // -x- if !~[RT] -x-
4236
4237 // --------------------------------------------------------------------------
4238 // Command processing. If the command is valid, then it will be interpolated
4239 // with its expected result by replacing it with the resulting value.
4240 // --------------------------------------------------------------------------
4241 //std::cout << "[" << cmd << "]"; // Debug
4242 if (cmd.ends_with("aR")) val = data->bytes_rx + data->crypt_rx;
4243 else if (cmd.ends_with("aT")) val = data->bytes_tx + data->crypt_tx;
4244 else if (cmd.ends_with("bR")) val = data->bytes_rx ;
4245 else if (cmd.ends_with("bT")) val = data->bytes_tx ;
4246 else if (cmd.ends_with("cR")) val = data->crypt_rx;
4247 else if (cmd.ends_with("cT")) val = data->crypt_tx;
4248 else { stats.append(cmd); continue; } // This is wrong, so ignore it
4249
4250 // --------------------------------------------------------------------------
4251 // Re-use cmd to generate formatted value.
4252 // --------------------------------------------------------------------------
4253 cmd.clear();
4254 cmd.resize(21); // Maximum length without commas (plus character zero): 18446744073709551615
4255 cmd.resize(sprintf(cmd.data(), flag_right ? "%20lu" : "%lu", val)); // The ::sprintf function can't use cmd.c_str() here
4256 if (flag_commas) cmd = randolf::rtools::insert_commas(cmd.c_str()); // Insert commas (this makes the string longer)
4257
4258 // --------------------------------------------------------------------------
4259 // Convert to std::string and add to the stats string.
4260 // --------------------------------------------------------------------------
4261 stats.append(cmd);
4262
4263 }; // -x- for i -x-
4264
4265 return stats;
4266 }; // -x- std::string net_io -x-
4267
4268 /*======================================================================*//**
4269 @brief
4270 Where the destructor should save final I/O statistics before this rsocket's
4271 resources are completely freed/deallocated.
4272 @attention
4273 Developers should take care to check that the @ref rsocket_io::is_final flag
4274 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
4275 since there's no guarantee that the destructor will necessarily be executed
4276 in a timely manner (this flag is set last, after all other statistics are
4277 copied into the structure while in a mutex-locked state).
4278 @returns The same rsocket object so as to facilitate stacking
4279 @see ~rsocket
4280 @see net_io
4281 @see net_io_update
4282 @see printf
4283 @qualifier TLS
4284 *///=========================================================================
4285 rsocket* net_io_final(
4286 /// Pointer to @ref rsocket_io structure (nullptr = disabled)
4287 rsocket_io* addr) noexcept {
4288 __io_final_addr = addr;
4289 return this;
4290 }; // -x- rsocket* net_io_final -x-
4291
4292 /*======================================================================*//**
4293 @brief
4294 Where the destructor should save current I/O statistics.
4295 @attention
4296 Statistics are copied into the structure while in a mutex-locked state, but
4297 the copy itself is not an overall atomic snapshot of the I/O statistics even
4298 though each statistic is copied atomically. This means that the actual
4299 statistics could be slightly different if updates occur independently (e.g.,
4300 due to activities in a different thread), but this likely won't matter since
4301 the anticipated use for these statistics is to display or otherwise present
4302 general statistical information to a user at regular intervals.
4303 @returns The same rsocket object so as to facilitate stacking
4304 @see ~rsocket
4305 @see net_io
4306 @see net_io_final
4307 @see printf
4308 @qualifier TLS
4309 *///=========================================================================
4310 rsocket* net_io_update(
4311 /// Pointer to @ref rsocket_io structure (nullptr = do nothing)
4312 rsocket_io* addr) noexcept {
4313
4314 // --------------------------------------------------------------------------
4315 // Copy statistics to final location. (We do this last in case there's an
4316 // issue with locking; there shouldn't be, but if there is then at least we
4317 // already we're not inadvertently hanging on to resources at this point that
4318 // need to be freed/closed anyway.)
4319 // --------------------------------------------------------------------------
4320 if (addr != nullptr) {
4321 addr->lock();
4322 addr->bytes_rx = __bytes_rx;
4323 addr->bytes_tx = __bytes_tx;
4324 addr->crypt_rx = __crypt_rx;
4325 addr->crypt_tx = __crypt_tx;
4326 addr->is_final = false;
4327 addr->unlock();
4328 } // -x- if addr -x-
4329
4330 return this;
4331 }; // -x- rsocket* net_io_update -x-
4332
4333 /*======================================================================*//**
4334 @brief
4335 Poll the underlying socket using the poll() method for data that's ready for
4336 receiving (default), etc.
4337
4338 @note
4339 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4340 select(), and accept()/accept4() when using multiple sockets.
4341
4342 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4343 part of the user address space
4344 @throws randolf::rex::xEINTR Interrupted by a signal
4345 @throws randolf::rex::xENOMEM Insufficient memory
4346 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4347 doesn't refer to a socket
4348 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4349 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4350 is a highly improbable chance that a timeout could still occur if the
4351 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4352 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4353 there's no new data
4354
4355 @returns The resulting (short)pollfd.revents bitfield
4356 @returns 0 if @c timeout_behaviour is set to TIMEOUT_ZERO
4357 @qualifier POSIX
4358 @qualifier TLS
4359 *///=========================================================================
4360 short poll(
4361 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4362 const short events = POLLIN,
4363 /// Number of milliseconds to wait
4364 const int timeout = 0,
4365 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4366 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4367 if (__debug) debug("poll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4368 + ", pollfd.events=" + std::to_string(events)
4369 + ", timeout=" + std::to_string(timeout)
4370 + ");");
4371 struct pollfd fds{__socket_fd, events, 0};
4372 if (__rc_check(::poll(&fds, 1, timeout)) == 0) {
4373 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4374 else return 0;
4375 } // -x- if 0 -x-
4376 return fds.revents;
4377 }; // -x- short poll -x-
4378
4379 /*======================================================================*//**
4380 @brief
4381 Get port number associated with underlying socket descriptor/handle.
4382
4383 Returns 0 if:
4384 - an emphemeral port number assignment (dynamic) is intended
4385 - the underlying socket details are not defined (e.g., in the case of an
4386 empty rsocket instantiation)
4387 - port numbers are not supported/utilized by the current family (e.g., not
4388 AF_INET {IPv4} or AF_INET6 {IPv6})
4389
4390 The port number can be set in most constructors, or via one of the socket()
4391 or bind() methods.
4392 @returns Port number (typically TCP and UDP, although some other families
4393 such as SCTP and DCCP also utilize port numbers)
4394 @see socket_family()
4395 @see socket_protocol()
4396 @see socket_type()
4397 @qualifier TLS
4398 *///=========================================================================
4399 const uint16_t port() noexcept {
4400 switch (__socket_addr.ss_family) {
4401 case AF_INET: // IPv4
4402 return ntohs(((sockaddr_in*)&__socket_addr)->sin_port);
4403 case AF_INET6: // IPv6
4404 return ntohs(((sockaddr_in6*)&__socket_addr)->sin6_port);
4405 default: // Everything else
4406 return 0;
4407 } // -x- switch __socket_addr.ss_family -x-
4408 }; // -x- uint16_t port -x-
4409
4410 /*======================================================================*//**
4411 @brief
4412 Poll the underlying socket using the ppoll() method for data that's ready for
4413 receiving (default), etc.
4414
4415 @note
4416 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4417 select(), and accept()/accept4() when using multiple sockets.
4418
4419 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4420 part of the user address space
4421 @throws randolf::rex::xEINTR Interrupted by a signal
4422 @throws randolf::rex::xENOMEM Insufficient memory
4423 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4424 doesn't refer to a socket
4425 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4426 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4427 is a highly improbable chance that a timeout could still occur if the
4428 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4429 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4430 there's no new data
4431
4432 @returns The resulting (short)pollfd.revents bitfield
4433 @qualifier POSIX
4434 @qualifier TLS
4435 *///=========================================================================
4436 short ppoll(
4437 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4438 const short events = POLLIN,
4439 /// Timeout
4440 const struct timespec* tmo_p = nullptr,
4441 /// Signal mask
4442 const sigset_t* sigmask = nullptr,
4443 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4444 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4445 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4446 + ", pollfd.events=" + std::to_string(events)
4447 + ", timespec{tv_sec=" + std::to_string(tmo_p->tv_sec)
4448 + ", tv_nsec=" + std::to_string(tmo_p->tv_nsec) + "}"
4449 + ", sigset_t"
4450 + ");");
4451 struct pollfd fds{__socket_fd, events, 0};
4452 if (__rc_check(::ppoll(&fds, 1, tmo_p, sigmask)) == 0) {
4453 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4454 else return 0;
4455 } // -x- if 0 -x-
4456 return fds.revents;
4457 }; // -x- short ppoll -x-
4458
4459 /*======================================================================*//**
4460 @brief
4461 Poll the underlying socket using the ppoll() method for data that's ready for
4462 receiving (default), etc.
4463
4464 @note
4465 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4466 select(), and accept()/accept4() when using multiple sockets.
4467
4468 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4469 part of the user address space
4470 @throws randolf::rex::xEINTR Interrupted by a signal
4471 @throws randolf::rex::xENOMEM Insufficient memory
4472 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4473 doesn't refer to a socket
4474 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4475 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4476 is a highly improbable chance that a timeout could still occur if the
4477 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4478 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4479 there's no new data
4480
4481 @returns The resulting (short)pollfd.revents bitfield
4482 @qualifier TLS
4483 *///=========================================================================
4484 short ppoll(
4485 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4486 const short events = POLLIN,
4487 /// Timeout in seconds
4488 const int tv_sec = 0,
4489 /// Timeout in nanoseconds
4490 const long tv_nsec = 0,
4491 /// Signal mask
4492 const sigset_t* sigmask = nullptr,
4493 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4494 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4495 struct timespec tmo_p{tv_sec, tv_nsec};
4496 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4497 + ", pollfd.events=" + std::to_string(events)
4498 + ", timespec{tv_sec=" + std::to_string(tmo_p.tv_sec)
4499 + ", tv_nsec=" + std::to_string(tmo_p.tv_nsec) + "}"
4500 + ", sigset_t"
4501 + ");");
4502 struct pollfd fds{__socket_fd, events, 0};
4503 if (__rc_check(::ppoll(&fds, 1, &tmo_p, sigmask)) == 0) {
4504 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4505 else return 0;
4506 } // -x- if 0 -x-
4507 return fds.revents;
4508 }; // -x- short ppoll -x-
4509
4510 /*======================================================================*//**
4511 @brief
4512 Send a formatted string to the @ref rsocket endpoint.
4513
4514 The @c format is described in the documentation for the POSIX or Standard C
4515 Library @c printf() function.
4516 @throws randolf::rex::xEBADF The underlying socket is not open
4517 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
4518 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
4519 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
4520 @throws randolf::rex::xENOMEM Insufficient memory
4521 @returns The same rsocket object so as to facilitate stacking
4522 @see eol_fix_printf
4523 @see is_eol_fix_printf
4524 @see net_io
4525 @see printfline
4526 @see vprintf
4527 @see vprintfline
4528 @qualifier POSIX
4529 @qualifier TLS
4530 *///=========================================================================
4531 rsocket* printf(
4532 /// Format string to use
4533 const char* format,
4534 /// Variadic arguments
4535 ...) {
4536 char* buf;
4537 ::va_list args;
4538 ::va_start(args, format);
4539 int rc = ::vasprintf(&buf, format, args);
4540 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
4541 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
4542 if (__eol_fix_printf && !__eol.empty()) {
4543 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
4544 ::free(buf);
4545 __send(str.c_str(), str.length());
4546 } else {
4547 try {
4548 __send(buf, rc);
4549 } catch (std::exception& e) { // Free buf then re-throw the exception
4550 ::free(buf); // Prevent memory leak when an exception is thrown
4551 throw;
4552 }
4553 } // -x- if __eol_fix_printf -x-
4554 return this;
4555 }; // -x- rsocket* printf -x-
4556
4557 /*======================================================================*//**
4558 @brief
4559 Send a formatted string to the @ref rsocket endpoint, and append an EoL
4560 sequence.
4561
4562 The @c format is described in the documentation for the POSIX or Standard C
4563 Library @c printf() function.
4564 @throws randolf::rex::xEBADF The underlying socket is not open
4565 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
4566 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
4567 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
4568 @throws randolf::rex::xENOMEM Insufficient memory
4569 @returns The same rsocket object so as to facilitate stacking
4570 @see eol
4571 @see eol_fix_printf
4572 @see is_eol_fix_printf
4573 @see net_io
4574 @see printf
4575 @see sendline
4576 @see vprintf
4577 @see vprintfline
4578 @qualifier TLS
4579 *///=========================================================================
4580 rsocket* printfline(
4581 /// Format string to use
4582 const char* format,
4583 /// Variadic arguments
4584 ...) {
4585 char* buf;
4586 ::va_list args;
4587 ::va_start(args, format);
4588 int rc = ::vasprintf(&buf, format, args);
4589 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
4590 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
4591 if (__eol_fix_printf && !__eol.empty()) {
4592 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
4593 .append(__eol);
4594 ::free(buf);
4595 __send(str.c_str(), str.length());
4596 } else {
4597 try {
4598 __sendline(buf, rc);
4599 } catch (std::exception& e) { // Free buf then re-throw the exception
4600 ::free(buf); // Prevent memory leak when an exception is thrown
4601 throw;
4602 }
4603 } // -x- if __eol_fix_printf -x-
4604 return this;
4605 }; // -x- rsocket* printfline -x-
4606
4607 /*======================================================================*//**
4608 @brief
4609 Receive data from the endpoint into a @c std::vector<char> that is allocated
4610 on-the-fly.
4611
4612 If nbytes is 0, then the internal @ref buffer_size() will be used as the
4613 default, which can also be changed from its compiled-in default of 1024 by
4614 using one of the buffer_size() methods.
4615
4616 @par Notes
4617 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4618 reception, but it's important to note that it does implement temporary
4619 blocking while waiting for data.
4620
4621 @throws randolf::rex::xEBADF The underlying socket is not open
4622 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4623 connections
4624 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4625 part of the user address space
4626 @throws randolf::rex::xEINTR Interrupted by a signal
4627 @throws randolf::rex::xEINVAL Invalid argument passed
4628 @throws randolf::rex::xENOMEM Insufficient memory
4629 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4630 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4631 doesn't refer to a socket
4632 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4633 there's no new data
4634
4635 @returns appropriately-sized vector of characters
4636 @see recv(std::vector<char>, const int)
4637 @see recvz(const size_t, const int)
4638 @qualifier TLS
4639 *///=========================================================================
4640 std::vector<char> recv(
4641 /// Maximum number of bytes to receive
4642 const size_t nbytes = 0,
4643 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4644 const int flags = 0) {
4645 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
4646 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4647 + ", <buf>"
4648 + ", " + std::to_string(buf_size)
4649 + ", " + std::to_string(flags)
4650 + ");");
4651 std::vector<char> buf(buf_size);
4652 buf.resize(__recv(buf.data(), buf.size(), flags));
4653 return buf;
4654 }; // -x- std::vector<char> recv -x-
4655
4656 /*======================================================================*//**
4657 @brief
4658 Receive data from the endpoint into the @c std::vector object supplied in the
4659 @c buf parameter.
4660
4661 The maximum number of bytes that can be received won't exceed the number of
4662 bytes that the supplied @c std::vector<char> was initialized or resized to.
4663
4664 @pre
4665 For @c std::vector it's important that the @c resize() method is used to
4666 pre-allocate the underlying char[] array, instead of the unfortunately-named
4667 @c reserve() method that doesn't pre-allocate, to avoid causing segmentation
4668 faults or other undefined behaviours.
4669
4670 @par Notes
4671 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4672 reception, but it's important to note that it does implement temporary
4673 blocking while waiting for data.
4674
4675 @throws randolf::rex::xEBADF The underlying socket is not open
4676 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4677 connections
4678 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4679 part of the user address space
4680 @throws randolf::rex::xEINTR Interrupted by a signal
4681 @throws randolf::rex::xEINVAL Invalid argument passed
4682 @throws randolf::rex::xENOMEM Insufficient memory
4683 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4684 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4685 doesn't refer to a socket
4686 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4687 there's no new data
4688
4689 @returns The same array that was specified in the @c buf parameter
4690 @see recv(const size_t, const int)
4691 @see recvz(const size_t, const int)
4692 @qualifier POSIX
4693 @qualifier TLS
4694 *///=========================================================================
4695 std::vector<char> recv(
4696 /// Target std::vector<char> to receive data into
4697 std::vector<char> buf,
4698 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4699 const int flags = 0) {
4700 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4701 + ", <buf>"
4702 + ", " + std::to_string(buf.size())
4703 + ", " + std::to_string(flags)
4704 + ");");
4705 buf.resize(__recv(buf.data(), buf.size(), flags));
4706 return buf;
4707 }; // -x- std::vector<char> recv -x-
4708
4709 /*======================================================================*//**
4710 @brief
4711 Receive data from the endpoint into a @c std::string object that is allocated
4712 on-the-fly.
4713
4714 If nbytes is 0, then the internal @ref buffer_size() will be used as the
4715 default, which can also be changed from its compiled-in default of 1024 by
4716 using one of the buffer_size() methods.
4717
4718 @par Notes
4719 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4720 reception, but it's important to note that it does implement temporary
4721 blocking while waiting for data.
4722
4723 @throws randolf::rex::xEBADF The underlying socket is not open
4724 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4725 connections
4726 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4727 part of the user address space
4728 @throws randolf::rex::xEINTR Interrupted by a signal
4729 @throws randolf::rex::xEINVAL Invalid argument passed
4730 @throws randolf::rex::xENOMEM Insufficient memory
4731 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4732 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4733 doesn't refer to a socket
4734 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4735 there's no new data
4736
4737 @returns appropriately-sized vector of characters
4738 @see recv
4739 @see recv(std::vector<char>, const int)
4740 @see recvz(const size_t, const int)
4741 @qualifier TLS
4742 *///=========================================================================
4743 std::string recv_as_string(
4744 /// Maximum number of bytes to receive
4745 const size_t nbytes = 0,
4746 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4747 const int flags = 0) {
4748 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
4749 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4750 + ", <buf>"
4751 + ", " + std::to_string(buf_size)
4752 + ", " + std::to_string(flags)
4753 + ");");
4754 std::string buf;
4755 buf.resize(buf_size); // Pre-fill anticipated string size
4756 buf.resize(__recv(buf.data(), buf.size(), flags)); // Shorten string
4757 return buf;
4758 }; // -x- std::string recv -x-
4759
4760 /*======================================================================*//**
4761 @brief
4762 Receive an ASCIIZ string from the endpoint, including the NULL terminator.
4763
4764 @throws randolf::rex::xEBADF The underlying socket is not open
4765 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4766 connections
4767 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4768 part of the user address space
4769 @throws randolf::rex::xEINTR Interrupted by a signal
4770 @throws randolf::rex::xEINVAL Invalid argument passed
4771 @throws randolf::rex::xENOMEM Insufficient memory
4772 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4773 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4774 doesn't refer to a socket
4775 @throws randolf::rex::xERANGE if no NULL terminator is detected (this may
4776 occur before the underlying ASCIIZ string char* array is allocated)
4777 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4778 there's no new data
4779
4780 @returns Pointer to ASCIIZ string (will need to be deleted by caller)
4781 @see send_asciiz(const char*, const int)
4782 @qualifier TLS
4783 *///=========================================================================
4784 char* recv_asciiz(
4785 /// Maximum number of bytes to receive
4786 const size_t nbytes = 0,
4787 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4788 const int flags = 0) {
4789 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
4790 if (__debug) debug("recv_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4791 + ", " + std::to_string(buf_size)
4792 + ", " + std::to_string(flags)
4793 + ");");
4794
4795 // --------------------------------------------------------------------------
4796 // Calculate size of buffer (includes NULL terminator since we'll also be
4797 // receiving this from the endpoint).
4798 // --------------------------------------------------------------------------
4799 char buf[buf_size];
4800
4801 // --------------------------------------------------------------------------
4802 // Reduce buffer size to what is actually read (remember: we don't actually
4803 // know where the EoL sequence is yet, or if there even is one).
4804 // --------------------------------------------------------------------------
4805 int max = __recv(&buf, buf_size, MSG_PEEK | flags); // Look-ahead at socket stream (buffer was inflated to include EoL sequence)
4806 int len = -1;
4807 for (int i = 0; i < max; i++) {
4808 if (buf[i] == 0) {
4809 len = i;
4810 break;
4811 } // -x- if v[i] -x-
4812 } // -x- for i -x-
4813 if (len < 0) throw randolf::rex::xERANGE("ERANGE: NULL terminator not found");
4814
4815 // --------------------------------------------------------------------------
4816 // I'd love to use std::shared_ptr<char*> for this next part, but it leads
4817 // to intermittent segmentation faults and/or "malloc(): corrupted top size
4818 // occurs" errors outside of this method. So, my conclusion is that char*
4819 // (and char[], as I've tried with this too) are not properly supported by:
4820 // std::shared_ptr<char*> v = std::make_shared<char*>(new char[len + 1]);
4821 //
4822 // Using std::string() is really not what we want because there is confusion
4823 // concerning the NULL terminator -- with char* strings, it's crystal clear
4824 // that there must be a NULL terminator (since a length isn't tracked). Now,
4825 // if you want an std::string, just initialize as follows:
4826 //
4827 // char* temp_string = r.recv_asciiz();
4828 // std::string mystr = std::string(temp_string);
4829 // delete temp_string;
4830 // --------------------------------------------------------------------------
4831 char* v = new char[len + 1];
4832 int rc = __recv(v, len + 1, flags); // Consume with NULL terminator from socket stream
4833// TODO: Free "v" in a catch-and-rethrow block (check other calls to __recv and __send too)
4834 return v;
4835
4836 }; // -x- char* recv_asciiz -x-
4837
4838 /*======================================================================*//**
4839 @brief
4840 Receive one byte (unsigned 8-bit byte) of data from the endpoint.
4841 @par Notes
4842 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4843 reception, but it's important to note that it does implement temporary
4844 blocking while waiting for data.
4845
4846 @throws randolf::rex::xEBADF The underlying socket is not open
4847 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4848 connections
4849 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4850 part of the user address space
4851 @throws randolf::rex::xEINTR Interrupted by a signal
4852 @throws randolf::rex::xEINVAL Invalid argument passed
4853 @throws randolf::rex::xENOMEM Insufficient memory
4854 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4855 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4856 doesn't refer to a socket
4857 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4858 there's no new data
4859
4860 @returns one unsigned character
4861 @see recv(std::vector<char>, const int)
4862 @see recvz(const size_t, const int)
4863 @see recv_char
4864 @see send_byte
4865 @see send_char
4866 @qualifier TLS
4867 *///=========================================================================
4868 u_char recv_byte(
4869 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4870 const int flags = 0) {
4871 char buf(0);
4872 if (__debug) debug("recv_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4873 + ", <buf>"
4874 + ", " + std::to_string(sizeof(buf))
4875 + ", " + std::to_string(flags)
4876 + ");");
4877 int rc = __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4878 return buf;
4879 }; // -x- byte recv_byte -x-
4880
4881 /*======================================================================*//**
4882 @brief
4883 Receive one character (signed 8-bit byte) of data from the endpoint.
4884 @par Notes
4885 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4886 reception, but it's important to note that it does implement temporary
4887 blocking while waiting for data.
4888
4889 @throws randolf::rex::xEBADF The underlying socket is not open
4890 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4891 connections
4892 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4893 part of the user address space
4894 @throws randolf::rex::xEINTR Interrupted by a signal
4895 @throws randolf::rex::xEINVAL Invalid argument passed
4896 @throws randolf::rex::xENOMEM Insufficient memory
4897 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4898 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4899 doesn't refer to a socket
4900 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4901 there's no new data
4902
4903 @returns one signed character
4904 @see recv(std::vector<char>, const int)
4905 @see recvz(const size_t, const int)
4906 @see recv_byte
4907 @see send_byte
4908 @see send_char
4909 @qualifier TLS
4910 *///=========================================================================
4911 char recv_char(
4912 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4913 const int flags = 0) {
4914 char buf(0);
4915 if (__debug) debug("recv_char(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4916 + ", <buf>"
4917 + ", " + std::to_string(sizeof(buf))
4918 + ", " + std::to_string(flags)
4919 + ");");
4920 int rc = __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4921 // How to detect disconnected stream when telnet user presses CTRL-C?
4922 //std::cout << "rc=" << std::to_string(rc) << std::endl; // Debug (to be removed)
4923// TODO: Investigate checking platform's char size and moving signing bit accordingly
4924 return buf;
4925 }; // -x- char recv_char -x-
4926
4927 /*======================================================================*//**
4928 @brief
4929 Receive a line of data from the endpoint, into an @ref randolf::rline object
4930 with the EoL character(s) isolated. While this is meant for ASCII and UTF-8
4931 text, it will also work with binary data that doesn't include EoL character
4932 sequences as non-line-ending data (when receiving binary data,
4933 the @ref recv() and @ref recvz() methods tend to be better-suited).
4934
4935 This is essentially a wrapper around what recvline() does, but returns both
4936 the line of text and the EoL sequence together in an @ref randolf::rline
4937 object.
4938 @note
4939 For additional details on the other parameters, please see the @ref recvline
4940 method's documentation for the remaining details.
4941 @warning
4942 If you're using a customzied EoL sequence, then it's important to note that
4943 it probably won't be recognized by the @ref randolf::rline class, but do also
4944 check that documentation to confirm this.
4945
4946 @throws randolf::rex::xEAGAIN if timeout occurs before EoL
4947 @throws randolf::rex::xEBADF The underlying socket is not open
4948 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4949 connections
4950 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4951 part of the user address space
4952 @throws randolf::rex::xEINTR Interrupted by a signal
4953 @throws randolf::rex::xEINVAL Invalid argument passed
4954 @throws randolf::rex::xENOMEM Insufficient memory
4955 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4956 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4957 doesn't refer to a socket
4958 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
4959 no EoL character sequence is detected after recvline's buffer became
4960 full (whatever data is waiting may still be received using any of the
4961 recv_ methods {with or without the @c MSG_PEEK flag set}, including
4962 @c recvline() with a larger buffer {see the @c nbytes parameter})
4963 @throws randolf::rex::xERANGE if the timeout parameter is below 0
4964 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4965 there's no new data
4966
4967 @returns One line of text as a randolf::rline object.
4968 @see discard
4969 @see eol()
4970 @see eol_consumed_seq()
4971 @see randolf::rline
4972 @see recvline
4973 @see sendline(const std::string, const int)
4974 @see timeout
4975 @see timeout_recvline
4976 @see timeout_recvline(long)
4977 @qualifier TLS
4978 *///=========================================================================
4979 randolf::rline recv_rline(
4980 /// Maximum number of bytes to receive (including EoL character sequence)
4981 const size_t nbytes = 0,
4982 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4983 const int flags = 0,
4984 /// Line timeout (in seconds)@n
4985 /// 0 = no timeout (default), unless it was configured by way of the
4986 /// @ref timeout_recvline(long) method
4987 long timeout = 0,
4988 /// Automatically consume bytes read during a failed read (this flag is
4989 /// ignored when the @c MSG_PEEK flag is set) just before throwing the
4990 /// randolf::rex::xEOVERFLOW exception
4991 bool discard_on_overflow = true) {
4992 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
4993 // + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF; TODO: Add an option for this
4994 // Internally, the length of the @ref eol() sequence is added to the buffer size
4995 // so that the maximum line length without EoL characters matches the maximum
4996 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
4997 if (__debug) debug("recv_rline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4998 + ", " + std::to_string(buf_size)
4999 + ", " + std::to_string(flags)
5000 + ");");
5001 std::string line = __recvline(buf_size, flags, timeout, discard_on_overflow);
5002 return randolf::rline(line.append(eol_consumed_seq()));
5003 }; // -x- randolf::rline recv_rline -x-
5004
5005 /*======================================================================*//**
5006 @brief
5007 Receive a data structure from the endpoint.
5008 @post
5009 MSB/LSB considerations are important for any integers within your structure,
5010 so be sure to sanitize them with htons(), ntohs(), and related methods prior
5011 to sending, and then after receiving. This way, your data will be protected
5012 against corruption resulting from byte order differences when communicating
5013 between hardware architectures that differ in MSB and LSB byte ordering.
5014 @par Notes
5015 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5016 reception, but it's important to note that it does implement temporary
5017 blocking while waiting for data.
5018
5019 @throws randolf::rex::xEBADF The underlying socket is not open
5020 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5021 connections
5022 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5023 part of the user address space
5024 @throws randolf::rex::xEINTR Interrupted by a signal
5025 @throws randolf::rex::xEINVAL Invalid argument passed
5026 @throws randolf::rex::xENOMEM Insufficient memory
5027 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5028 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5029 doesn't refer to a socket
5030 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5031 there's no new data
5032
5033 @returns Data structure
5034 @see send_struct
5035 @qualifier TLS
5036 *///=========================================================================
5037 template <typename T> T recv_struct(
5038 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5039 const int flags = 0) {
5040 T buf{0};
5041 if (__debug) debug("recv_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5042 + ", <buf>"
5043 + ", " + std::to_string(sizeof(buf))
5044 + ", " + std::to_string(flags)
5045 + ");");
5046 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5047 return buf;
5048 }; // -x- T recv_struct -x-
5049
5050 /*======================================================================*//**
5051 @brief
5052 Receive one 16-bit unsigned integer of data in LSB (little endian) order from
5053 the endpoint.
5054 @par Notes
5055 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5056 reception, which implements temporary blocking while waiting for data.
5057
5058 @throws randolf::rex::xEBADF The underlying socket is not open
5059 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5060 connections
5061 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5062 part of the user address space
5063 @throws randolf::rex::xEINTR Interrupted by a signal
5064 @throws randolf::rex::xEINVAL Invalid argument passed
5065 @throws randolf::rex::xENOMEM Insufficient memory
5066 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5067 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5068 doesn't refer to a socket
5069 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5070 there's no new data
5071
5072 @returns uint16_t (converted to local endianness)
5073 @qualifier TLS
5074 *///=========================================================================
5075 uint16_t recv_uint16_lsb(
5076 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5077 const int flags = 0) {
5078 uint16_t buf(0);
5079 if (__debug) debug("recv_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5080 + ", <buf>"
5081 + ", " + std::to_string(sizeof(buf))
5082 + ", " + std::to_string(flags)
5083 + ");");
5084 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5085 return !__endian_is_msb ? buf : ntohs(buf);
5086 }; // -x- uint16_t recv_uint16_lsb -x-
5087
5088 /*======================================================================*//**
5089 @brief
5090 Receive one 16-bit unsigned integer of data in MSB (big endian) order from
5091 the endpoint.
5092 @par Notes
5093 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5094 reception, which implements temporary blocking while waiting for data.
5095
5096 @throws randolf::rex::xEBADF The underlying socket is not open
5097 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5098 connections
5099 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5100 part of the user address space
5101 @throws randolf::rex::xEINTR Interrupted by a signal
5102 @throws randolf::rex::xEINVAL Invalid argument passed
5103 @throws randolf::rex::xENOMEM Insufficient memory
5104 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5105 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5106 doesn't refer to a socket
5107 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5108 there's no new data
5109
5110 @returns uint16_t (converted to local endianness)
5111 @qualifier TLS
5112 *///=========================================================================
5113 uint16_t recv_uint16_msb(
5114 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5115 const int flags = 0) {
5116 uint16_t buf(0);
5117 if (__debug) debug("recv_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5118 + ", <buf>"
5119 + ", " + std::to_string(sizeof(buf))
5120 + ", " + std::to_string(flags)
5121 + ");");
5122 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5123 return __endian_is_msb ? buf : htons(buf);
5124 }; // -x- uint16_t recv_uint16_msb -x-
5125
5126 /*======================================================================*//**
5127 @brief
5128 Receive one 32-bit unsigned integer of data in LSB (little endian) order from
5129 the endpoint.
5130 @par Notes
5131 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5132 reception, which implements temporary blocking while waiting for data.
5133
5134 @throws randolf::rex::xEBADF The underlying socket is not open
5135 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5136 connections
5137 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5138 part of the user address space
5139 @throws randolf::rex::xEINTR Interrupted by a signal
5140 @throws randolf::rex::xEINVAL Invalid argument passed
5141 @throws randolf::rex::xENOMEM Insufficient memory
5142 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5143 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5144 doesn't refer to a socket
5145 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5146 there's no new data
5147
5148 @returns uint32_t (converted to local endianness)
5149 @qualifier TLS
5150 *///=========================================================================
5151 uint32_t recv_uint32_lsb(
5152 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5153 const int flags = 0) {
5154 uint32_t buf(0);
5155 if (__debug) debug("recv_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5156 + ", <buf>"
5157 + ", " + std::to_string(sizeof(buf))
5158 + ", " + std::to_string(flags)
5159 + ");");
5160 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5161 return !__endian_is_msb ? buf : htonl(buf);
5162 }; // -x- uint32_t recv_uint32_lsb -x-
5163
5164 /*======================================================================*//**
5165 @brief
5166 Receive one 32-bit unsigned integer of data in MSB (big endian) order from
5167 the endpoint.
5168 @par Notes
5169 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5170 reception, which implements temporary blocking while waiting for data.
5171
5172 @throws randolf::rex::xEBADF The underlying socket is not open
5173 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5174 connections
5175 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5176 part of the user address space
5177 @throws randolf::rex::xEINTR Interrupted by a signal
5178 @throws randolf::rex::xEINVAL Invalid argument passed
5179 @throws randolf::rex::xENOMEM Insufficient memory
5180 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5181 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5182 doesn't refer to a socket
5183 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5184 there's no new data
5185
5186 @returns uint32_t (converted to local endianness)
5187 @qualifier TLS
5188 *///=========================================================================
5189 uint32_t recv_uint32_msb(
5190 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5191 const int flags = 0) {
5192 uint32_t buf(0);
5193 if (__debug) debug("recv_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5194 + ", <buf>"
5195 + ", " + std::to_string(sizeof(buf))
5196 + ", " + std::to_string(flags)
5197 + ");");
5198 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5199 return __endian_is_msb ? buf : ntohl(buf);
5200 }; // -x- uint32_t recv_uint32_msb -x-
5201
5202 /*======================================================================*//**
5203 @brief
5204 Receive one 64-bit unsigned integer of data in LSB (little endian) order from
5205 the endpoint.
5206 @par Notes
5207 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5208 reception, which implements temporary blocking while waiting for data.
5209
5210 @throws randolf::rex::xEBADF The underlying socket is not open
5211 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5212 connections
5213 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5214 part of the user address space
5215 @throws randolf::rex::xEINTR Interrupted by a signal
5216 @throws randolf::rex::xEINVAL Invalid argument passed
5217 @throws randolf::rex::xENOMEM Insufficient memory
5218 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5219 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5220 doesn't refer to a socket
5221 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5222 there's no new data
5223
5224 @returns uint64_t (converted to local endianness)
5225 @qualifier TLS
5226 *///=========================================================================
5227 uint64_t recv_uint64_lsb(
5228 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5229 const int flags = 0) {
5230 uint64_t buf(0);
5231 if (__debug) debug("recv_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5232 + ", <buf>"
5233 + ", " + std::to_string(sizeof(buf))
5234 + ", " + std::to_string(flags)
5235 + ");");
5236 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5237 return !__endian_is_msb ? buf : ((((uint64_t)ntohl((buf) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((buf) >> 32)));
5238 }; // -x- uint64_t recv_uint64_lsb -x-
5239
5240 /*======================================================================*//**
5241 @brief
5242 Receive one 64-bit unsigned integer of data in MSB (big endian) order from
5243 the endpoint.
5244 @par Notes
5245 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5246 reception, which implements temporary blocking while waiting for data.
5247
5248 @throws randolf::rex::xEBADF The underlying socket is not open
5249 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5250 connections
5251 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5252 part of the user address space
5253 @throws randolf::rex::xEINTR Interrupted by a signal
5254 @throws randolf::rex::xEINVAL Invalid argument passed
5255 @throws randolf::rex::xENOMEM Insufficient memory
5256 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5257 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5258 doesn't refer to a socket
5259 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5260 there's no new data
5261
5262 @returns uint64_t (converted to local endianness)
5263 @qualifier TLS
5264 *///=========================================================================
5265 uint64_t recv_uint64_msb(
5266 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5267 const int flags = 0) {
5268 uint64_t buf(0);
5269 if (__debug) debug("recv_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5270 + ", <buf>"
5271 + ", " + std::to_string(sizeof(buf))
5272 + ", " + std::to_string(flags)
5273 + ");");
5274 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5275 return __endian_is_msb ? buf : ((((uint64_t)htonl((buf) & 0xffffffffUL)) << 32) | htonl((uint32_t)((buf) >> 32)));
5276 }; // -x- uint64_t recv_uint64_msb -x-
5277
5278 /*======================================================================*//**
5279 @brief
5280 Receive data from a specific endpoint.
5281
5282 @par Notes
5283 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5284 reception, but it's important to note that it does implement temporary
5285 blocking while waiting for data.
5286
5287 @warning
5288 This method is not compatible with TLS.
5289
5290 @throws randolf::rex::xEBADF The underlying socket is not open
5291 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5292 connections
5293 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5294 part of the user address space
5295 @throws randolf::rex::xEINTR Interrupted by a signal
5296 @throws randolf::rex::xEINVAL Invalid argument passed
5297 @throws randolf::rex::xENOMEM Insufficient memory
5298 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5299 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5300 doesn't refer to a socket
5301 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5302 there's no new data
5303
5304 @returns Appropriately-sized vector of characters
5305 @qualifier POSIX
5306 *///=========================================================================
5307 std::vector<char> recvfrom(
5308 /// Maximum number of bytes to receive
5309 const size_t nbytes,
5310 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5311 const int flags,
5312 /// Target endpoint address structure
5313 struct sockaddr *from,
5314 /// Size of target endpoint structure
5315 socklen_t fromlen = sizeof(sockaddr)) {
5316 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5317 if (__debug) debug("recvfrom(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5318 + ", <buf>"
5319 + ", " + std::to_string(buf_size)
5320 + ", " + std::to_string(flags)
5321 + ", <sockaddr>"
5322 + ", " + std::to_string(fromlen)
5323 + ");");
5324 std::vector<char> v(buf_size);
5325 v.resize(__track_bytes_rx(__rc_check(::recvfrom(__socket_fd, v.data(), v.size(), flags, from, &fromlen))));
5326 return v;
5327 }; // -x- std::vector<char> recvfrom -x-
5328
5329 private:
5330 /*======================================================================*//**
5331 @brief
5332 This is an internal function that:
5333 1. receives data from the endpoint:
5334 - via underlying socket when TLS is not enabled
5335 - via the OpenSSL socket API when TLS is enabled
5336 2. checks for a socket I/O error (and throws an exception)
5337 3. tracks number of bytes received (if there were no errors)
5338
5339 @throws randolf::rex::xEAGAIN The underlying socket timed out
5340 @throws randolf::rex::xEBADF The underlying socket is not open
5341 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5342 connections
5343 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5344 part of the user address space
5345 @throws randolf::rex::xEINTR Interrupted by a signal
5346 @throws randolf::rex::xEINVAL Invalid argument passed
5347 @throws randolf::rex::xENOMEM Insufficient memory
5348 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5349 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5350 doesn't refer to a socket
5351 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5352 there's no new data
5353
5354 @returns Number of bytes that were successfully received
5355 @qualifier TLS
5356 *///=========================================================================
5357 int __recv(
5358 /// Pointer to data
5359 void* data,
5360 /// Length of message data
5361 const size_t len,
5362 /// Flags (ignored with encrypted streams, except for the MSG_PEEK flag)
5363 const int flags = 0) {
5364
5365// TODO: For MSG_PEEK with OpenSSL, try this suggestion: You can get the underlying file descriptor with BIO_get_fd and then call recv() with the MSG_PEEK flag
5366
5367 // --------------------------------------------------------------------------
5368 // When internal buffering is not set up (which is the default operation), we
5369 // simply passing data directly.
5370 // --------------------------------------------------------------------------
5371 if (__buffer == nullptr) {
5372 return __tls ? (flags & MSG_PEEK ? __rc_check_tls(SSL_peek(__tls_fd, data, len))
5373 : __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, data, len))))
5374 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, data, len, flags)));
5375 } // -x- if !__buffer -x-
5376
5377 // --------------------------------------------------------------------------
5378 // Consume data from socket to make up for OpenSSL's inability to read beyond
5379 // one packet of data in its SSL_peek() and SSL_read() methods, and also some
5380 // inconsistencies with certain raw (unencrypted) socket implementations that
5381 // occasionally refuse to read beyond one packet of data (at least on a first
5382 // attempt to read with or without the MSG_PEEK flag).
5383 // --------------------------------------------------------------------------
5384
5385/*
5386std::cout << "-(0)- __buffer=" << (long)__buffer << std::endl
5387 << " __buffer_head=" << (long)__buffer_head << std::endl
5388 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5389 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5390 << " __buffer_size=" << __buffer_size << std::endl
5391 << " len=" << len << std::endl;
5392*/
5393 // --------------------------------------------------------------------------
5394 // Fill internal buffer. It may be more than len is set to, but this is okay
5395 // because we're committed to using internal buffering now anyway (it's far
5396 // more likely that a protocol that requires the use of recvline() once is
5397 // going to be using it repeatedly).
5398 // --------------------------------------------------------------------------
5399 int n;
5400
5401 // --------------------------------------------------------------------------
5402 // If the amount of data contained in the internal buffer is sufficient to
5403 // satisfy the amount of data that "len" represents, then skip the attempts
5404 // to read from the socket and just drain from the buffer what's needed.
5405 // --------------------------------------------------------------------------
5406 if ((__buffer_head >= __buffer_tail // Does the buffer wrap around?
5407 ? __buffer_head - __buffer_tail // No, so calcluation is straight-forward
5408 : (__buffer_boundary - __buffer_tail) // Yes, so we need the sum of both parts
5409 + (__buffer_head - __buffer)) >= len) goto __recv_drain;
5410
5411 __recv_loop:
5412 // --------------------------------------------------------------------------
5413 // Tail is behind head, so do one extra read beforehand to fill it up, which
5414 // affects the first part of a wrap-around since this is a circular buffer.
5415 // --------------------------------------------------------------------------
5416 if (__buffer_head >= __buffer_tail) { // std::cout << "__buffer_head >= __buffer_tail" << std::endl;
5417 n = __tls ? __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, __buffer_head, std::min(len, (size_t)(__buffer_boundary - __buffer_head))))) // Not using SSL_peek() at all here because we need to consume this data as we stuff it into the internal buffer
5418 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, __buffer_head, std::min(len, (size_t)(__buffer_boundary - __buffer_head)), flags & (~MSG_PEEK)))); // Mask the MSG_PEEK flag because we need to consume this data as we stuff it into the internal buffer
5419//std::cout << "n(0)=" << n << std::endl;
5420 if ((__buffer_head += n) > __buffer_boundary) __buffer_head = __buffer; // Advance head (and wrap around if we're at the boundary)
5421// TODO: Test wrap-around, make sure we don't need >= instead of >
5422 } // -x- if __buffer_head -x-
5423
5424/*
5425std::cout << "-(1)- __buffer=" << (long)__buffer << std::endl
5426 << " __buffer_head=" << (long)__buffer_head << std::endl
5427 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5428 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5429 << " __buffer_size=" << __buffer_size << std::endl
5430 << " len=" << len << std::endl;
5431*/
5432//std::cout << "Buffer: [" << __buffer_tail[0] << "]" << std::endl;
5433
5434 // --------------------------------------------------------------------------
5435 // Fill in the remaining (unused) portion of the internal buffer.
5436 // --------------------------------------------------------------------------
5437 if (__buffer_head < __buffer_tail && ioctl(__socket_fd, FIONREAD, &n) == 0) {
5438 n = __tls ? __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, __buffer_head, __buffer_head - __buffer_tail))) // Not using SSL_peek() at all here because we need to consume this data as we stuff it into the internal buffer
5439 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, __buffer_head, __buffer_head - __buffer_tail, flags & (~MSG_PEEK)))); // Mask the MSG_PEEK flag because we need to consume this data as we stuff it into the internal buffer
5440//std::cout << "n(1)=" << n << std::endl;
5441 __buffer_head += n; // Advance the head
5442 } // -x- if __buffer_head -x-
5443
5444 // --------------------------------------------------------------------------
5445 // Check if more bytes are available for reading, then run the loop again so
5446 // that we're pulling in as many bytes as possible. This makes up for a
5447 // problem with socket I/O functions not always returning all the data that's
5448 // ready to be consumed.
5449 // --------------------------------------------------------------------------
5450// if (__buffer_head != __buffer_tail && ioctl(__socket_fd, FIONREAD, &n) == 0 && n > 0) goto __recv_loop;
5451//std::cout << "Buffer size = " << __buffer_size << std::endl; // Debug
5452 // --------------------------------------------------------------------------
5453 // Drain the internal buffer. If we can drain it completely, we'll either
5454 // reset head and tail (default), or free() the internal buffer (based on the
5455 // internal policy). TODO: Create policy control options
5456 //
5457 // MSG_PEEK mode is limited to the internal buffer size. Not using this flag
5458 // is not limited, ... TODO: Figure out how to deal with this
5459 // --------------------------------------------------------------------------
5460 __recv_drain:
5461 char* drain = __buffer_tail;
5462//std::cout << "Drain: [";
5463 for (n = 0; n < len; n++) {
5464//if (*drain < 32) std::cout << "^" << (char)(drain[0] + 64); else std::cout << drain[0];
5465 ((char*)data)[n] = *drain++; // Copy byte, and advance the drain
5466//std::cout << "{" << (long)drain << "}";
5467 if (drain > __buffer_boundary) drain = __buffer; // Wrap around // TODO: Test this
5468 if (drain == __buffer_head) break; // We've reached the limit; there's no more data // TODO: Move this to earlier to avoid buffer overrun
5469 } // -x- for n -x-
5470 n++;
5471//std::cout << "] n=" << ++n << std::endl;
5472
5473 // --------------------------------------------------------------------------
5474 // Consume data in __buffer if this isn't a MSG_PEEK operation.
5475 // --------------------------------------------------------------------------
5476/*
5477std::cout << "-(2)- __buffer=" << (long)__buffer << std::endl
5478 << " __buffer_head=" << (long)__buffer_head << std::endl
5479 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5480 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5481 << " MSG_PEEK=" << (flags && MSG_PEEK ? "true" : "false") << std::endl
5482 << " len=" << len << std::endl
5483 << " drain=" << (long)drain << std::endl;
5484*/
5485// if ((flags & MSG_PEEK) == 0) std::cout << "[Consuming buffer]" << std::endl;
5486 if ((flags & MSG_PEEK) == 0) __buffer_tail = drain; // Consume character(s) unless MSG_PEEK flag is set
5487/*
5488std::cout << "-(3)- __buffer=" << (long)__buffer << std::endl
5489 << " __buffer_head=" << (long)__buffer_head << std::endl
5490 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5491 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5492 << " MSG_PEEK=" << (flags && MSG_PEEK ? "true" : "false") << std::endl
5493 << " len=" << len << std::endl
5494 << " drain=" << (long)drain << std::endl;
5495*/
5496 return n;
5497
5498 }; // -x- int __recv -x-
5499
5500 /*======================================================================*//**
5501 @brief
5502 This is an internal function that:
5503 1. receives a line of data from the endpoint:
5504 - via underlying socket when TLS is not enabled
5505 - via the OpenSSL socket API when TLS is enabled
5506 2. checks for a socket I/O error (and throws an exception)
5507 3. tracks number of bytes received (if there were no errors)
5508 4. isolates the EoL sequence from the line of data, and records it for
5509 later reference (for those cases where it's needed)
5510
5511 @throws randolf::rex::xEAGAIN if timeout occurs before EoL
5512 @throws randolf::rex::xEBADF The underlying socket is not open
5513 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5514 connections
5515 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5516 part of the user address space
5517 @throws randolf::rex::xEINTR Interrupted by a signal
5518 @throws randolf::rex::xEINVAL Invalid argument passed
5519 @throws randolf::rex::xENOMEM Insufficient memory
5520 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5521 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5522 doesn't refer to a socket
5523 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
5524 no EoL character sequence is detected after recvline's buffer became
5525 full (whatever data is waiting may still be received using any of the
5526 recv_ methods {with or without the @c MSG_PEEK flag set}, including
5527 @c recvline() with a larger buffer {see the @c nbytes parameter})
5528 @throws randolf::rex::xERANGE if the timeout parameter is below 0
5529 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5530 there's no new data
5531
5532 @qualifier TLS
5533 *///=========================================================================
5534 std::string __recvline(
5535 /// Maximum number of bytes to receive (including EoL character sequence)
5536 const size_t buf_size,
5537 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5538 const int flags,
5539 /// Line timeout (in seconds)@n
5540 /// 0 = no timeout (default), unless it was configured by way of the
5541 /// @ref timeout_recvline(long) method
5542 long timeout,
5543 /// Automatically consume bytes read during a failed read (this flag is
5544 /// ignored when the @c MSG_PEEK flag is set) just before throwing the
5545 /// randolf::rex::xEOVERFLOW exception
5546 bool discard_on_overflow) {
5547
5548 // --------------------------------------------------------------------------
5549 // Syntax checks as part of preparation of timeout variable for faster
5550 // comparisons within line-reading loop.
5551 // --------------------------------------------------------------------------
5552 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
5553 if (timeout == 0) timeout = __recvline_timeout;
5554 timeout = timeout == 0 ? LONG_MAX : timeout + time(0);
5555
5556 // --------------------------------------------------------------------------
5557 // Allocate line buffer.
5558 // --------------------------------------------------------------------------
5559 std::string v; v.resize(buf_size); // Add EoL size to buffer size; we'll probably be shrinking later anyway
5560
5561 // --------------------------------------------------------------------------
5562 // Create internal buffer for __recv (if it hasn't already been created).
5563 //
5564 // Buffering becomes necessary with TLS connection because SSL_peek fails to
5565 // return data that spans multiple packets. What triggers this behaviour is
5566 // the command "stty -icanon && openssl s_client host_name tcp_port" followed
5567 // by manual interactive typing some text then pressing the "[Enter]" key.
5568 //
5569 // The ::malloc(bsize) won't be leaked because it will be freed elsewhere,
5570 // including in this rsocket's destructor.
5571 // --------------------------------------------------------------------------
5572 if (__buffer == nullptr) {
5573 size_t bsize = std::max(__buffer_size, buf_size);
5574 __buffer = (char*)(::malloc(bsize)); //new char[bsize];
5575 __buffer_head = __buffer;
5576 __buffer_tail = __buffer;
5577 __buffer_boundary = __buffer + bsize;
5578 } // -x- if !__buffer -x-
5579
5580 // --------------------------------------------------------------------------
5581 // Line-reading loop.
5582 // --------------------------------------------------------------------------
5583 int len_with_eol = 0;
5584 int br = 0; // Number of Bytes Received
5585 try {
5586 br = __recv(v.data(), v.size(), MSG_PEEK | flags); // Look-ahead at socket stream (with buffer size that was inflated earlier to include EoL sequence)
5587 } catch (const randolf::rex::xEAGAIN e) { // Socket timeout occurred, but recvline timeout may not have yet so we need to catch it here to implement the recvline timeout
5588std::cout << "EAGAIN caught (recvline_check socket timeout occurred) WIP" << std::endl; // Debug
5589 __rc_check(nanosleep(&__recvline_idle, nullptr)); // Prevent tight CPU utilization loop; EINVAL = bad parameters (e.g., tv_nsec > 999999999 is not uncommon)
5590 }
5591 if (time(0) > timeout) throw randolf::rex::xEAGAIN("recvline timed out");
5592 int len = eol_index(v, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
5593 if (len < 0) {
5594if (br >= buf_size) std::cout << "overflow... br=" << br << " buf_size=" << buf_size << std::endl;
5595std::sleep(100);
5596 if (br >= buf_size) {
5597 if (discard_on_overflow && (flags & MSG_PEEK) != 0) __recv(v.data(), br, flags); // Consume undesired data before throwing exception
5598 throw randolf::rex::xEOVERFLOW("recvline overflow (line too long or missing EoL sequence)");
5599 } // -x- if br -x-
5600 } // -x- if !len -x-
5601
5602 // --------------------------------------------------------------------------
5603 // Save the EoL sequence that was detected following a successful recvline()
5604 // (this must be completed prior to "final clean-up" {which is next}, because
5605 // we need to copy these characters before the truncation occurs).
5606 // --------------------------------------------------------------------------
5607 __eol_consumed_seq = v.substr(len, len_with_eol - len);
5608
5609 // --------------------------------------------------------------------------
5610 // Final clean-up.
5611 // --------------------------------------------------------------------------
5612std::cout << "pre-drain" << std::endl;
5613 if ((flags & MSG_PEEK) == 0) __recv(v.data(), len_with_eol, flags); // Consume data if not in MSG_PEEK mode / __recv will throw exception if something goes wrong so there's no need for an additional __rc_check call from here
5614std::cout << "post-drain" << std::endl;
5615 v.resize(len); // Truncate string to exclude unused portion
5616 return v;
5617
5618 }; // -x- std::string __recvline -x-
5619
5620 public:
5621 /*======================================================================*//**
5622 @brief
5623 Receive a line of data from the endpoint, with the EoL character(s) removed.
5624 While this is meant for ASCII and UTF-8 text, it will also work with binary
5625 data that doesn't include EoL character sequences as non-line-ending data
5626 (when receiving binary data, the @ref recv() and @ref recvz() methods tend to
5627 be better-suited).
5628
5629 If @c nbytes is 0, then the internal buffer_size will be used as the default,
5630 which can also be changed from its compiled-in default of 1024 by using one
5631 of the buffer_size() methods. The total number of bytes received, including
5632 the EoL sequence, will not exceed the total number of byte specified with the
5633 @c nbytes parameter.
5634 @warning
5635 For @c nbytes the EoL character sequence size should be included, especially
5636 for shorter lines -- if the EoL character sequence is detected automatically
5637 on-the-fly (which is the default), then provisioning 2 characters is needed
5638 since an EoL sequence could be 1 or 2 characters long (line lengths shorter
5639 than 3 characters could be particularly problematic when detecting on-the-fly
5640 EoL character sequences). In such a case, a better approach is to allow for
5641 2 additional characters and then test the length of the returned string to
5642 ensure it doesn't exceed whatever the required maximum is (and throw the same
5643 @ref randolf::rex::xEOVERFLOW exception if the length is exceeded).
5644
5645 For more information about which characters an EoL sequence is comprised of,
5646 and how our specialized support for multiple EoL sequences works, see the
5647 documentation for the various @ref eol methods.
5648
5649 @pre
5650 When setting the recvline timeout (either with the @c timeout parameter with
5651 this method, or by using the @ref timeout_recvline(long) method), you may
5652 also need to set the socket timeout beforehand using the @ref timeout()
5653 method, because @c recvline doesn't do this...
5654 @n
5655 <blockquote>
5656 @ref timeout_recvline sets the overall total timeout for an entire line to
5657 be entered
5658 @n
5659 @ref timeout sets the timeout between individual characters received (such
5660 as keystrokes from a live end-user)
5661 </blockquote>
5662 @n
5663 If your socket timeout is longer than then recvline timeout, this will have
5664 the effect of rendering the recvling timeout as ineffective.
5665
5666 The @c timeout parameter can be used to override the total overall timeout
5667 for receiving a line, which is useful for specific situations where a
5668 specific timeout is desired for particular prompts, during certain data entry
5669 stages, etc.
5670
5671 @post
5672 When a line is not found (@c randolf::rex::xEAGAIN), or a line is too long
5673 because there's too much data (longer than a line) without an EoL sequence
5674 (@c randolf::rex::xOVERFLOW), then that data will not be discarded or
5675 consumed, and will remain ready for receiving (@ref recv) or for discarding
5676 @ref discard).
5677
5678 @par Notes
5679 If you're searching for a readline() method for socket I/O, then this is most
5680 likely what you're wanting/needing.
5681
5682 @throws randolf::rex::xEAGAIN if timeout occurs before EoL
5683 @throws randolf::rex::xEBADF The underlying socket is not open
5684 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5685 connections
5686 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5687 part of the user address space
5688 @throws randolf::rex::xEINTR Interrupted by a signal
5689 @throws randolf::rex::xEINVAL Invalid argument passed
5690 @throws randolf::rex::xENOMEM Insufficient memory
5691 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5692 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5693 doesn't refer to a socket
5694 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
5695 no EoL character sequence is detected after recvline's buffer became
5696 full (whatever data is waiting may still be received using any of the
5697 recv_ methods {with or without the @c MSG_PEEK flag set}, including
5698 @c recvline() with a larger buffer {see the @c nbytes parameter})
5699 @throws randolf::rex::xERANGE if the timeout parameter is below 0
5700 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5701 there's no new data
5702
5703 @returns One line of text as a std::string object.
5704 @see discard
5705 @see eol()
5706 @see eol_consumed_seq()
5707 @see recv_rline
5708 @see sendline(const std::string, const int)
5709 @see timeout
5710 @see timeout_recvline
5711 @see timeout_recvline(long)
5712 @qualifier TLS
5713 *///=========================================================================
5714 std::string recvline(
5715 /// Maximum number of bytes to receive (including EoL character sequence)@n
5716 /// 0 = use internal @ref buffer_size()
5717 const size_t nbytes = 0,
5718 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5719 const int flags = 0,
5720 /// Line timeout (in seconds)@n
5721 /// 0 = no timeout (default), unless it was configured by way of the
5722 /// @ref timeout_recvline(long) method
5723 long timeout = 0,
5724 /// Automatically consume bytes read during a failed read (this flag is
5725 /// ignored when the @c MSG_PEEK flag is set) just before throwing the
5726 /// randolf::rex::xEOVERFLOW exception
5727 bool discard_on_overflow = true) {
5728 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
5729 // + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF; TODO: Add an option for this
5730 // Internally, the length of the @ref eol() sequence is added to the buffer size
5731 // so that the maximum line length without EoL characters matches the maximum
5732 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
5733 if (__debug) debug("recvline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5734 + ", " + std::to_string(buf_size)
5735 + ", " + std::to_string(flags)
5736 + ");");
5737 return __recvline(buf_size, flags, timeout, discard_on_overflow);
5738 }; // -x- std::string recvline -x-
5739
5740 /*======================================================================*//**
5741 @brief
5742 Receive data in the form of a "msghdr" structure.
5743 @warning
5744 This method is not compatible with TLS.
5745
5746 @throws randolf::rex::xEBADF The underlying socket is not open
5747 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5748 connections
5749 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5750 part of the user address space
5751 @throws randolf::rex::xEINTR Interrupted by a signal
5752 @throws randolf::rex::xEINVAL Invalid argument passed
5753 @throws randolf::rex::xENOMEM Insufficient memory
5754 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5755 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5756 doesn't refer to a socket
5757 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5758 there's no new data
5759
5760 @returns pointer to "msghdr" structure
5761 @qualifier POSIX
5762 *///=========================================================================
5763 msghdr* recvmsg(
5764 /// Pointer to "msghdr" structure (or nullptr for automatic allocation)
5765 msghdr* msg,
5766 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5767 const int flags = 0) {
5768 if (__debug) debug("recvmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5769 + ", <msghdr>"
5770 + ", " + std::to_string(flags)
5771 + ");");
5772 if (msg == nullptr) msg = new msghdr{0};
5773 __track_bytes_rx(__rc_check(::recvmsg(__socket_fd, msg, flags)));
5774 return msg;
5775 }; // -x- msghdr* recvmsg -x-
5776
5777 /*======================================================================*//**
5778 @brief
5779 Receive data in the form of an "mmsghdr" structure.
5780 @warning
5781 This method is not compatible with TLS.
5782
5783 @throws randolf::rex::xEBADF The underlying socket is not open
5784 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5785 connections
5786 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5787 part of the user address space
5788 @throws randolf::rex::xEINTR Interrupted by a signal
5789 @throws randolf::rex::xEINVAL Invalid argument passed
5790 @throws randolf::rex::xENOMEM Insufficient memory
5791 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5792 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5793 doesn't refer to a socket
5794 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5795 there's no new data
5796
5797 @returns pointer to "mmsghdr" structure
5798 @qualifier POSIX
5799 *///=========================================================================
5800 mmsghdr* recvmmsg(
5801 /// Pointer to "mmsghdr" structure (or nullptr for automatic allocation)
5802 struct mmsghdr* mmsg,
5803 /// Size of target endpoint structure
5804 const unsigned int vlen = sizeof(mmsghdr),
5805 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
5806 const int flags = 0,
5807 /// Timeout
5808 struct timespec* timeout = {0}) {
5809 if (__debug) debug("recvmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5810 + ", <mmsghdr>"
5811 + ", " + std::to_string(vlen)
5812 + ", " + std::to_string(flags)
5813 + ", timeout{tv_sec=" + std::to_string(timeout->tv_sec)
5814 + ", tv_nsec=" + std::to_string(timeout->tv_nsec) + "}"
5815 + ");");
5816 __track_bytes_rx(__rc_check(::recvmmsg(__socket_fd, mmsg, vlen, flags, timeout)));
5817 return mmsg;
5818 }; // -x- mmsghdr* recvmmsg -x-
5819
5820 /*======================================================================*//**
5821 @brief
5822 Receive data from the endpoint, and add a 0 (null) onto the end. This is
5823 useful when using the resulting std::vector<char> as an ASCIIZ string.
5824
5825 If nbytes is 0, then the internal buffer_size will be used as the default,
5826 which can also be changed from its compiled-in default of 1024 by using one
5827 of the buffer_size() methods.
5828
5829 @post
5830 The resulting std::vector size is always inflated by 1. This means that
5831 relying on a comparison against 0 will result in an infinite loop:
5832 @code{.cpp}
5833 for (std::vector<char> v = r.recvz(); v.size > 0; v = recvz()) { ... }
5834 @endcode
5835 So, you'll need to compare against 1 instead of 0 to compensate fot the
5836 inflated size due to the addition of null character 0:
5837 @code{.cpp}
5838 for (std::vector<char> v = r.recvz(); v.size > 1; v = recvz()) { ... }
5839 @endcode
5840
5841 @warning
5842 The resulting size of std::vector<char> will be <tt>nbytes + 1</tt> which
5843 ensures that any string processing functions or presentation libraries - such
5844 as @c printf() - expecting an ASCIIZ string buffer will stop at null (0), and
5845 will reliably output no more than the maximum size specified.
5846 @n@n
5847 The way to think of this is that @c nbytes specifies the maximum number of
5848 bytes (a.k.a., characters) to recieve over the underlying socket, and the
5849 final 0 (null) being added is not included in the maximum specified by the
5850 @c nbytes parameter.
5851
5852 @par Notes
5853 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5854 reception, but it's important to note that it does implement temporary
5855 blocking while waiting for data.
5856
5857 @throws randolf::rex::xEBADF The underlying socket is not open
5858 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5859 connections
5860 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5861 part of the user address space
5862 @throws randolf::rex::xEINTR Interrupted by a signal
5863 @throws randolf::rex::xEINVAL Invalid argument passed
5864 @throws randolf::rex::xENOMEM Insufficient memory
5865 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5866 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5867 doesn't refer to a socket
5868 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5869 there's no new data
5870
5871 @returns appropriately-sized vector of characters + 1 (includes additional
5872 null terminator character 0)
5873 @see recv(const size_t, const int)
5874 @see recv(std::vector<char>, const int)
5875 @qualifier TLS
5876 *///=========================================================================
5877 std::vector<char> recvz(
5878 /// Maximum number of bytes to receive
5879 const size_t nbytes = 0,
5880 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5881 const int flags = 0) {
5882 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5883 if (__debug) debug("recvz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5884 + ", <buf>"
5885 + ", " + std::to_string(buf_size)
5886 + ", " + std::to_string(flags)
5887 + ");");
5888 std::vector<char> v(buf_size);
5889 v.resize(__recv(v.data(), v.size(), flags) + 1); // Add 1 to include room for final null character 0
5890 v[v.size() - 1] = (char)0; // Set final element to 0 (null)
5891 return v;
5892 }; // -x- std::vector<char> recvz -x-
5893
5894 private:
5895 /*======================================================================*//**
5896 @brief
5897 This is an internal function that:
5898 1. sends data to the endpoint:
5899 - via underlying socket when TLS is not enabled
5900 - via the OpenSSL socket API when TLS is enabled
5901 2. checks for a socket I/O error (and throws an exception)
5902 3. tracks number of bytes transmitted (if there were no errors)
5903
5904 @par Threads
5905 This method is threadsafe because the underlying POSIX send() and OpenSSL's
5906 SSL_write() functions are threadsafe.
5907
5908 @throws randolf::rex::xEBADF The underlying socket is not open
5909 @throws randolf::rex::xECONNRESET Connect reset by peer
5910 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5911 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5912 part of the user address space
5913 @throws randolf::rex::xEINTR Interrupted by a signal
5914 @throws randolf::rex::xEINVAL Invalid argument passed
5915 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5916 occur, but the POSIX sockets documentation lists it as one of the
5917 errors that can be returned, perhaps because some incorrectly
5918 implemented TCP/IP stacks return this error?)
5919 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5920 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5921 and 65,527 bytes for IPv6)
5922 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5923 network congestion (or, less commonly, insufficient memory)
5924 @throws randolf::rex::xENOMEM Insufficient memory
5925 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5926 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5927 doesn't refer to a socket
5928 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5929 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5930 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5931 isn't set)
5932 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5933 there's no new data
5934 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5935 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5936 but it really isn't)
5937
5938 @returns Number of bytes that were successfully transmitted
5939 @qualifier TLS
5940 *///=========================================================================
5941 int __send(
5942 /// Pointer to data
5943 const void* data,
5944 /// Length of message data
5945 const size_t len,
5946 /// Flags (ignored with encrypted streams)
5947 const int flags = 0) {
5948 if (__tls) return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data, len)));
5949 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data, len, flags)));
5950 }; // -x- int __send -x-
5951
5952 /*======================================================================*//**
5953 @brief
5954 This is an internal function that:
5955 1. sends data to the endpoint:
5956 - via underlying socket when TLS is not enabled
5957 - via the OpenSSL socket API when TLS is enabled
5958 2. checks for a socket I/O error (and throws an exception)
5959 3. tracks number of bytes transmitted (if there were no errors)
5960
5961 @par Threads
5962 This method is threadsafe because the underlying POSIX send() and OpenSSL's
5963 SSL_write() functions are threadsafe.
5964
5965 @throws randolf::rex::xEBADF The underlying socket is not open
5966 @throws randolf::rex::xECONNRESET Connect reset by peer
5967 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5968 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5969 part of the user address space
5970 @throws randolf::rex::xEINTR Interrupted by a signal
5971 @throws randolf::rex::xEINVAL Invalid argument passed
5972 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5973 occur, but the POSIX sockets documentation lists it as one of the
5974 errors that can be returned, perhaps because some incorrectly
5975 implemented TCP/IP stacks return this error?)
5976 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5977 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5978 and 65,527 bytes for IPv6)
5979 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5980 network congestion (or, less commonly, insufficient memory)
5981 @throws randolf::rex::xENOMEM Insufficient memory
5982 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5983 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5984 doesn't refer to a socket
5985 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5986 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5987 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5988 isn't set)
5989 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5990 there's no new data
5991 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5992 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5993 but it really isn't)
5994
5995 @returns Number of bytes that were successfully transmitted
5996 @qualifier TLS
5997 *///=========================================================================
5998 int __sendline(
5999 /// Pointer to data
6000 const void* data,
6001 /// Number of bytes to send
6002 const size_t len,
6003 /// Flags (ignored with encrypted streams)
6004 const int flags = 0) {
6005 if (__tls) { // Encrypted
6006 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data , len )))
6007 + __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.c_str(), __eol_out.length())));
6008 } else { // Not encrypted
6009 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data , len , MSG_MORE | flags))) // MSG_MORE prevents extra packets from being sent
6010 + __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.c_str(), __eol_out.length(), flags)));
6011 } // -x- if __tls -x-
6012 }; // -x- int __sendline -x-
6013
6014 /*======================================================================*//**
6015 @brief
6016 This is an internal function that:
6017 1. sends data to the endpoint:
6018 - via underlying socket when TLS is not enabled
6019 - via the OpenSSL socket API when TLS is enabled
6020 2. checks for a socket I/O error (and throws an exception)
6021 3. tracks number of bytes transmitted (if there were no errors)
6022
6023 @par Threads
6024 This method is threadsafe because the underlying POSIX send() and OpenSSL's
6025 SSL_write() functions are threadsafe.
6026
6027 @throws randolf::rex::xEBADF The underlying socket is not open
6028 @throws randolf::rex::xECONNRESET Connect reset by peer
6029 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6030 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6031 part of the user address space
6032 @throws randolf::rex::xEINTR Interrupted by a signal
6033 @throws randolf::rex::xEINVAL Invalid argument passed
6034 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6035 occur, but the POSIX sockets documentation lists it as one of the
6036 errors that can be returned, perhaps because some incorrectly
6037 implemented TCP/IP stacks return this error?)
6038 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6039 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6040 and 65,527 bytes for IPv6)
6041 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6042 network congestion (or, less commonly, insufficient memory)
6043 @throws randolf::rex::xENOMEM Insufficient memory
6044 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6045 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6046 doesn't refer to a socket
6047 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6048 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6049 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6050 isn't set)
6051 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6052 there's no new data
6053 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6054 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6055 but it really isn't)
6056
6057 @returns Number of bytes that were successfully transmitted
6058 @qualifier TLS
6059 *///=========================================================================
6060 int __send_eol(
6061 /// Flags (ignored with encrypted streams)
6062 const int flags = 0) {
6063 if (__tls) { // Encrypted
6064 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.c_str(), __eol_out.length())));
6065 } else { // Not encrypted
6066 return __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.c_str(), __eol_out.length(), flags)));
6067 } // -x- if __tls -x-
6068 }; // -x- int __send_eol -x-
6069 // TODO: Create a recv_eol() method that some people will probably consider to be evil (ha ha!)
6070 // recv_eol() will need to be able to auto-detect the CRLF sequence on-the-fly (if necessary) just like recvline() does
6071
6072 public:
6073 /*======================================================================*//**
6074 @brief
6075 Send data in the form of a std::string to the endpoint.
6076 @par Threads
6077 This method is threadsafe.
6078
6079 @throws randolf::rex::xEBADF The underlying socket is not open
6080 @throws randolf::rex::xECONNRESET Connect reset by peer
6081 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6082 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6083 part of the user address space
6084 @throws randolf::rex::xEINTR Interrupted by a signal
6085 @throws randolf::rex::xEINVAL Invalid argument passed
6086 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6087 occur, but the POSIX sockets documentation lists it as one of the
6088 errors that can be returned, perhaps because some incorrectly
6089 implemented TCP/IP stacks return this error?)
6090 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6091 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6092 and 65,527 bytes for IPv6)
6093 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6094 network congestion (or, less commonly, insufficient memory)
6095 @throws randolf::rex::xENOMEM Insufficient memory
6096 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6097 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6098 doesn't refer to a socket
6099 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6100 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6101 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6102 isn't set)
6103 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6104 there's no new data
6105 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6106 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6107 but it really isn't)
6108
6109 @returns The same rsocket object so as to facilitate stacking
6110 @qualifier TLS
6111 *///=========================================================================
6112 rsocket* send(
6113 /// Data to send
6114 const std::string msg,
6115 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6116 const int flags = 0) {
6117 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6118 + ", <std::string>"
6119 + ", " + std::to_string(msg.length())
6120 + ", " + std::to_string(flags)
6121 + ");");
6122 __send(msg.c_str(), msg.length(), flags);
6123 return this;
6124 }; // -x- rsocket* send -x-
6125
6126 /*======================================================================*//**
6127 @brief
6128 Send data in the form of a std::vector<char> to the endpoint.
6129 @par Threads
6130 This method is threadsafe.
6131
6132 @throws randolf::rex::xEBADF The underlying socket is not open
6133 @throws randolf::rex::xECONNRESET Connect reset by peer
6134 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6135 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6136 part of the user address space
6137 @throws randolf::rex::xEINTR Interrupted by a signal
6138 @throws randolf::rex::xEINVAL Invalid argument passed
6139 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6140 occur, but the POSIX sockets documentation lists it as one of the
6141 errors that can be returned, perhaps because some incorrectly
6142 implemented TCP/IP stacks return this error?)
6143 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6144 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6145 and 65,527 bytes for IPv6)
6146 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6147 network congestion (or, less commonly, insufficient memory)
6148 @throws randolf::rex::xENOMEM Insufficient memory
6149 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6150 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6151 doesn't refer to a socket
6152 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6153 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6154 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6155 isn't set)
6156 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6157 there's no new data
6158 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6159 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6160 but it really isn't)
6161
6162 @returns The same rsocket object so as to facilitate stacking
6163 @qualifier TLS
6164 *///=========================================================================
6165 rsocket* send(
6166 /// Data to send
6167 const std::vector<char> msg,
6168 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6169 const int flags = 0) {
6170 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6171 + ", <std::vector>"
6172 + ", " + std::to_string(msg.size())
6173 + ", " + std::to_string(flags)
6174 + ");");
6175 __send(msg.data(), msg.size(), flags);
6176 return this;
6177 }; // -x- rsocket* send -x-
6178
6179 /*======================================================================*//**
6180 @brief
6181 Send data in the form of a C-string to the endpoint.
6182 @par Threads
6183 This method is threadsafe.
6184
6185 @throws randolf::rex::xEBADF The underlying socket is not open
6186 @throws randolf::rex::xECONNRESET Connect reset by peer
6187 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6188 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6189 part of the user address space
6190 @throws randolf::rex::xEINTR Interrupted by a signal
6191 @throws randolf::rex::xEINVAL Invalid argument passed
6192 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6193 occur, but the POSIX sockets documentation lists it as one of the
6194 errors that can be returned, perhaps because some incorrectly
6195 implemented TCP/IP stacks return this error?)
6196 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6197 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6198 and 65,527 bytes for IPv6)
6199 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6200 network congestion (or, less commonly, insufficient memory)
6201 @throws randolf::rex::xENOMEM Insufficient memory
6202 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6203 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6204 doesn't refer to a socket
6205 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6206 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6207 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6208 isn't set)
6209 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6210 there's no new data
6211 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6212 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6213 but it really isn't)
6214
6215 @returns The same rsocket object so as to facilitate stacking
6216 @qualifier POSIX
6217 @qualifier TLS
6218 *///=========================================================================
6219 rsocket* send(
6220 /// Pointer to data to send
6221 const char* msg,
6222 /// Number of bytes to send, or 0 to auto-detect length if message is an ASCIIZ string
6223 size_t len = 0,
6224 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6225 const int flags = 0) {
6226 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6227 + ", <msg>"
6228 + ", " + std::to_string(len)
6229 + ", " + std::to_string(flags)
6230 + ");");
6231
6232 // --------------------------------------------------------------------------
6233 // Measure size of format string if an ASCIIZ string was indicated.
6234 // --------------------------------------------------------------------------
6235 if (len == 0) len = std::strlen(msg);
6236
6237 // --------------------------------------------------------------------------
6238 // Send string.
6239 // --------------------------------------------------------------------------
6240 __send(msg, len, flags);
6241 return this;
6242 }; // -x- rsocket* send -x-
6243
6244 /*======================================================================*//**
6245 @brief
6246 Send data in the form of an ASCIIZ string to the endpoint, including the
6247 terminating NULL character.
6248 @par Threads
6249 This method is threadsafe.
6250
6251 @throws randolf::rex::xEBADF The underlying socket is not open
6252 @throws randolf::rex::xECONNRESET Connect reset by peer
6253 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6254 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6255 part of the user address space
6256 @throws randolf::rex::xEINTR Interrupted by a signal
6257 @throws randolf::rex::xEINVAL Invalid argument passed
6258 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6259 occur, but the POSIX sockets documentation lists it as one of the
6260 errors that can be returned, perhaps because some incorrectly
6261 implemented TCP/IP stacks return this error?)
6262 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6263 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6264 and 65,527 bytes for IPv6)
6265 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6266 network congestion (or, less commonly, insufficient memory)
6267 @throws randolf::rex::xENOMEM Insufficient memory
6268 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6269 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6270 doesn't refer to a socket
6271 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6272 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6273 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6274 isn't set)
6275 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6276 there's no new data
6277 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6278 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6279 but it really isn't)
6280
6281 @returns The same rsocket object so as to facilitate stacking
6282 @see recv_asciiz(const size_t, const int)
6283 @see sendz(const char*, const int) which doesn't transmit the terminating
6284 NULL character
6285 @qualifier TLS
6286 *///=========================================================================
6287 rsocket* send_asciiz(
6288 /// Pointer to data to send
6289 const char* msg,
6290 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6291 const int flags = 0) {
6292 if (__debug) debug("send_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6293 + ", <msg>"
6294 + ", " + std::to_string(std::strlen(msg) + 1)
6295 + ", " + std::to_string(flags)
6296 + ");");
6297 __send(msg, std::strlen(msg) + 1, flags);
6298 return this;
6299 }; // -x- rsocket* send_asciiz -x-
6300
6301 /*======================================================================*//**
6302 @brief
6303 Send one 8-bit byte (one unsigned character) of data to the endpoint.
6304 @par Threads
6305 This method is threadsafe.
6306
6307 @throws randolf::rex::xEBADF The underlying socket is not open
6308 @throws randolf::rex::xECONNRESET Connect reset by peer
6309 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6310 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6311 part of the user address space
6312 @throws randolf::rex::xEINTR Interrupted by a signal
6313 @throws randolf::rex::xEINVAL Invalid argument passed
6314 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6315 occur, but the POSIX sockets documentation lists it as one of the
6316 errors that can be returned, perhaps because some incorrectly
6317 implemented TCP/IP stacks return this error?)
6318 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6319 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6320 and 65,527 bytes for IPv6)
6321 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6322 network congestion (or, less commonly, insufficient memory)
6323 @throws randolf::rex::xENOMEM Insufficient memory
6324 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6325 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6326 doesn't refer to a socket
6327 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6328 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6329 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6330 isn't set)
6331 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6332 there's no new data
6333 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6334 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6335 but it really isn't)
6336
6337 @returns The same rsocket object so as to facilitate stacking
6338 @see recv_byte
6339 @see recv_char
6340 @see send_char
6341 @qualifier TLS
6342 *///=========================================================================
6343 rsocket* send_byte(
6344 /// Data to send
6345 const u_char value,
6346 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6347 const int flags = 0) {
6348 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6349 + ", <value>"
6350 + ", " + std::to_string(sizeof(value))
6351 + ", " + std::to_string(flags)
6352 + ");");
6353 __send(&value, sizeof(value), flags);
6354 return this;
6355 }; // -x- rsocket* send_byte -x-
6356
6357 /*======================================================================*//**
6358 @brief
6359 Send one signed character (one 8-bit byte) of data to the endpoint.
6360 @copydoc send_byte(char, int)
6361 @returns The same rsocket object so as to facilitate stacking
6362 @see recv_byte
6363 @see recv_char
6364 @see send_byte
6365 @qualifier TLS
6366 *///=========================================================================
6367 rsocket* send_char(
6368 /// Data to send
6369 const char value,
6370 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6371 const int flags = 0) {
6372 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6373 + ", <value>"
6374 + ", " + std::to_string(sizeof(value))
6375 + ", " + std::to_string(flags)
6376 + ");");
6377 __send(&value, sizeof(value), flags);
6378 return this;
6379 }; // -x- rsocket* send_char -x-
6380
6381 /*======================================================================*//**
6382 @brief
6383 Send the EoL sequence to the endpoint.
6384 @par Threads
6385 This method is threadsafe.
6386
6387 @throws randolf::rex::xEBADF The underlying socket is not open
6388 @throws randolf::rex::xECONNRESET Connect reset by peer
6389 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6390 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6391 part of the user address space
6392 @throws randolf::rex::xEINTR Interrupted by a signal
6393 @throws randolf::rex::xEINVAL Invalid argument passed
6394 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6395 occur, but the POSIX sockets documentation lists it as one of the
6396 errors that can be returned, perhaps because some incorrectly
6397 implemented TCP/IP stacks return this error?)
6398 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6399 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6400 and 65,527 bytes for IPv6)
6401 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6402 network congestion (or, less commonly, insufficient memory)
6403 @throws randolf::rex::xENOMEM Insufficient memory
6404 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6405 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6406 doesn't refer to a socket
6407 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6408 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6409 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6410 isn't set)
6411 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6412 there's no new data
6413 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6414 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6415 but it really isn't)
6416
6417 @returns The same rsocket object so as to facilitate stacking
6418 @qualifier TLS
6419 *///=========================================================================
6420 rsocket* send_eol(
6421 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6422 const int flags = 0) {
6423 if (__debug) debug("send_eol(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6424 + ", " + std::to_string(flags)
6425 + ");");
6426 __send_eol(flags);
6427 return this;
6428 }; // -x- rsocket* send_eol -x-
6429
6430 /*======================================================================*//**
6431 @brief
6432 Send a data structure to the endpoint.
6433 @pre
6434 MSB/LSB considerations are important for any integers within your structure,
6435 so be sure to sanitize them with htons(), ntohs(), and related methods prior
6436 to sending, and then after receiving. This way, your data will be protected
6437 against corruption resulting from byte order differences when communicating
6438 between hardware architectures that differ in MSB and LSB byte ordering.
6439 @par Threads
6440 This method is threadsafe.
6441
6442 @throws randolf::rex::xEBADF The underlying socket is not open
6443 @throws randolf::rex::xECONNRESET Connect reset by peer
6444 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6445 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6446 part of the user address space
6447 @throws randolf::rex::xEINTR Interrupted by a signal
6448 @throws randolf::rex::xEINVAL Invalid argument passed
6449 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6450 occur, but the POSIX sockets documentation lists it as one of the
6451 errors that can be returned, perhaps because some incorrectly
6452 implemented TCP/IP stacks return this error?)
6453 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6454 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6455 and 65,527 bytes for IPv6)
6456 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6457 network congestion (or, less commonly, insufficient memory)
6458 @throws randolf::rex::xENOMEM Insufficient memory
6459 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6460 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6461 doesn't refer to a socket
6462 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6463 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6464 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6465 isn't set)
6466 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6467 there's no new data
6468 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6469 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6470 but it really isn't)
6471
6472 @returns The same rsocket object so as to facilitate stacking
6473 @see recv_struct
6474 @qualifier TLS
6475 *///=========================================================================
6476 template <typename T> rsocket* send_struct(
6477 /// Data to send
6478 const T value,
6479 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6480 const int flags = 0) {
6481 if (__debug) debug("send_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6482 + ", <value>"
6483 + ", " + std::to_string(sizeof(value))
6484 + ", " + std::to_string(flags)
6485 + ");");
6486 __send(&value, sizeof(value), flags);
6487 return this;
6488 }; // -x- rsocket* send_struct -x-
6489
6490 /*======================================================================*//**
6491 @brief
6492 Send one 16-bit unsigned integer of data in LSB (little endian) order to the
6493 endpoint.
6494 @par Threads
6495 This method is threadsafe.
6496
6497 @throws randolf::rex::xEBADF The underlying socket is not open
6498 @throws randolf::rex::xECONNRESET Connect reset by peer
6499 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6500 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6501 part of the user address space
6502 @throws randolf::rex::xEINTR Interrupted by a signal
6503 @throws randolf::rex::xEINVAL Invalid argument passed
6504 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6505 occur, but the POSIX sockets documentation lists it as one of the
6506 errors that can be returned, perhaps because some incorrectly
6507 implemented TCP/IP stacks return this error?)
6508 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6509 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6510 and 65,527 bytes for IPv6)
6511 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6512 network congestion (or, less commonly, insufficient memory)
6513 @throws randolf::rex::xENOMEM Insufficient memory
6514 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6515 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6516 doesn't refer to a socket
6517 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6518 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6519 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6520 isn't set)
6521 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6522 there's no new data
6523 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6524 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6525 but it really isn't)
6526
6527 @returns The same rsocket object so as to facilitate stacking
6528 @qualifier TLS
6529 *///=========================================================================
6530 rsocket* send_uint16_lsb(
6531 /// Data to send
6532 const uint16_t value,
6533 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6534 const int flags = 0) {
6535 uint16_t buf = !__endian_is_msb ? value : ntohs(value);
6536 if (__debug) debug("send_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6537 + ", " + std::to_string(value)
6538 + ", " + std::to_string(sizeof(buf))
6539 + ", " + std::to_string(flags)
6540 + ");");
6541 __send(&buf, sizeof(buf), flags);
6542 return this;
6543 }; // -x- rsocket* send_uint16_lsb -x-
6544
6545 /*======================================================================*//**
6546 @brief
6547 Send one 16-bit integer of data in MSB (big endian) order to the endpoint.
6548 @par Threads
6549 This method is threadsafe.
6550
6551 @throws randolf::rex::xEBADF The underlying socket is not open
6552 @throws randolf::rex::xECONNRESET Connect reset by peer
6553 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6554 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6555 part of the user address space
6556 @throws randolf::rex::xEINTR Interrupted by a signal
6557 @throws randolf::rex::xEINVAL Invalid argument passed
6558 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6559 occur, but the POSIX sockets documentation lists it as one of the
6560 errors that can be returned, perhaps because some incorrectly
6561 implemented TCP/IP stacks return this error?)
6562 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6563 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6564 and 65,527 bytes for IPv6)
6565 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6566 network congestion (or, less commonly, insufficient memory)
6567 @throws randolf::rex::xENOMEM Insufficient memory
6568 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6569 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6570 doesn't refer to a socket
6571 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6572 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6573 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6574 isn't set)
6575 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6576 there's no new data
6577 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6578 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6579 but it really isn't)
6580
6581 @returns The same rsocket object so as to facilitate stacking
6582 @qualifier TLS
6583 *///=========================================================================
6584 rsocket* send_uint16_msb(
6585 /// Data to send
6586 const uint16_t value,
6587 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6588 const int flags = 0) {
6589 int16_t buf = __endian_is_msb ? value : htons(value);
6590 if (__debug) debug("send_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6591 + ", " + std::to_string(value)
6592 + ", " + std::to_string(sizeof(buf))
6593 + ", " + std::to_string(flags)
6594 + ");");
6595 __send(&buf, sizeof(buf), flags);
6596 return this;
6597 }; // -x- rsocket* send_int16_msb -x-
6598
6599 /*======================================================================*//**
6600 @brief
6601 Send one 32-bit unsigned integer of data in LSB (little endian) order to the
6602 endpoint.
6603 @par Threads
6604 This method is threadsafe.
6605
6606 @throws randolf::rex::xEBADF The underlying socket is not open
6607 @throws randolf::rex::xECONNRESET Connect reset by peer
6608 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6609 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6610 part of the user address space
6611 @throws randolf::rex::xEINTR Interrupted by a signal
6612 @throws randolf::rex::xEINVAL Invalid argument passed
6613 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6614 occur, but the POSIX sockets documentation lists it as one of the
6615 errors that can be returned, perhaps because some incorrectly
6616 implemented TCP/IP stacks return this error?)
6617 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6618 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6619 and 65,527 bytes for IPv6)
6620 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6621 network congestion (or, less commonly, insufficient memory)
6622 @throws randolf::rex::xENOMEM Insufficient memory
6623 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6624 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6625 doesn't refer to a socket
6626 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6627 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6628 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6629 isn't set)
6630 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6631 there's no new data
6632 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6633 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6634 but it really isn't)
6635
6636 @returns The same rsocket object so as to facilitate stacking
6637 @qualifier TLS
6638 *///=========================================================================
6639 rsocket* send_uint32_lsb(
6640 /// Data to send
6641 const uint32_t value,
6642 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6643 const int flags = 0) {
6644 uint32_t buf = !__endian_is_msb ? value : ntohl(value);
6645 if (__debug) debug("send_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6646 + ", " + std::to_string(value)
6647 + ", " + std::to_string(sizeof(buf))
6648 + ", " + std::to_string(flags)
6649 + ");");
6650 __send(&buf, sizeof(buf), flags);
6651 return this;
6652 }; // -x- rsocket* send_uint32_lsb -x-
6653
6654 /*======================================================================*//**
6655 @brief
6656 Send one 32-bit unsigned integer of data in MSB (big endian) order to the
6657 endpoint.
6658 @par Threads
6659 This method is threadsafe.
6660
6661 @throws randolf::rex::xEBADF The underlying socket is not open
6662 @throws randolf::rex::xECONNRESET Connect reset by peer
6663 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6664 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6665 part of the user address space
6666 @throws randolf::rex::xEINTR Interrupted by a signal
6667 @throws randolf::rex::xEINVAL Invalid argument passed
6668 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6669 occur, but the POSIX sockets documentation lists it as one of the
6670 errors that can be returned, perhaps because some incorrectly
6671 implemented TCP/IP stacks return this error?)
6672 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6673 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6674 and 65,527 bytes for IPv6)
6675 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6676 network congestion (or, less commonly, insufficient memory)
6677 @throws randolf::rex::xENOMEM Insufficient memory
6678 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6679 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6680 doesn't refer to a socket
6681 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6682 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6683 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6684 isn't set)
6685 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6686 there's no new data
6687 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6688 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6689 but it really isn't)
6690
6691 @returns The same rsocket object so as to facilitate stacking
6692 @qualifier TLS
6693 *///=========================================================================
6694 rsocket* send_uint32_msb(
6695 /// Data to send
6696 const uint32_t value,
6697 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6698 const int flags = 0) {
6699 uint32_t buf = __endian_is_msb ? value : htonl(value);
6700 if (__debug) debug("send_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6701 + ", " + std::to_string(value)
6702 + ", " + std::to_string(sizeof(buf))
6703 + ", " + std::to_string(flags)
6704 + ");");
6705 __send(&buf, sizeof(buf), flags);
6706 return this;
6707 }; // -x- rsocket* send_uint32_msb -x-
6708
6709 /*======================================================================*//**
6710 @brief
6711 Send one 64-bit unsigned integer of data in LSB (little endian) order to the
6712 endpoint.
6713 @par Threads
6714 This method is threadsafe.
6715
6716 @throws randolf::rex::xEBADF The underlying socket is not open
6717 @throws randolf::rex::xECONNRESET Connect reset by peer
6718 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6719 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6720 part of the user address space
6721 @throws randolf::rex::xEINTR Interrupted by a signal
6722 @throws randolf::rex::xEINVAL Invalid argument passed
6723 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6724 occur, but the POSIX sockets documentation lists it as one of the
6725 errors that can be returned, perhaps because some incorrectly
6726 implemented TCP/IP stacks return this error?)
6727 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6728 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6729 and 65,527 bytes for IPv6)
6730 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6731 network congestion (or, less commonly, insufficient memory)
6732 @throws randolf::rex::xENOMEM Insufficient memory
6733 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6734 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6735 doesn't refer to a socket
6736 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6737 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6738 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6739 isn't set)
6740 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6741 there's no new data
6742 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6743 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6744 but it really isn't)
6745
6746 @returns The same rsocket object so as to facilitate stacking
6747 @qualifier TLS
6748 *///=========================================================================
6749 rsocket* send_uint64_lsb(
6750 /// Data to send
6751 const uint64_t value,
6752 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6753 const int flags = 0) {
6754 uint64_t buf = !__endian_is_msb ? value : ((((uint64_t)ntohl((value) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((value) >> 32)));
6755 if (__debug) debug("send_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6756 + ", " + std::to_string(value)
6757 + ", " + std::to_string(sizeof(buf))
6758 + ", " + std::to_string(flags)
6759 + ");");
6760 __send(&buf, sizeof(buf), flags);
6761 return this;
6762 }; // -x- rsocket* send_uint64_lsb -x-
6763
6764 /*======================================================================*//**
6765 @brief
6766 Send one 64-bit unsigned integer of data in MSB (big endian) order to the
6767 endpoint.
6768 @par Threads
6769 This method is threadsafe.
6770
6771 @throws randolf::rex::xEBADF The underlying socket is not open
6772 @throws randolf::rex::xECONNRESET Connect reset by peer
6773 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6774 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6775 part of the user address space
6776 @throws randolf::rex::xEINTR Interrupted by a signal
6777 @throws randolf::rex::xEINVAL Invalid argument passed
6778 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6779 occur, but the POSIX sockets documentation lists it as one of the
6780 errors that can be returned, perhaps because some incorrectly
6781 implemented TCP/IP stacks return this error?)
6782 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6783 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6784 and 65,527 bytes for IPv6)
6785 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6786 network congestion (or, less commonly, insufficient memory)
6787 @throws randolf::rex::xENOMEM Insufficient memory
6788 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6789 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6790 doesn't refer to a socket
6791 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6792 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6793 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6794 isn't set)
6795 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6796 there's no new data
6797 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6798 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6799 but it really isn't)
6800
6801 @returns The same rsocket object so as to facilitate stacking
6802 @qualifier TLS
6803 *///=========================================================================
6804 rsocket* send_uint64_msb(
6805 /// Data to send
6806 const uint64_t value,
6807 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6808 const int flags = 0) {
6809 uint64_t buf = __endian_is_msb ? value : ((((uint64_t)htonl((value) & 0xffffffffUL)) << 32) | htonl((uint32_t)((value) >> 32)));
6810 if (__debug) debug("send_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6811 + ", " + std::to_string(value)
6812 + ", " + std::to_string(sizeof(buf))
6813 + ", " + std::to_string(flags)
6814 + ");");
6815 __send(&buf, sizeof(buf), flags);
6816 return this;
6817 }; // -x- rsocket* send_uint64_msb -x-
6818
6819 /*======================================================================*//**
6820 @brief
6821 Send data in the form of a std::string to the endpoint, with an EoL sequence
6822 appended.
6823 @par Threads
6824 This method is threadsafe.
6825
6826 @throws randolf::rex::xEBADF The underlying socket is not open
6827 @throws randolf::rex::xECONNRESET Connect reset by peer
6828 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6829 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6830 part of the user address space
6831 @throws randolf::rex::xEINTR Interrupted by a signal
6832 @throws randolf::rex::xEINVAL Invalid argument passed
6833 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6834 occur, but the POSIX sockets documentation lists it as one of the
6835 errors that can be returned, perhaps because some incorrectly
6836 implemented TCP/IP stacks return this error?)
6837 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6838 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6839 and 65,527 bytes for IPv6)
6840 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6841 network congestion (or, less commonly, insufficient memory)
6842 @throws randolf::rex::xENOMEM Insufficient memory
6843 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6844 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6845 doesn't refer to a socket
6846 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6847 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6848 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6849 isn't set)
6850 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6851 there's no new data
6852 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6853 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6854 but it really isn't)
6855
6856 @returns The same rsocket object so as to facilitate stacking
6857 @see eol
6858 @see printfline
6859 @see recvline(const size_t, const int)
6860 @see vprintfline
6861 @qualifier TLS
6862 *///=========================================================================
6863 rsocket* sendline(
6864 /// Data to send
6865 const std::string msg = std::string(),
6866 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6867 const int flags = 0) {
6868 if (__debug) debug("sendline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6869 + ", <std::string>"
6870 + ", " + std::to_string(msg.length())
6871 + "+" + std::to_string(__eol_out.length())
6872 + ", " + std::to_string(flags)
6873 + ");");
6874 __sendline(msg.c_str(), msg.length(), flags);
6875 return this;
6876 }; // -x- rsocket* sendline -x-
6877
6878 /*======================================================================*//**
6879 @brief
6880 Send data in the form of a "msghdr" structure to a specific endpoint.
6881 @warning
6882 This method is not compatible with TLS.
6883 @par Threads
6884 This method is threadsafe.
6885
6886 @throws randolf::rex::xEBADF The underlying socket is not open
6887 @throws randolf::rex::xECONNRESET Connect reset by peer
6888 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6889 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6890 part of the user address space
6891 @throws randolf::rex::xEINTR Interrupted by a signal
6892 @throws randolf::rex::xEINVAL Invalid argument passed
6893 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6894 occur, but the POSIX sockets documentation lists it as one of the
6895 errors that can be returned, perhaps because some incorrectly
6896 implemented TCP/IP stacks return this error?)
6897 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6898 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6899 and 65,527 bytes for IPv6)
6900 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6901 network congestion (or, less commonly, insufficient memory)
6902 @throws randolf::rex::xENOMEM Insufficient memory
6903 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6904 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6905 doesn't refer to a socket
6906 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6907 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6908 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6909 isn't set)
6910 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6911 there's no new data
6912 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6913 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6914 but it really isn't)
6915
6916 @returns The same rsocket object so as to facilitate stacking
6917 @qualifier POSIX
6918 *///=========================================================================
6919 rsocket* sendmsg(
6920 /// Pointer to data to send
6921 const struct msghdr* msg,
6922 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6923 const int flags = 0) {
6924 if (__debug) debug("sendmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6925 + ", <msghdr>"
6926 + ", " + std::to_string(flags)
6927 + ");");
6928 __track_bytes_tx(__rc_check(::sendmsg(__socket_fd, msg, flags)));
6929 return this;
6930 }; // -x- rsocket* sendmsg -x-
6931
6932 /*======================================================================*//**
6933 @brief
6934 Send data in the form of a "mmsghdr" structure to a specific endpoint.
6935 @warning
6936 This method is not compatible with TLS.
6937 @par Threads
6938 This method is threadsafe.
6939
6940 @throws randolf::rex::xEBADF The underlying socket is not open
6941 @throws randolf::rex::xECONNRESET Connect reset by peer
6942 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6943 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6944 part of the user address space
6945 @throws randolf::rex::xEINTR Interrupted by a signal
6946 @throws randolf::rex::xEINVAL Invalid argument passed
6947 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6948 occur, but the POSIX sockets documentation lists it as one of the
6949 errors that can be returned, perhaps because some incorrectly
6950 implemented TCP/IP stacks return this error?)
6951 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6952 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6953 and 65,527 bytes for IPv6)
6954 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6955 network congestion (or, less commonly, insufficient memory)
6956 @throws randolf::rex::xENOMEM Insufficient memory
6957 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6958 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6959 doesn't refer to a socket
6960 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6961 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6962 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6963 isn't set)
6964 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6965 there's no new data
6966 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6967 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6968 but it really isn't)
6969
6970 @returns The same rsocket object so as to facilitate stacking
6971 @qualifier POSIX
6972 *///=========================================================================
6973 rsocket* sendmmsg(
6974 /// Pointer to data to send
6975 struct mmsghdr* mmsg,
6976 /// Size of target endpoint structure
6977 const unsigned int vlen = sizeof(mmsghdr),
6978 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6979 const int flags = 0) {
6980 if (__debug) debug("sendmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6981 + ", <mmsghdr>"
6982 + ", " + std::to_string(vlen)
6983 + ", " + std::to_string(flags)
6984 + ");");
6985 __track_bytes_tx(__rc_check(::sendmmsg(__socket_fd, mmsg, vlen, flags)));
6986 return this;
6987 }; // -x- rsocket* sendmsg -x-
6988
6989 /*======================================================================*//**
6990 @brief
6991 Send data in the form of a std::string to a specific endpoint.
6992 @warning
6993 This method is not compatible with TLS.
6994 @par Threads
6995 This method is threadsafe.
6996
6997 @throws randolf::rex::xEBADF The underlying socket is not open
6998 @throws randolf::rex::xECONNRESET Connect reset by peer
6999 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7000 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7001 part of the user address space
7002 @throws randolf::rex::xEINTR Interrupted by a signal
7003 @throws randolf::rex::xEINVAL Invalid argument passed
7004 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7005 occur, but the POSIX sockets documentation lists it as one of the
7006 errors that can be returned, perhaps because some incorrectly
7007 implemented TCP/IP stacks return this error?)
7008 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7009 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7010 and 65,527 bytes for IPv6)
7011 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7012 network congestion (or, less commonly, insufficient memory)
7013 @throws randolf::rex::xENOMEM Insufficient memory
7014 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7015 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7016 doesn't refer to a socket
7017 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7018 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7019 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7020 isn't set)
7021 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7022 there's no new data
7023 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7024 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7025 but it really isn't)
7026
7027 @returns The same rsocket object so as to facilitate stacking
7028 @qualifier POSIX
7029 *///=========================================================================
7030 rsocket* sendto(
7031 /// Data to send
7032 const std::string msg,
7033 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
7034 const int flags,
7035 /// Target endpoint address structure
7036 const struct sockaddr *to,
7037 /// Size of target endpoint structure
7038 socklen_t tolen = sizeof(sockaddr)) {
7039 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7040 + ", <std::string>"
7041 + ", " + std::to_string(flags)
7042 + ", <sockaddr>"
7043 + ", " + std::to_string(tolen)
7044 + ");");
7045 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg.c_str(), msg.length(), flags, to, tolen)));
7046 return this;
7047 }; // -x- rsocket* sendto -x- // TODO: Create easier-to-use variants
7048
7049 /*======================================================================*//**
7050 @brief
7051 Send data in the form of a C-string to a specific endpoint.
7052 @warning
7053 This method is not compatible with TLS.
7054 @par Threads
7055 This method is threadsafe.
7056
7057 @throws randolf::rex::xEBADF The underlying socket is not open
7058 @throws randolf::rex::xECONNRESET Connect reset by peer
7059 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7060 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7061 part of the user address space
7062 @throws randolf::rex::xEINTR Interrupted by a signal
7063 @throws randolf::rex::xEINVAL Invalid argument passed
7064 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7065 occur, but the POSIX sockets documentation lists it as one of the
7066 errors that can be returned, perhaps because some incorrectly
7067 implemented TCP/IP stacks return this error?)
7068 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7069 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7070 and 65,527 bytes for IPv6)
7071 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7072 network congestion (or, less commonly, insufficient memory)
7073 @throws randolf::rex::xENOMEM Insufficient memory
7074 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7075 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7076 doesn't refer to a socket
7077 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7078 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7079 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7080 isn't set)
7081 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7082 there's no new data
7083 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7084 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7085 but it really isn't)
7086
7087 @returns The same rsocket object so as to facilitate stacking
7088 @qualifier POSIX
7089 *///=========================================================================
7090 rsocket* sendto(
7091 /// Pointer to data to send
7092 const char* msg,
7093 /// Number of bytes to send
7094 const size_t len,
7095 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
7096 const int flags,
7097 /// Target endpoint address structure
7098 const struct sockaddr *to,
7099 /// Size of target endpoint structure
7100 socklen_t tolen = sizeof(sockaddr)) {
7101 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7102 + ", <msg>"
7103 + ", " + std::to_string(std::strlen(msg))
7104 + ", " + std::to_string(flags)
7105 + ", <sockaddr>"
7106 + ", " + std::to_string(tolen)
7107 + ");");
7108 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, len, flags, to, tolen)));
7109 return this;
7110 }; // -x- rsocket* sendto -x-
7111
7112 /*======================================================================*//**
7113 @brief
7114 Send data in the form of an ASCIIZ string to the endpoint. The terminating
7115 NULL character won't be transmitted.
7116 @par Threads
7117 This method is threadsafe.
7118
7119 @throws randolf::rex::xEBADF The underlying socket is not open
7120 @throws randolf::rex::xECONNRESET Connect reset by peer
7121 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7122 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7123 part of the user address space
7124 @throws randolf::rex::xEINTR Interrupted by a signal
7125 @throws randolf::rex::xEINVAL Invalid argument passed
7126 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7127 occur, but the POSIX sockets documentation lists it as one of the
7128 errors that can be returned, perhaps because some incorrectly
7129 implemented TCP/IP stacks return this error?)
7130 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7131 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7132 and 65,527 bytes for IPv6)
7133 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7134 network congestion (or, less commonly, insufficient memory)
7135 @throws randolf::rex::xENOMEM Insufficient memory
7136 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7137 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7138 doesn't refer to a socket
7139 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7140 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7141 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7142 isn't set)
7143 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7144 there's no new data
7145 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7146 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7147 but it really isn't)
7148
7149 @returns The same rsocket object so as to facilitate stacking
7150 @see recvz(const size_t, const int)
7151 @see send_asciiz(const char*, const int) which also transmits the terminating
7152 NULL character
7153 @qualifier TLS
7154 *///=========================================================================
7155 rsocket* sendz(
7156 /// Pointer to data to send
7157 const char* msg,
7158 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
7159 const int flags = 0) {
7160 if (__debug) debug("sendz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7161 + ", <msg>"
7162 + ", " + std::to_string(std::strlen(msg))
7163 + ", " + std::to_string(flags)
7164 + ");");
7165 __send(msg, std::strlen(msg), flags);
7166 return this;
7167 }; // -x- rsocket* sendz -x-
7168
7169 /*======================================================================*//**
7170 @brief
7171 Send data in the form of an ASCIIZ string to a specific endpoint. The
7172 terminating NULL character won't be transmitted.
7173 @warning
7174 This method is not compatible with TLS.
7175 @par Threads
7176 This method is threadsafe.
7177
7178 @throws randolf::rex::xEBADF The underlying socket is not open
7179 @throws randolf::rex::xECONNRESET Connect reset by peer
7180 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7181 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7182 part of the user address space
7183 @throws randolf::rex::xEINTR Interrupted by a signal
7184 @throws randolf::rex::xEINVAL Invalid argument passed
7185 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7186 occur, but the POSIX sockets documentation lists it as one of the
7187 errors that can be returned, perhaps because some incorrectly
7188 implemented TCP/IP stacks return this error?)
7189 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7190 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7191 and 65,527 bytes for IPv6)
7192 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7193 network congestion (or, less commonly, insufficient memory)
7194 @throws randolf::rex::xENOMEM Insufficient memory
7195 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7196 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7197 doesn't refer to a socket
7198 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7199 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7200 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7201 isn't set)
7202 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7203 there's no new data
7204 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7205 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7206 but it really isn't)
7207
7208 @returns The same rsocket object so as to facilitate stacking
7209 *///=========================================================================
7210 rsocket* sendzto(
7211 /// Pointer to data to send
7212 const char* msg,
7213 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
7214 const int flags,
7215 /// Target endpoint address structure
7216 const struct sockaddr *to,
7217 /// Size of target endpoint structure
7218 socklen_t tolen = sizeof(sockaddr)) {
7219 if (__debug) debug("sendzto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7220 + ", <msg>"
7221 + ", " + std::to_string(std::strlen(msg))
7222 + ", " + std::to_string(flags)
7223 + ", <sockaddr>"
7224 + ", " + std::to_string(tolen)
7225 + ");");
7226 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, std::strlen(msg), flags, to, tolen)));
7227 return this;
7228 }; // -x- rsocket* sendzto -x-
7229
7230 /*======================================================================*//**
7231 @brief
7232 Set socket option to the specific integer.
7233
7234 @par Notes
7235 These setsockopt() methods take an integer or character value directly, or a
7236 pointer to a structure, and then rsocket handles the remaining tedious
7237 technical details behind-the-scenes for you when calling the underlying
7238 socket's setsockopt() function.
7239
7240 @throws randolf::rex::xEBADF The underlying socket is not open
7241 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7242 part of the user address space
7243 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7244 valid for this socket's family (a.k.a., communication domain)
7245 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7246 is not supported
7247 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7248 doesn't refer to a socket
7249
7250 @returns The same rsocket object so as to facilitate stacking
7251 @qualifier POSIX
7252 @qualifier TLS
7253 *///=========================================================================
7254 rsocket* setsockopt(
7255 /// The level at which the option resides; typically @c SOL_SOCKET
7256 const int level,
7257 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7258 const int option,
7259 /// The value that this socket option will be set to
7260 const int value) {
7261 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
7262 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7263 return this;
7264 }; // -x- rsocket* setsockopt -x-
7265
7266 /*======================================================================*//**
7267 @brief
7268 Set socket option to the specific unsigned integer.
7269 @copydetails setsockopt(const int, const int, const int)
7270
7271 @pre
7272 For any values that require a u_int, you'll need to explicitly cast this type
7273 when specifying the value directly; for example: (u_int)32768
7274
7275 @throws randolf::rex::xEBADF The underlying socket is not open
7276 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7277 part of the user address space
7278 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7279 valid for this socket's family (a.k.a., communication domain)
7280 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7281 is not supported
7282 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7283 doesn't refer to a socket
7284
7285 @returns The same rsocket object so as to facilitate stacking
7286 @qualifier TLS
7287 *///=========================================================================
7288 rsocket* setsockopt(
7289 /// The level at which the option resides; typically @c SOL_SOCKET
7290 const int level,
7291 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7292 const int option,
7293 /// The value that this socket option will be set to
7294 const u_int value) {
7295 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
7296 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7297 return this;
7298 }; // -x- rsocket* setsockopt -x-
7299
7300 /*======================================================================*//**
7301 @brief
7302 Set socket option to the specific unsigned character.
7303 @copydetails setsockopt(const int, const int, const int)
7304 @qualifier TLS
7305 *///=========================================================================
7306 rsocket* setsockopt(
7307 /// The level at which the option resides; typically @c SOL_SOCKET
7308 const int level,
7309 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7310 const int option,
7311 /// The value that this socket option will be set to
7312 const u_char value) {
7313 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
7314 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7315 return this;
7316 }; // -x- rsocket* setsockopt -x-
7317
7318 /*======================================================================*//**
7319 @brief
7320 Set socket option to the specific structure.
7321 @copydetails setsockopt(const int, const int, const int)
7322 @qualifier TLS
7323 *///=========================================================================
7324 rsocket* setsockopt(
7325 /// The level at which the option resides; typically @c SOL_SOCKET
7326 const int level,
7327 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7328 const int option,
7329 /// The structure that this socket option will be set to
7330 const linger& value) {
7331 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7332 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7333 return this;
7334 }; // -x- rsocket* setsockopt -x-
7335
7336 /*======================================================================*//**
7337 @brief
7338 Set socket option to the specific structure.
7339 @copydetails setsockopt(const int, const int, const linger&)
7340 @qualifier TLS
7341 *///=========================================================================
7342 rsocket* setsockopt(
7343 /// The level at which the option resides; typically @c SOL_SOCKET
7344 const int level,
7345 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7346 const int option,
7347 /// The structure that this socket option will be set to
7348 const timeval& value) {
7349 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7350 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7351 return this;
7352 }; // -x- rsocket* setsockopt -x-
7353
7354 /*======================================================================*//**
7355 @brief
7356 Set socket option to the specific structure.
7357 @copydetails setsockopt(const int, const int, const linger&)
7358 @qualifier TLS
7359 *///=========================================================================
7360 rsocket* setsockopt(
7361 /// The level at which the option resides; typically @c SOL_SOCKET
7362 const int level,
7363 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7364 const int option,
7365 /// The structure that this socket option will be set to
7366 const in_addr& value) {
7367 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7368 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7369 return this;
7370 }; // -x- rsocket* setsockopt -x-
7371
7372 /*======================================================================*//**
7373 @brief
7374 Set socket option to the specific structure.
7375 @copydetails setsockopt(const int, const int, const linger&)
7376 @qualifier TLS
7377 *///=========================================================================
7378 rsocket* setsockopt(
7379 /// The level at which the option resides; typically @c SOL_SOCKET
7380 const int level,
7381 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7382 const int option,
7383 /// The structure that this socket option will be set to
7384 const ip_mreq& value) {
7385 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7386 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7387 return this;
7388 }; // -x- rsocket* setsockopt -x-
7389
7390 /*======================================================================*//**
7391 @brief
7392 Set socket option to the specific structure.
7393 @copydetails setsockopt(const int, const int, const linger&)
7394 @qualifier TLS
7395 *///=========================================================================
7396 rsocket* setsockopt(
7397 /// The level at which the option resides; typically @c SOL_SOCKET
7398 const int level,
7399 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7400 const int option,
7401 /// The structure that this socket option will be set to
7402 const ip_mreq_source& value) {
7403 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7404 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7405 return this;
7406 }; // -x- rsocket* setsockopt -x-
7407
7408 /*======================================================================*//**
7409 @brief
7410 Set socket option to the specific structure.
7411 @copydetails setsockopt(const int, const int, const linger&)
7412 @qualifier TLS
7413 *///=========================================================================
7414 rsocket* setsockopt(
7415 /// The level at which the option resides; typically @c SOL_SOCKET
7416 const int level,
7417 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7418 const int option,
7419 /// The structure that this socket option will be set to
7420 const icmp6_filter& value) {
7421 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7422 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7423 return this;
7424 }; // -x- rsocket* setsockopt -x-
7425
7426 /*======================================================================*//**
7427 @brief
7428 Set socket option to the specific structure.
7429 @copydetails setsockopt(const int, const int, const linger&)
7430 @qualifier TLS
7431 *///=========================================================================
7432 rsocket* setsockopt(
7433 /// The level at which the option resides; typically @c SOL_SOCKET
7434 const int level,
7435 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7436 const int option,
7437 /// The structure that this socket option will be set to
7438 const sockaddr_in6& value) {
7439 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7440 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7441 return this;
7442 }; // -x- rsocket* setsockopt -x-
7443
7444 /*======================================================================*//**
7445 @brief
7446 Set socket option to the specific structure.
7447 @copydetails setsockopt(const int, const int, const linger&)
7448 @qualifier TLS
7449 *///=========================================================================
7450 rsocket* setsockopt(
7451 /// The level at which the option resides; typically @c SOL_SOCKET
7452 const int level,
7453 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7454 const int option,
7455 /// The structure that this socket option will be set to
7456 const ip6_mtuinfo& value) {
7457 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7458 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7459 return this;
7460 }; // -x- rsocket* setsockopt -x-
7461
7462 /*======================================================================*//**
7463 @brief
7464 Set socket option to the specific structure.
7465 @copydetails setsockopt(const int, const int, const linger&)
7466 @qualifier TLS
7467 *///=========================================================================
7468 rsocket* setsockopt(
7469 /// The level at which the option resides; typically @c SOL_SOCKET
7470 const int level,
7471 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7472 const int option,
7473 /// The structure that this socket option will be set to
7474 const ipv6_mreq& value) {
7475 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7476 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7477 return this;
7478 }; // -x- rsocket* setsockopt -x-
7479
7480 /*======================================================================*//**
7481 @brief
7482 Set socket option to the specific structure.
7483 @copydetails setsockopt(const int, const int, const linger&)
7484 @qualifier TLS
7485 *///=========================================================================
7486 rsocket* setsockopt(
7487 /// The level at which the option resides; typically @c SOL_SOCKET
7488 const int level,
7489 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7490 const int option,
7491 /// The structure that this socket option will be set to
7492 const group_req& value) {
7493 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7494 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7495 return this;
7496 }; // -x- rsocket* setsockopt -x-
7497
7498 /*======================================================================*//**
7499 @brief
7500 Set socket option to the specific structure.
7501 @copydetails setsockopt(const int, const int, const linger&)
7502 @qualifier TLS
7503 *///=========================================================================
7504 rsocket* setsockopt(
7505 /// The level at which the option resides; typically @c SOL_SOCKET
7506 const int level,
7507 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7508 const int option,
7509 /// The structure that this socket option will be set to
7510 const group_source_req& value) {
7511 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7512 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7513 return this;
7514 }; // -x- rsocket* setsockopt -x-
7515
7516 /*======================================================================*//**
7517 @brief
7518 Shut down the underlying socket, partially or fully.
7519
7520 <div style=padding-left:32px;>
7521 <table>
7522 <tr>
7523 <td valign=top>SHUT_RD:</td>
7524 <td>Further receives will be disallowed.</td>
7525 </tr>
7526 <tr>
7527 <td valign=top>SHUT_WR:</td>
7528 <td>Further sends will be disallowed (this may cause actions specific
7529 to the protocol family of the socket to occur).</td>
7530 </tr>
7531 <tr>
7532 <td valign=top>SHUT_RDWR:</td>
7533 <td>Further sends and receives will be disallowed (default).</td>
7534 </tr>
7535 </table>
7536 </div>
7537
7538 @throws randolf::rex::xEBADF The underlying socket is not open
7539 @throws randolf::rex::xEINVAL Invalid argument passed
7540 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7541 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7542 doesn't refer to a socket
7543
7544 @returns The same rsocket object so as to facilitate stacking
7545 @qualifier POSIX
7546 @qualifier TLS
7547 *///=========================================================================
7548 rsocket* shutdown(
7549 /// SHUT_RD@n
7550 /// SHUT_RW@n
7551 /// SHUT_RDWR (default)
7552 const int how = SHUT_RDWR) {
7553 if (__debug) debug("shutdown(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7554 + ", " + std::to_string(how)
7555 + ");");
7556 if (__tls) SSL_shutdown(__tls_fd); // We have to shut down TLS connections first, if they're active
7557 __rc_check(::shutdown(__socket_fd, how));
7558 //__socket_connected = false; // TODO: Figure out when to change this to false
7559 return this;
7560 }; // -x- rsocket* shutdown -x-
7561
7562 /*======================================================================*//**
7563 @brief
7564 Complete the configuration of an rsocket that was previously initialized
7565 without any parameters (a.k.a., an "empty rsocket").
7566 @copydetails rsocket()
7567 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
7568 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
7569 @throws randolf::rex::xEALREADY If this socket() method was already used, or
7570 it was used after rsocket() initialized with at least one parameter
7571 @throws randolf::rex::xEINVAL Protocal family invalid or not available
7572 @throws randolf::rex::xEINVAL Invalid flags in type
7573 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
7574 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
7575 @throws randolf::rex::xENOBUFS Insufficient memory
7576 @throws randolf::rex::xENOMEM Insufficient memory
7577 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
7578 supported within the specified family (a.k.a., communication domain)
7579 @returns The same rsocket object so as to facilitate stacking
7580 @see rsocket()
7581 @see socket_family()
7582 @see socket_fd()
7583 @see socket_protocol()
7584 @see socket_type()
7585 @qualifier POSIX
7586 @qualifier TLS
7587 *///=========================================================================
7588 rsocket* socket(
7589 /// Communication domain; usually one of:@n
7590 /// AF_INET (IPv4)@n
7591 /// AF_INET6 (IPv6)@n
7592 /// AF_UNIX (UNIX domain sockets)
7593 const int family,
7594 /// Communication semantics; usually one of:@n
7595 /// SOCK_STREAM (common for TCP)@n
7596 /// SOCK_DGRAM (common for UDP)
7597 const int type = SOCK_STREAM,
7598 /// Network protocol; usually one of:@n
7599 /// IPPROTO_TCP@n
7600 /// IPPROTO_UDP@n
7601 /// IPPROTO_IP@n
7602 /// PF_UNSPEC (auto-detect)
7603 const int protocol = PF_UNSPEC) {
7604 __socket(family, type, protocol);
7605 return this;
7606 }; // -x- rsocket* socket -x-
7607
7608 /*======================================================================*//**
7609 @brief
7610 Get underlying socket family/domain constant (SO_DOMAIN).
7611 @returns socket family/domain constant
7612 @see port()
7613 @see socket()
7614 @see socket_fd()
7615 @see socket_protocol()
7616 @see socket_type()
7617 @qualifier TLS
7618 *///=========================================================================
7619 const int socket_family() noexcept { return __socket_addr.ss_family; }; // -x- int socket_family -x-
7620
7621 /*======================================================================*//**
7622 @brief
7623 Get underlying socket descriptor/handle.
7624 @returns socket descriptor/handle
7625 @returns 0 = socket not yet allocated
7626 @see port()
7627 @see socket()
7628 @see socket_family()
7629 @see socket_protocol()
7630 @see socket_type()
7631 @qualifier TLS
7632 *///=========================================================================
7633 const int socket_fd() noexcept { return __socket_fd; }; // -x- int socket_fd -x-
7634
7635 /*======================================================================*//**
7636 @brief
7637 Set underlying socket descriptor/handle (to one that is presumed to be open).
7638 @note
7639 This method is only available while an underlying socket has not been created
7640 or previously assigned, such as after an empty @ref rsocket instantiation.
7641 @throws randolf::rex::xEALREADY If this socket_fd() method was already used,
7642 or it was used after socket() initialized it, or if rsocket() had
7643 initialized with at least one parameter that resulted in the creation
7644 of an underlying socket
7645 @returns The same rsocket object so as to facilitate stacking
7646 @see socket()
7647 @see socket_family()
7648 @see socket_protocol()
7649 @see socket_type()
7650 @qualifier TLS
7651 *///=========================================================================
7652 rsocket* socket_fd(
7653 /// New socket descriptor/handle
7654 const int new_socket_fd) {
7655 if (__debug) debug("socket_fd(socket{0x" + randolf::rtools::to_hex(new_socket_fd) + "}"
7656 + ");");
7657 if (__socket_fd != 0) throw randolf::rex::xEALREADY("EALREADY");
7658 __socket_fd = new_socket_fd;
7659 __socket_addr.ss_family = getsockopt_int(SOL_SOCKET, SO_DOMAIN);
7660 __socket_type = getsockopt_int(SOL_SOCKET, SO_TYPE);
7661 __socket_protocol = getsockopt_int(SOL_SOCKET, SO_PROTOCOL);
7662 __socket_open = true;
7663 return this;
7664 }; // -x- rsocket* socket_fd -x-
7665
7666 /*======================================================================*//**
7667 @brief
7668 Get underlying socket protocol constant (SO_PROTOCOL).
7669 @returns socket protocol constant
7670 @see port()
7671 @see socket()
7672 @see socket_family()
7673 @see socket_fd()
7674 @see socket_type()
7675 @qualifier TLS
7676 *///=========================================================================
7677 const int socket_protocol() noexcept { return __socket_protocol; }; // -x- int socket_protocol -x-
7678
7679 /*======================================================================*//**
7680 @brief
7681 Get underlying socket type constant (SO_TYPE).
7682 @returns socket type constant
7683 @see port()
7684 @see socket()
7685 @see socket_family()
7686 @see socket_fd()
7687 @see socket_protocol()
7688 @qualifier TLS
7689 *///=========================================================================
7690 const int socket_type() noexcept { return __socket_type; }; // -x- int socket_type -x-
7691
7692 /*======================================================================*//**
7693 @brief
7694 Find out whether the underlying socket is at the out-of-band (OOB) mark.
7695
7696 @throws randolf::rex::xEBADF The underlying socket is not open
7697 @throws randolf::rex::xEINVAL The underlying socket file descriptor is not a
7698 type to which @ref sockatmark() can be applied
7699
7700 @returns TRUE = at OOB mark
7701 @returns FALSE = not at OOB mark
7702 @qualifier POSIX
7703 @qualifier TLS
7704 *///=========================================================================
7705 const bool sockatmark() {
7706 return __rc_check(::sockatmark(__socket_fd)) ? true : false;
7707 }; // -x- bool sockatmark -x-
7708
7709 /*======================================================================*//**
7710 @brief
7711 Find out what the read timeout is set to on the current socket.
7712
7713 Since getting the read timeout is such a common operation, this specialized
7714 method was created to ease software development efforts; internally we're
7715 just calling getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO).
7716
7717 @throws randolf::rex::xEBADF The underlying socket is not open
7718 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7719 part of the user address space
7720 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7721 valid for this socket's family (a.k.a., communication domain)
7722 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7723 is not supported
7724 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7725 doesn't refer to a socket
7726
7727 @returns @c timeval socket option structure wrapped in std::shared_ptr
7728 @qualifier TLS
7729 *///=========================================================================
7730 std::shared_ptr<timeval> timeout() {
7731 return getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO);
7732 }; // -x- std::shared_ptr<timeval> timeout -x-
7733
7734 /*======================================================================*//**
7735 @brief
7736 Set the recv timeout on the current socket.
7737
7738 Since setting the read timeout is such a common operation, this specialized
7739 method was created to ease software development efforts; internally we're
7740 just calling setsockopt(SOL_SOCKET, SO_RCVTIMEO, timeval).
7741 @attention
7742 Although a timeout of 100,000 microseconds (1/10 of one second) may suffice
7743 in healthy and efficient networks, a more conservative setting of 1 second
7744 tends to minimally yield more reliable results. Many end-user applications
7745 use defaults of 3 to 5 seconds to cover a wider variety of unpredictable
7746 network connections (such as over shared wireless connections that are slow),
7747 and this setting should ultimately be configurable by users/administrators.
7748
7749 @note
7750 The default timeout for new sockets is normally 0 (no timeout).
7751
7752 @throws randolf::rex::xEBADF The underlying socket is not open
7753 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7754 part of the user address space
7755 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7756 valid for this socket's family (a.k.a., communication domain)
7757 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7758 is not supported
7759 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7760 doesn't refer to a socket
7761
7762 @returns The same rsocket object so as to facilitate stacking
7763 @qualifier TLS
7764 *///=========================================================================
7765 rsocket* timeout(
7766 /// timeval structure
7767 const struct timeval tv) {
7768 return setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
7769 }; // -x- rsocket* timeout -x-
7770
7771 /*======================================================================*//**
7772 @copydoc timeout(struct timeval)
7773 @qualifier TLS
7774 *///=========================================================================
7775 rsocket* timeout(
7776 /// Timeout in seconds
7777 const int tv_sec = 0,
7778 /// Timeout in microseconds
7779 const long tv_usec = 0) {
7780 struct timeval tv{tv_sec, tv_usec};
7781 return setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
7782 }; // -x- rsocket* timeout -x-
7783
7784 /*======================================================================*//**
7785 @brief
7786 Find out what the read timeout is set to when using the @ref recvline()
7787 method.
7788
7789 @returns @c long value (0 = no timeout)
7790 @see recvline
7791 @see timeout
7792 @see timeout_recvline(long)
7793 @qualifier TLS
7794 *///=========================================================================
7795 long timeout_recvline() {
7796 return __recvline_timeout;
7797 }; // -x- long timeout_recvline -x-
7798
7799 /*======================================================================*//**
7800 @brief
7801 Set the read timeout for the @ref recvline() method (the @ref recvline()
7802 method's @c timeout parameter can override this setting).
7803
7804 @note
7805 The default timeout for this recvline_timeout setting is 0 (no timeout).
7806
7807 @throws randolf::rex::xERANGE if the timeout parameter is below 0
7808
7809 @returns The same rsocket object so as to facilitate stacking
7810 @see recvline
7811 @see timeout
7812 @see timeout_recvline
7813 @qualifier TLS
7814 *///=========================================================================
7815 rsocket* timeout_recvline(
7816 /// timeval structure
7817 const long timeout) {
7818 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
7819 __recvline_timeout = timeout;
7820 return this;
7821 }; // -x- rsocket* timeout_recvline -x-
7822
7823 /*======================================================================*//**
7824 @brief
7825 Enable or disable encrypted communications (from the OpenSSL library).
7826
7827 @warning
7828 TLS cannot be enabled before a non-encrypted rsocket is open (an rsocket is
7829 typically opened with the @ref socket() method, the @ref connect() method, or
7830 one of the @ref accept() methods). If calling @c tls(true) on an rsocket
7831 that isn't open, an exception will be thrown.
7832
7833 If needed, a new TLS context will be instantiated and TLS will be initialized
7834 (if this hasn't already been done). TLS instantiation can be done first by
7835 calling the @ref tls_ctx() method (regardless of whether encryption is being
7836 enabled or disabled). If the default @ref TLS_FLAGS aren't sufficient for
7837 the needs of your application, then the @ref tls_ctx() method facilitates
7838 this regardless of wehther rsocket is open.
7839
7840 @note
7841 The reason a TLS context is instantiated and TLS is initialized even when the
7842 status is being set to FALSE is to facilitate TLS ingress from an unencrypted
7843 connection later in the session (see the @ref TLS_NO_INGRESS flag for more
7844 information).
7845
7846 @post
7847 If a client attempts to upgrade a TLS connection to TLS (e.g., by using a
7848 command such as @c STARTTLS, which is commonly transmitted in unencrypted
7849 form, like @c telnet-ssl does -- using `telnet-ssl -z ssl` prevents this
7850 condition), the following error that's difficult to track down may be
7851 triggered when calling any of the @c recv methods (I hope that including this
7852 information here in this documentation will be helpful):
7853 @verbatim
7854 SSL_ERROR_UNKNOWN: error:0A00010B:SSL routines::wrong version number
7855 @endverbatim
7856 This is most likely not a programming error, but rather a problem with how
7857 users may attempt to mis-use a connection based on a misunderstanding of the
7858 communications requirements (e.g., connecting unencrypted and attempting to
7859 upgrade to TLS over a connection that's expecting TLS encrypted data from the
7860 very beginning, without involving any ingress).
7861
7862 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7863 OpenSSL library doesn't document which errors may be returned)
7864
7865 @returns The same rsocket object so as to facilitate stacking
7866 @see is_tls
7867 @see tls_ctx
7868 @qualifier TLS
7869 *///=========================================================================
7870 rsocket* tls(
7871 /// TRUE = Enable encrypted communications@n FALSE = Disable encrypted communications
7872 const bool status = true,
7873 /// Configuration parameters
7874 const int flags = TLS_FLAGS::TLS_DEFAULT) { // | TLS_FLAGS::TLS_CLIENT
7875 if (__debug) debug("tls(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7876 + ", " + (status ? "true" : "false")
7877 + ");");
7878
7879 // --------------------------------------------------------------------------
7880 // Create default context (with "flags" passthrough), unless it was already
7881 // created (usually by one of the tls_ctx() methods).
7882 // --------------------------------------------------------------------------
7883 if (__tls_ctx == nullptr) tls_ctx((SSL_CTX*)nullptr, flags);
7884
7885 // --------------------------------------------------------------------------
7886 // Make sure tls_fd (ssl_fd) is allocated. If not, then it needs to be
7887 // allocated and configured.
7888 // --------------------------------------------------------------------------
7889 if (status == true && __tls_fd == nullptr) {
7890 __tls_fd = SSL_new(__tls_ctx);
7891 if (__debug) debug("SSL_set_fd(<tls_fd>, socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7892 + ");");
7893
7894 // --------------------------------------------------------------------------
7895 // Associate OpenSSL file descriptor with underlying socket file descriptor.
7896 // --------------------------------------------------------------------------
7897 __rc_check_tls(SSL_set_fd(__tls_fd, __socket_fd));
7898
7899 // --------------------------------------------------------------------------
7900 // Enable read-ahead so that SSL_peek will work properly.
7901 // --------------------------------------------------------------------------
7902 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
7903 SSL_set_read_ahead(__tls_fd, 1); // 0=disable / 1=enable
7904
7905// SSL_CTX_set_max_pipelines(__tls_ctx, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
7906// SSL_set_max_pipelines(__tls_fd, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
7907
7908// SSL_CTX_set_mode(__tls_ctx, SSL_MODE_AUTO_RETRY);
7909// SSL_set_mode(__tls_fd, SSL_MODE_AUTO_RETRY);
7910
7911 // --------------------------------------------------------------------------
7912 // We're probably not going to use BIO because we don't need it, and it adds
7913 // unnecessary overhead for what we're doing. Also, BIO doesn't provide an
7914 // alternative to the SSL_peek function, and also doesn't resolve the need
7915 // for the MSG_WAITALL flag that the ::recv() function supports.
7916 //
7917 // TODO: Remove this completely, unless is can solve the problem of reading
7918 // all incoming data (needed for readline, primarily)
7919 // --------------------------------------------------------------------------
7920 __tls_rbio = SSL_get_rbio(__tls_fd);
7921 __tls_wbio = SSL_get_wbio(__tls_fd);
7922
7923 } // -x- if !__tls_fd -x-
7924 __tls = status;
7925 return this;
7926 }; // -x- rsocket* tls -x-
7927
7928 /*======================================================================*//**
7929 @brief
7930 Return the current TLS context (multiple TLS contexts are supported, although
7931 typically needed to support SNI with inbound connections).
7932 @returns Pointer to OpenSSL's context (normally labelled @c SSL_CTX* in the
7933 documentation for OpenSSL), or nullptr if this context was never
7934 assigned to (or created by) this rsocket
7935 @see tls_ctx
7936 @qualifier TLS
7937 *///=========================================================================
7938 SSL_CTX* tls_ctx() noexcept {
7939 return __tls_ctx;
7940 }; // -x- SSL_CTX* tls_ctx -x-
7941
7942 /*======================================================================*//**
7943 @brief
7944 Copy the source rsocket's TLS context map and add it to this rsocket's
7945 collection; or, if the source doesn't have any TLS contexts and this rsocket
7946 doesn't have any TLS contexts in its collection, then initialize TLS and
7947 instantiate a new TLS context. In either scenario, the source rsocket will
7948 be treated as a template as all TLS flags duplicated to enable encrypted
7949 socket I/O for use in this rsocket().
7950
7951 @note
7952 At least one TLS context is needed to enable encrypted socket I/O for use in
7953 this rsocket().
7954
7955 @post
7956 Encrypted socket I/O is only possible after a TLS context has been
7957 initialized (this is not a global setting as it has per-rsocket specificity).
7958
7959 @note
7960 The only @ref TLS_FLAGS flag that doesn't get transferred is @ref TLS_SERVER
7961 when no flags are specified. Specifying any flag(s) will cause this method
7962 to ignore the source rsocket's TLS flags so as to defer to this override.
7963
7964 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7965 OpenSSL library doesn't document which errors may be returned)
7966
7967 @returns The same rsocket object so as to facilitate stacking
7968 @see tls_sni
7969 @qualifier TLS
7970 *///=========================================================================
7971 rsocket* tls_ctx(
7972 /// OpenSSL's TLS context to use (if not provided, a new context will be
7973 /// created automatically using OpenSSL's defaults)
7974 rsocket* rtemplate,
7975 /// Configuration parameters
7976 const int flags = TLS_FLAGS::TLS_DEFAULT) {
7977 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7978 + ", <rsocket>"
7979 + ", " + std::to_string(flags)
7980 + ");");
7981
7982 // --------------------------------------------------------------------------
7983 // TLS-related variables (OpenSSL).
7984 //
7985 // Note: The __tls_fd variable cannot be allocated until after __socket_fd
7986 // has been, hence the "post" note in the documentation.
7987 // --------------------------------------------------------------------------
7988 __tls = (bool)rtemplate->__tls; // Preserve TLS mode setting
7989 if (rtemplate->__tls_ctx != nullptr) { // If TLS context is defined, then do these important things:
7990 __tls_ctx = rtemplate->__tls_ctx; // 1. copy the pointer to SSL_CTX
7991 SSL_CTX_up_ref(__tls_ctx); // 2. increment SSL_CTX's internal reference count
7992 } // -x- if __tlx_ctx -x-
7993
7994 // --------------------------------------------------------------------------
7995 // Copy or override TLS flags.
7996 // --------------------------------------------------------------------------
7997 if (flags == TLS_FLAGS::TLS_DEFAULT) {
7998 __tls_exclusive = rtemplate->__tls_exclusive; // TLS policy
7999 __tls_egress = rtemplate->__tls_egress; // TLS policy
8000 __tls_ingress = rtemplate->__tls_ingress; // TLS policy
8001 } else { // Save flags
8002 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
8003 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
8004 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
8005 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
8006 } // -x- if flags -x-
8007
8008 return this;
8009 }; // -x- rsocket* tls_ctx -x-
8010
8011 /*======================================================================*//**
8012 @brief
8013 Initialize TLS and instantiate a TLS context, and add it to this rsocket's
8014 current collection of TLS contexts, and set it as the currently active TLS
8015 context (so that a certificate chain and private key may be added to it).
8016 @note
8017 At least one TLS context is needed to enable encrypted socket I/O for use in
8018 this rsocket().
8019 @post
8020 Encrypted socket I/O is only possible after a TLS context has been
8021 initialized (this is not a global setting as it has per-rsocket specificity).
8022 @note
8023 This is the default TLS context for this @c rsocket, which will also be used
8024 for non-SNI handshakes.
8025
8026 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8027 OpenSSL library doesn't document which errors may be returned)
8028
8029 @returns The same rsocket object so as to facilitate stacking
8030 @see tls_sni
8031 @qualifier TLS
8032 *///=========================================================================
8033 rsocket* tls_ctx(
8034 /// OpenSSL's TLS context to use (if not provided, a new context will be
8035 /// created using OpenSSL's defaults)
8036 SSL_CTX* ctx,
8037 /// Configuration parameters
8038 const int flags = TLS_FLAGS::TLS_DEFAULT) {
8039 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8040 + ", <ctx>"
8041 + ", " + std::to_string(flags)
8042 + ");");
8043
8044 // --------------------------------------------------------------------------
8045 // Ignore repeated calls to this method.
8046 // --------------------------------------------------------------------------
8047 if (__tls_ctx != nullptr) return this;
8048
8049 // --------------------------------------------------------------------------
8050 // Fire up OpenSSL's algorithms and pre-load its error strings.
8051 //
8052 // These two functions have been deprecated since OpenSSL v1.1.0, so we don't
8053 // need to call them. If someone needs them, then they can always call them
8054 // in their own code anyway.
8055 // --------------------------------------------------------------------------
8056 //OpenSSL_add_all_algorithms(); // Load and register cryptography algorithms
8057 //SSL_load_error_strings(); // Load all error messages into memory
8058
8059 // --------------------------------------------------------------------------
8060 // Save (ctx != nullptr) or create (ctx == nullptr) OpenSSL context.
8061 // --------------------------------------------------------------------------
8062 __tls_ctx = ctx == nullptr ? SSL_CTX_new(flags & TLS_FLAGS::TLS_SERVER ? TLS_server_method() : TLS_client_method()) // Create anew (default)
8063 : ctx; // Use OpenSSL context that was provided
8064 if (__tls_ctx == nullptr) randolf::rex::mk_exception("Cannot create TLS context", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8065
8066 // --------------------------------------------------------------------------
8067 // Enable read-ahead so that SSL_peek will work properly.
8068 // --------------------------------------------------------------------------
8069 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
8070
8071 // --------------------------------------------------------------------------
8072 // Save flags.
8073 // --------------------------------------------------------------------------
8074 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
8075 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
8076 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
8077 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
8078
8079 return this;
8080 }; // -x- rsocket* tls_ctx -x-
8081
8082 /*======================================================================*//**
8083 @brief
8084 Check the private key it to ensure it's consistent with the corresponding TLS
8085 certificate chain.
8086
8087 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8088 OpenSSL library doesn't document which errors may be returned)
8089
8090 @returns The same rsocket object so as to facilitate stacking
8091 @see tls_ctx_use_privatekey_file
8092 @see tls_ctx_use_privatekey_pem
8093 @qualifier TLS
8094 *///=========================================================================
8095 rsocket* tls_ctx_check_privatekey() {
8096 if (__debug) debug("tls_ctx_check_privatekey();");
8097 if (!SSL_CTX_check_private_key(__tls_ctx)) randolf::rex::mk_exception("Cannot validate consistency between certificate chain and private key", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8098 return this;
8099 }; // -x- rsocket* tls_ctx_check_privatekey -x-
8100
8101 /*======================================================================*//**
8102 @brief
8103 Load a TLS certificate chain and private key in PEM format from text files
8104 and use them in the TLS context.
8105
8106 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8107 OpenSSL library doesn't document which errors may be returned)
8108
8109 @returns The same rsocket object so as to facilitate stacking
8110 @see tls_ctx_use_certificate_chain_and_privatekey_pems
8111 @see tls_ctx_use_certificate_chain_file
8112 @see tls_ctx_use_certificate_chain_pem
8113 @see tls_ctx_use_privatekey_file
8114 @see tls_ctx_use_privatekey_pem
8115 @see tls_ctx_check_privatekey
8116 @qualifier TLS
8117 *///=========================================================================
8118 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
8119 /// Pointer to ASCIIZ path and filename to certificate chain file (@c nullptr
8120 /// will simply be ignored)
8121 const char* chain_file,
8122 /// Pointer to ASCIIZ path and filename to private key file (@c nullptr will
8123 /// simply be ignored)
8124 const char* key_file) {
8125 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
8126 + std::string(chain_file)
8127 + ", " + std::string( key_file)
8128 + ");");
8129 if (chain_file != nullptr) tls_ctx_use_certificate_chain_file(chain_file);
8130 if ( key_file != nullptr) tls_ctx_use_privatekey_file( key_file);
8131 return this;
8132 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
8133
8134 /*======================================================================*//**
8135 @brief
8136 Load a TLS certificate chain and private key in PEM format from text files
8137 and use them in the TLS context.
8138
8139 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8140 OpenSSL library doesn't document which errors may be returned)
8141
8142 @returns The same rsocket object so as to facilitate stacking
8143 @see tls_ctx_use_certificate_chain_and_privatekey_pems
8144 @see tls_ctx_use_certificate_chain_file
8145 @see tls_ctx_use_certificate_chain_pem
8146 @see tls_ctx_use_privatekey_file
8147 @see tls_ctx_use_privatekey_pem
8148 @see tls_ctx_check_privatekey
8149 @qualifier TLS
8150 *///=========================================================================
8151 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
8152 /// Pointer to ASCIIZ path and filename to certificate chain file (an empty
8153 /// string will simply be ignored)
8154 const std::string chain_file,
8155 /// Pointer to ASCIIZ path and filename to private key file (an empty string
8156 /// will simply be ignored)
8157 const std::string key_file) {
8158 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
8159 + chain_file
8160 + ", " + key_file
8161 + ");");
8162 if (!chain_file.empty()) tls_ctx_use_certificate_chain_file(chain_file);
8163 if ( !key_file.empty()) tls_ctx_use_privatekey_file( key_file);
8164 return this;
8165 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
8166
8167 /*======================================================================*//**
8168 @brief
8169 Load a TLS certificate chain and a TLS private key in PEM format from memory
8170 and use them in the TLS context.
8171
8172 Although this functionality doesn't exist in OpenSSL (at the time of writing
8173 this method), it's provided here in a manner that has exactly the same effect
8174 as the @ref tls_ctx_use_certificate_chain_and_privatekey_files() methods, but
8175 without needing the PEM-formatted certificate chain stored in files
8176 beforehand.
8177
8178 @note
8179 The @c cert_pem_data and key_pem_data parameters are pointers to the memory
8180 locations that holds the PEM formatted certificate chain data and private key
8181 data, respectively. If the corresponding lengths of each of these data aren't
8182 specified or are set to zero (default), then they will be treated as multiline
8183 ASCIIZ strings.
8184
8185 Behind the scenes, we're just writing the cert_pem_data and key_pem_data
8186 memory to temporary files with severely-limited permissions (), then
8187 optionally overwriting those temporary files with random data prior to
8188 deleting them (this is the default, since better security practices should be
8189 the default, but on a secured system it may not be necessary and so this
8190 option can also be disabled to save CPU cycles and reduce overall disk-write
8191 I/O operations).
8192
8193 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8194 OpenSSL library doesn't document which errors may be returned)
8195
8196 @returns The same rsocket object so as to facilitate stacking
8197 @see tls_ctx_use_certificate_chain_and_privatekey_files
8198 @see tls_ctx_use_certificate_chain_file
8199 @see tls_ctx_use_certificate_chain_pem
8200 @see tls_ctx_use_privatekey_file
8201 @see tls_ctx_use_privatekey_pem
8202 @see tls_ctx_check_privatekey
8203 @qualifier TLS
8204 *///=========================================================================
8205 rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems(
8206 /// Pointer to certificate chain data in PEM format
8207 const char* cert_pem_data,
8208 /// Pointer to private key data in PEM format
8209 const char* key_pem_data,
8210 /// Length of cert_pem_data (in bytes), or 0 to auto-detect length if cert_pem_data is an ASCIIZ string
8211 size_t cert_len = 0,
8212 /// Length of key_pem_data (in bytes), or 0 to auto-detect length if key_pem_data is an ASCIIZ string
8213 size_t key_len = 0,
8214 /// Whether to overwrite the temporary files with random data before deleting them
8215 const bool random_fill = true) {
8216 tls_ctx_use_certificate_chain_pem(cert_pem_data, cert_len, random_fill);
8217 tls_ctx_use_privatekey_pem( key_pem_data, key_len, random_fill);
8218 return this;
8219 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems -x-
8220
8221 /*======================================================================*//**
8222 @brief
8223 Load a TLS certificate chain in PEM format from a text file and use it in the
8224 TLS context.
8225
8226 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8227 OpenSSL library doesn't document which errors may be returned)
8228
8229 @returns The same rsocket object so as to facilitate stacking
8230 @see tls_ctx_use_certificate_chain_file
8231 @see tls_ctx_use_certificate_chain_pem
8232 @see tls_ctx_check_privatekey
8233 @qualifier TLS
8234 *///=========================================================================
8235 rsocket* tls_ctx_use_certificate_chain_file(
8236 /// Pointer to ASCIIZ path and filename to certificate chain file
8237 const char* file) {
8238 if (__debug) debug("tls_ctx_use_certificate_chain_file("
8239 + std::string(file)
8240 + ");");
8241 if (SSL_CTX_use_certificate_chain_file(__tls_ctx, file) != 1) randolf::rex::mk_exception(std::string("Cannot use certificate chain file ") + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8242 return this;
8243 }; // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
8244
8245 /*======================================================================*//**
8246 @brief
8247 Load a TLS certificate chain in PEM format from a text file and use it in the
8248 TLS context.
8249
8250 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8251 OpenSSL library doesn't document which errors may be returned)
8252
8253 @returns The same rsocket object so as to facilitate stacking
8254 @see tls_ctx_use_certificate_chain_file
8255 @see tls_ctx_use_certificate_chain_pem
8256 @see tls_ctx_check_privatekey
8257 @qualifier TLS
8258 *///=========================================================================
8259 rsocket* tls_ctx_use_certificate_chain_file(
8260 /// Path and filename to certificate chain file
8261 const std::string file) {
8262 if (__debug) debug("tls_ctx_use_certificate_chain_file("
8263 + file
8264 + ");");
8265 if (SSL_CTX_use_certificate_chain_file(__tls_ctx, file.c_str()) != 1) randolf::rex::mk_exception("Cannot use certificate chain file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8266 return this;
8267 }; // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
8268
8269 /*======================================================================*//**
8270 @brief
8271 Load a TLS certificate chain in PEM format from memory and use it in the TLS
8272 context.
8273
8274 Although this functionality doesn't exist in OpenSSL (at the time of writing
8275 this method), it's provided here in a manner that has exactly the same effect
8276 as the @ref tls_ctx_use_certificate_chain_file() methods, but without needing
8277 the PEM-formatted certificate chain stored in a file beforehand.
8278
8279 @note
8280 The @c pem_data parameter is a pointer to the memory location that holds
8281 the PEM formatted certificate chain data. If the length of this data isn't
8282 specified or is set to zero (default), then it will be treated as a multiline
8283 ASCIIZ string.
8284
8285 Behind the scenes, we're just writing the pem_data memory to a temporary
8286 file with severely-limited permissions (), then optionally overwriting that
8287 temporary file with random data prior to deleting it (this is the default,
8288 since better security practices should be the default, but on a secured
8289 system it may not be necessary and so this option can also be disabled to
8290 save CPU cycles and reduce overall disk-write I/O operations).
8291
8292 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8293 OpenSSL library doesn't document which errors may be returned)
8294
8295 @returns The same rsocket object so as to facilitate stacking
8296 @see tls_ctx_use_certificate_chain_file
8297 @see tls_ctx_check_privatekey
8298 @qualifier TLS
8299 *///=========================================================================
8300 rsocket* tls_ctx_use_certificate_chain_pem(
8301 /// Pointer to certificate chain data in PEM format
8302 const char* pem_data,
8303 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
8304 size_t len = 0,
8305 /// Whether to overwrite the temporary file with random data before deleting it
8306 const bool random_fill = true) {
8307 if (__debug) debug("tls_ctx_use_certificate_chain_pem(<buf>, " + std::to_string(len)
8308 + ", " + (random_fill ? "true" : "false")
8309 + ");");
8310
8311 // --------------------------------------------------------------------------
8312 // Measure size of certificate chain if an ASCIIZ string was indicated.
8313 // --------------------------------------------------------------------------
8314 if (len == 0) len = std::strlen(pem_data);
8315
8316 // --------------------------------------------------------------------------
8317 // Generate filename for temporary use.
8318 // --------------------------------------------------------------------------
8319 std::string file = std::filesystem::temp_directory_path();
8320 file.append("/rsocket.")
8321 .append(std::to_string(::getpid()))
8322 .append(".")
8323 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
8324
8325 // --------------------------------------------------------------------------
8326 // Open temporary file.
8327 // --------------------------------------------------------------------------
8328 FILE* fp = fopen(file.c_str(), "w+");
8329 if (fp == nullptr) randolf::rex::mk_exception("Cannot open temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8330 { int attr;
8331 ioctl(fileno(fp), FS_IOC_GETFLAGS, &attr);
8332 attr |= FS_NOATIME_FL // Don't update access time attribute
8333 | FS_NODUMP_FL // Don't include in filesystem backup dumps
8334 | FS_SECRM_FL; // Mark file for secure deletion (where supported)
8335 ioctl(fileno(fp), FS_IOC_SETFLAGS, &attr);
8336 }
8337 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
8338 if (fputs(pem_data, fp) == EOF) {
8339 fclose(fp);
8340 randolf::rex::mk_exception("Cannot write to temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8341 } // -x- if !fputs -x-
8342 fflush(fp);
8343
8344 // --------------------------------------------------------------------------
8345 // Attempt to load certificate chain file, but save the error code for later
8346 // because we need to clean up the temporary file before possibly throwing an
8347 // exception.
8348 // --------------------------------------------------------------------------
8349 int rc = SSL_CTX_use_certificate_chain_file(__tls_ctx, file.c_str());
8350
8351 // --------------------------------------------------------------------------
8352 // Overwrite the contenst of the temporary file before deleting it so as to
8353 // sabotage a simple attempt to undelete the file and access the certificate.
8354 //
8355 // We're also re-using the "len" local variable because it's not needed once
8356 // we get the loop started, and it's more optimal to not allocate yet another
8357 // local variable while "len" goes to waste. :D
8358 // --------------------------------------------------------------------------
8359 if (random_fill) { // This option is configurable
8360 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
8361 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
8362 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
8363 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
8364 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
8365 } // -x- for i -x-
8366 } // -x- if randfill -x-
8367 fchmod(fileno(fp), 0); // Remove all permissions
8368 fclose(fp); // Close file handle
8369 unlink(file.c_str()); // Delete temporary file
8370
8371 // --------------------------------------------------------------------------
8372 // Error check ... was delayed here until after temporary file cleanup.
8373 // --------------------------------------------------------------------------
8374 if (rc != 1) randolf::rex::mk_exception("Cannot use certificate chain file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8375
8376 return this;
8377 }; // -x- rsocket* tls_ctx_use_certificate_chain_pem -x-
8378
8379 /*======================================================================*//**
8380 @brief
8381 Load a TLS private key in PEM format from a text file and use it in the TLS
8382 context.
8383
8384 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8385 OpenSSL library doesn't document which errors may be returned)
8386
8387 @returns The same rsocket object so as to facilitate stacking
8388 @see tls_ctx_use_privatekey_file
8389 @see tls_ctx_use_privatekey_pem
8390 @see tls_ctx_check_privatekey
8391 @qualifier TLS
8392 *///=========================================================================
8393 rsocket* tls_ctx_use_privatekey_file(
8394 /// Pointer to ASCIIZ path-and-filename of private key file
8395 const char* file) {
8396 if (__debug) debug("tls_ctx_use_privatekey_file("
8397 + std::string(file)
8398 + ");");
8399 if (SSL_CTX_use_PrivateKey_file(__tls_ctx, file, SSL_FILETYPE_PEM) != 1) randolf::rex::mk_exception(std::string("Cannot use private key file ") + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8400
8401 return this;
8402 }; // -x- rsocket* tls_ctx_use_privatekey_file -x-
8403
8404 /*======================================================================*//**
8405 @brief
8406 Load a TLS private key in PEM format from a text file and use it in the TLS
8407 context.
8408
8409 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8410 OpenSSL library doesn't document which errors may be returned)
8411
8412 @returns The same rsocket object so as to facilitate stacking
8413 @see tls_ctx_use_privatekey_file
8414 @see tls_ctx_use_privatekey_pem
8415 @see tls_ctx_check_privatekey
8416 @qualifier TLS
8417 *///=========================================================================
8418 rsocket* tls_ctx_use_privatekey_file(
8419 /// Path and filename to private key file
8420 const std::string file) {
8421 if (__debug) debug("tls_ctx_use_privatekey_file("
8422 + file
8423 + ");");
8424 if (SSL_CTX_use_PrivateKey_file(__tls_ctx, file.c_str(), SSL_FILETYPE_PEM) != 1) randolf::rex::mk_exception("Cannot use private key file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8425
8426 return this;
8427 }; // -x- rsocket* tls_ctx_use_privatekey_file -x-
8428
8429 /*======================================================================*//**
8430 @brief
8431 Load a TLS private key in PEM format from memory and use it in the TLS
8432 context.
8433
8434 Although this functionality doesn't exist in OpenSSL (at the time of writing
8435 this method), it's provided here in a manner that has exactly the same effect
8436 as the @ref tls_ctx_use_privatekey_file() methods, but without needing the
8437 PEM-formatted private key stored in a file beforehand.
8438
8439 @note
8440 The @c pem_data parameter is a pointer to the memory location that holds the
8441 PEM formatted private key data. If the length of this data isn't specified
8442 or is set to zero (default), then it will be treated as a multiline ASCIIZ
8443 string.
8444
8445 Behind the scenes, we're just writing the pem_data memory to a temporary
8446 file (with severely-limited permissions), then optionally overwriting that
8447 temporary file with random data prior to deleting it (this is the default,
8448 since better security practices should be the default, but on a secured
8449 system it may not be necessary and so this option can also be disabled to
8450 save CPU cycles and reduce overall disk-write I/O operations).
8451
8452 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8453 OpenSSL library doesn't document which errors may be returned)
8454
8455 @returns The same rsocket object so as to facilitate stacking
8456 @see tls_ctx_use_privatekey_file
8457 @see tls_ctx_check_privatekey
8458 @qualifier TLS
8459 *///=========================================================================
8460 rsocket* tls_ctx_use_privatekey_pem(
8461 /// Pointer to private key data in PEM format
8462 const char* pem_data,
8463 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
8464 size_t len = 0,
8465 /// Whether to overwrite the temporary file with random data before deleting it
8466 const bool random_fill = true) {
8467 if (__debug) debug("tls_ctx_use_privatekey_pem(<buf>, " + std::to_string(len)
8468 + ", " + (random_fill ? "true" : "false")
8469 + ");");
8470
8471 // --------------------------------------------------------------------------
8472 // Measure size of private key if an ASCIIZ string was indicated.
8473 // --------------------------------------------------------------------------
8474 if (len == 0) len = std::strlen(pem_data);
8475
8476 // --------------------------------------------------------------------------
8477 // Generate filename for temporary use.
8478 // --------------------------------------------------------------------------
8479 std::string file = std::filesystem::temp_directory_path();
8480 file.append("/rsocket.")
8481 .append(std::to_string(::getpid()))
8482 .append(".")
8483 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
8484
8485 // --------------------------------------------------------------------------
8486 // Open temporary file.
8487 // --------------------------------------------------------------------------
8488 FILE* fp = fopen(file.c_str(), "w+");
8489 if (fp == nullptr) randolf::rex::mk_exception("Cannot cannot open temporary file (to use private key) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8490 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
8491 if (fputs(pem_data, fp) == EOF) {
8492 fclose(fp);
8493 randolf::rex::mk_exception("Cannot cannot write to temporary file (to use private key) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8494 } // -x- if !fputs -x-
8495 fflush(fp);
8496
8497 // --------------------------------------------------------------------------
8498 // Attempt to load private key file, but save the error code for later
8499 // because we need to clean up the temporary file before possibly throwing an
8500 // exception.
8501 // --------------------------------------------------------------------------
8502 int rc = SSL_CTX_use_PrivateKey_file(__tls_ctx, file.c_str(), SSL_FILETYPE_PEM);
8503
8504 // --------------------------------------------------------------------------
8505 // Overwrite the contenst of the temporary file before deleting it so as to
8506 // sabotage a simple attempt to undelete the file and access the certificate.
8507 //
8508 // We're also re-using the "len" local variable because it's not needed once
8509 // we get the loop started, and it's more optimal to not allocate yet another
8510 // local variable while "len" goes to waste. :D
8511 // --------------------------------------------------------------------------
8512 if (random_fill) { // This option is configurable
8513 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
8514 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
8515 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
8516 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
8517 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
8518 } // -x- for i -x-
8519 } // -x- if randfill -x-
8520 fchmod(fileno(fp), 0); // Remove all permissions
8521 fclose(fp); // Close file handle
8522 unlink(file.c_str()); // Delete temporary file
8523
8524 // --------------------------------------------------------------------------
8525 // Error check ... was delayed here until after temporary file cleanup.
8526 // --------------------------------------------------------------------------
8527 if (rc != 1) randolf::rex::mk_exception("Cannot use private key file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8528
8529 return this;
8530 }; // -x- rsocket* tls_ctx_use_privatekey_pem -x-
8531
8532 /*======================================================================*//**
8533 @brief
8534 Initiate the TLS handshake with the endpoint (which is presumed to be a
8535 server).
8536 This method makes it easier to support application-level commands such as @c
8537 STARTTLS (which are implemented in protocols like SMTP, POP3, IMAP4, MEOW,
8538 FTP, NNTP, LDAP, XMPP, etc.).
8539
8540 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8541 OpenSSL library doesn't document which errors may be returned)
8542
8543 @returns The same rsocket object so as to facilitate stacking
8544 @see connect()
8545 @see connect(std::string, int)
8546 @see TLS_NO_INGRESS
8547 @qualifier TLS
8548 *///=========================================================================
8549 rsocket* tls_do_handshake() {
8550 if (__debug) debug("tls_handshake(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8551 + ");");
8552 __rc_check_tls(SSL_do_handshake(__tls_fd));
8553 return this;
8554 }; // -x- rsocket* tls_do_handshake -x-
8555
8556 /*======================================================================*//**
8557 @brief
8558 Get OpenSSL's TLS structure.
8559 @returns TLS structure
8560 @returns nullptr = TLS structure not yet allocated
8561 @qualifier TLS
8562 *///=========================================================================
8563 const SSL* tls_fd() noexcept {
8564 return __tls_fd;
8565 }; // -x- int tls_fd -x-
8566
8567 /*======================================================================*//**
8568 @brief
8569 Return the current @ref rsocket_sni object that this @c rsocket will use when
8570 accepting incoming encrypted connections.
8571 @returns Pointer to @c rsocket_sni object
8572 @returns nullptr = SNI is not assigned
8573 @see name_sni
8574 @see tls_sni(rsocket_sni*)
8575 @qualifier TLS
8576 *///=========================================================================
8577 rsocket_sni* tls_sni() noexcept {
8578 return __tls_sni;
8579 }; // -x- rsocket_sni* tls_sni -x-
8580
8581 /*======================================================================*//**
8582 @brief
8583 Set the current @ref rsocket_sni object that this @c rsocket will use when
8584 accepting incoming encrypted connections.
8585
8586 Use the @ref name() method to find out which server name was supplied by the
8587 endpoint that triggered the SNI callback, regardless of whether it matches
8588 any of the TLS certificates used with this rsocket object or the rsocket_sni
8589 object that's associated with this rsocket object. If an SNI callback wasn't
8590 triggered, or if the endpoint didn't provide a server name, then it will
8591 remain unaffected (and the default {empty string} will remain unchanged).
8592 @returns The same rsocket object so as to facilitate stacking
8593 @see name_sni
8594 @see tls_sni
8595 @see is_tls_sni_match
8596 @qualifier TLS
8597 *///=========================================================================
8598 rsocket* tls_sni(
8599 /// Pointer to the @ref rsocket_sni object to use, or specify @c nullptr to
8600 /// remove SNI support from this rsocket object
8601 rsocket_sni* sni) noexcept {
8602
8603 // --------------------------------------------------------------------------
8604 // Remove SNI support.
8605 // --------------------------------------------------------------------------
8606 if (sni == nullptr) {
8607 if (__tls_sni != nullptr) SSL_CTX_set_client_hello_cb(__tls_ctx, nullptr, this);
8608
8609 // --------------------------------------------------------------------------
8610 // Add or set SNI support.
8611 // --------------------------------------------------------------------------
8612 } else {
8613 SSL_CTX_set_client_hello_cb(__tls_ctx, tls_sni_callback, this); // Configure SNI callbacks for TLS.
8614 //SSL_CTX_set_tlsext_servername_callback(__tls_ctx, tls_sni_callback); // Don't use this anymore; it's outdated
8615 //SSL_CTX_set_tlsext_servername_arg(__tls_ctx, this); // Don't use this anymore; it's outdated
8616
8617 } // -x- if !sni -x-
8618
8619 // --------------------------------------------------------------------------
8620 // Update internal pointer to the SNI map.
8621 // --------------------------------------------------------------------------
8622 __tls_sni = sni;
8623
8624 return this;
8625 }; // -x- rsocket_sni* tls_sni -x-
8626
8627private:
8628 /*======================================================================*//**
8629 @brief
8630 Get OpenSSL's TLS structure.
8631 @returns TLS structure
8632 @returns nullptr = TLS structure not yet allocated
8633 @qualifier TLS
8634 *///=========================================================================
8635 int static tls_sni_callback(
8636 /// OpenSSL's socket descriptor/handle
8637 SSL* tls_fd,
8638 /// Where to store the @c alert value
8639 int* al,
8640 /// Context-specific argument
8641 void* arg) noexcept {
8642
8643 // --------------------------------------------------------------------------
8644 // Internal variables.
8645 // --------------------------------------------------------------------------
8646 rsocket* r = (rsocket*)arg; // We may be updating the TLS context (this is normally the new client's rsocket object)
8647 const unsigned char* out = nullptr;
8648 size_t out_size = 0;
8649
8650 // --------------------------------------------------------------------------
8651 // Obtain a pointer to newly-allocated ClientHello fields data. If *out is
8652 // nullptr, it means that no TLSEXT_TYPE_server_name was received from the
8653 // client, and so the default TLS context will suffice. If out_size is less
8654 // than or equal to 2, then it also won't have what we need.
8655 // --------------------------------------------------------------------------
8656 if (!SSL_client_hello_get0_ext(tls_fd, TLSEXT_TYPE_server_name, &out, &out_size)
8657 || out == nullptr || out_size <= 2) return SSL_CLIENT_HELLO_SUCCESS; // 1
8658
8659 // --------------------------------------------------------------------------
8660 // Scan for TLSEXT_NAMETYPE_host_name, but if we can't find it then we'll
8661 // just leave the default TLS context as is.
8662 // --------------------------------------------------------------------------
8663 unsigned char* p = (unsigned char*)out;
8664 size_t len = (*(p++) << 8);
8665 len += *(p++);
8666 if (len + 2 != out_size) goto finish;
8667
8668 // --------------------------------------------------------------------------
8669 // We're taking a shortcut by examining only the first element in the list,
8670 // but in the future we need to make this more robust in case other types of
8671 // elements precede what we're looking for.
8672 //
8673 // Unfortunately, there's no documentation that properly-explains the format
8674 // of the list, so some deeper research into OpenSSL's source code will be
8675 // needed (a cursory look so far has not yielded the necessary insight).
8676 //
8677 // TODO: Turn this into a loop that supports future clients that provide
8678 // multiple SNI server names in their requests. (Although this isn't
8679 // occuring at present with common end-user tools such as web browsers
8680 // and eMail software, it may happen in the future as client/server
8681 // software becomes more savvy.)
8682 // --------------------------------------------------------------------------
8683 if (out_size == 0 || *p++ != TLSEXT_NAMETYPE_host_name) goto finish;
8684
8685 // --------------------------------------------------------------------------
8686 // Avoid buffer overrun caused by corrupt or prematurely-truncated data.
8687 // --------------------------------------------------------------------------
8688 if (--out_size <= 2) goto finish;
8689
8690 // --------------------------------------------------------------------------
8691 // Extract and use the hostname (SNI server name) that was supplied by the
8692 // endpoint so that the correct TLS certificate can be selected and assigned.
8693 // --------------------------------------------------------------------------
8694 len = (*(p++) << 8);
8695 len += *(p++);
8696 if (!(len + 2 > out_size)) { // Only process SNI server name string if it's at least 2 bytes long
8697 // Debug: std::cout << "Server name: " << (const char*)p << std::endl; // Debug
8698
8699 // --------------------------------------------------------------------------
8700 // Obtain the correct TLS context (wildcards supported) that is associated
8701 // with the hostname (SNI server name) that was supplied by the endpoint.
8702 // --------------------------------------------------------------------------
8703 const char* sni_name = (const char*)p;
8704 SSL_CTX* new_ctx = r->__tls_sni->get_ctx(sni_name, true, r->tls_ctx());
8705
8706 // --------------------------------------------------------------------------
8707 // Change TLS context so that encryption with the endpoint uses the correct
8708 // TLS certificate (otherwise the endpoint will indicate security risk errors
8709 // to end users, log files, etc., and may {should} reject the connection).
8710 // --------------------------------------------------------------------------
8711//std::cout << "SNI setup... " << sni_name << std::endl;
8712 SSL_set_SSL_CTX(tls_fd, new_ctx);
8713//std::cout << "---1--- " << r->__tls_new_endpoint << std::endl;
8714 r->__tls_new_endpoint->tls_ctx(new_ctx);
8715//std::cout << "---2---" << std::endl;
8716 r->__tls_new_endpoint->__name_sni.assign(sni_name);
8717//std::cout << "SNI name() = " << r->__tls_new_endpoint->name() << std::endl;
8718 r->__tls_new_endpoint->__tls_sni_match = true;
8719
8720 } // -x- if len+2 -x-
8721
8722 finish:
8723 // --------------------------------------------------------------------------
8724 // Free the ClientHello fields data, then return SSL_CLIENT_HELLO_SUCCESS.
8725 // Even if we encountered a problem with the ClientHello fields data, we
8726 // still return SSL_CLIENT_HELLO_SUCCESS so that the TLS context will be
8727 // accepted as valid.
8728 //
8729 // If we return SSL_CLIENT_HELLO_ERROR the connection will fail unnecessarily
8730 // for valid TLS certificates. If the ClientHello fields data is malformed
8731 // to a degree that doesn't satisfy OpenSSL, then OpenSSL will reject it, so
8732 // there's really no point in duplicating what OpenSSL already does properly,
8733 // which OpenSSL will pass through the standard error channels with normal
8734 // error details-and-diagnostics anyway.
8735 // --------------------------------------------------------------------------
8736 //OPENSSL_free((char*)out); // OpenSSL documentation says to do this, but it fails and crashes the program
8737
8738 return SSL_CLIENT_HELLO_SUCCESS; // 1
8739 }; // -x- int tls_sni_callback -x-
8740
8741public:
8742 /*======================================================================*//**
8743 @brief
8744 Convert a 48-bit integer to a machine address in the form of @c
8745 xx:xx:xx:xx:xx:xx where every instance of @c xx is a hexadecimal
8746 representation of each respective 8-bit byte portion.
8747
8748 This method is needed because we don't want to bring in the heavy fmt::format
8749 class as a dependency.
8750 @returns Mac address as 17-character in the typical format expected by system
8751 administrators
8752 @qualifier TLS
8753 *///=========================================================================
8754 static std::string to_mac(
8755 /// Pointer to 48-bit integer
8756 const void* addr) noexcept {
8757 std::string h;
8758 h.resize(18); // 48-bit mac address needs 6 hexadecimal pairs of nybbles delimited by colons plus a NULL terminator
8759 h.resize(snprintf(h.data(),
8760 h.size(),
8761 "%02x:%02x:%02x:%02x:%02x:%02x",
8762 ((u_char*)addr)[0],
8763 ((u_char*)addr)[1],
8764 ((u_char*)addr)[2],
8765 ((u_char*)addr)[3],
8766 ((u_char*)addr)[4],
8767 ((u_char*)addr)[5])); // Convert, and truncate NULL terminator
8768 return h;
8769 }; // -x- std::string to_mac -x-
8770
8771 /*======================================================================*//**
8772 @brief
8773 Convert a 48-bit integer to a node address in the form of @c xxxx:xxxx:xxxx
8774 where every instance of @c xxxx is a hexadecimal representation of each
8775 respective 16-bit word portion.
8776
8777 This method is needed because we don't want to bring in the heavy fmt::format
8778 class as a dependency.
8779 @returns Node address as 14-character in the typical format expected by
8780 network administrators
8781 @qualifier TLS
8782 *///=========================================================================
8783 static std::string to_node(
8784 /// Pointer to 48-bit integer
8785 const void* addr) noexcept {
8786 std::string h;
8787 h.resize(15); // 48-bit node address needs 3 hexadecimal sets of words delimited by colons plus a NULL terminator
8788 h.resize(snprintf(h.data(),
8789 h.size(),
8790 "%04x:%04x:%04x",
8791 ((u_int16_t*)addr)[0],
8792 ((u_int16_t*)addr)[1],
8793 ((u_int16_t*)addr)[2])); // Convert, and truncate NULL terminator
8794 return h;
8795 }; // -x- std::string to_node -x-
8796
8797 /*======================================================================*//**
8798 @brief
8799 Send a formatted string to the @ref rsocket endpoint.
8800
8801 The @c format is described in the documentation for the POSIX or Standard C
8802 Library @c printf() function.
8803 @throws randolf::rex::xEBADF The underlying socket is not open
8804 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
8805 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
8806 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
8807 @throws randolf::rex::xENOMEM Insufficient memory
8808 @returns The same rsocket object so as to facilitate stacking
8809 @see eol_fix_printf
8810 @see is_eol_fix_printf
8811 @see net_io
8812 @see printf
8813 @see printfline
8814 @see vprintfline
8815 @qualifier POSIX
8816 @qualifier TLS
8817 *///=========================================================================
8818 rsocket* vprintf(
8819 /// Format string to use
8820 const char* format,
8821 /// Variadic arguments in @c va_list format
8822 va_list args) {
8823 char* buf;
8824 int rc = ::vasprintf(&buf, format, args);
8825 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
8826 if (__eol_fix_printf && !__eol.empty()) {
8827 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
8828 ::free(buf);
8829 __send(str.c_str(), str.length());
8830 } else {
8831 try {
8832 __send(buf, rc);
8833 } catch (std::exception& e) { // Free buf then re-throw the exception
8834 ::free(buf); // Prevent memory leak when an exception is thrown
8835 throw;
8836 }
8837 } // -x- if __eol_fix_printf -x-
8838 return this;
8839 }; // -x- rsocket* vprintf -x-
8840
8841 /*======================================================================*//**
8842 @brief
8843 Send a formatted string to the @ref rsocket endpoint, and append an EoL
8844 sequence.
8845
8846 The @c format is described in the documentation for the POSIX or Standard C
8847 Library @c printf() function.
8848 @throws randolf::rex::xEBADF The underlying socket is not open
8849 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
8850 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
8851 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
8852 @throws randolf::rex::xENOMEM Insufficient memory
8853 @returns The same rsocket object so as to facilitate stacking
8854 @see eol
8855 @see eol_fix_printf
8856 @see is_eol_fix_printf
8857 @see net_io
8858 @see printf
8859 @see printfline
8860 @see sendline
8861 @see vprintf
8862 @qualifier TLS
8863 *///=========================================================================
8864 rsocket* vprintfline(
8865 /// Format string to use
8866 const char* format,
8867 /// Variadic arguments in @c va_list format
8868 va_list args) {
8869 char* buf;
8870 int rc = ::vasprintf(&buf, format, args);
8871 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
8872 if (__eol_fix_printf && !__eol.empty()) {
8873 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
8874 .append(__eol);
8875 ::free(buf);
8876 __send(str.c_str(), str.length());
8877 } else {
8878 try {
8879 __sendline(buf, rc);
8880 } catch (std::exception& e) { // Free buf then re-throw the exception
8881 ::free(buf); // Prevent memory leak when an exception is thrown
8882 throw;
8883 }
8884 } // -x- if __eol_fix_printf -x-
8885 return this;
8886 }; // -x- rsocket* vprintfline -x-
8887
8888 private:
8889 /*======================================================================*//**
8890 Track unencrypted bytes received. When the number of bytes is negative or
8891 zero, it isn't recorded.
8892 This is an internal function.
8893 @returns same value provided in @ref n
8894 @qualifier TLS
8895 *///=========================================================================
8896 int __track_bytes_rx(
8897 /// Number of bytes transferred
8898 const int n) {
8899 if (n > 0) __bytes_rx.fetch_add(n, std::memory_order_relaxed);
8900 return n;
8901 }; // -x- __track_bytes_rx -x-
8902
8903 /*======================================================================*//**
8904 Track unencrypted bytes transmitted. When the number of bytes is negative or
8905 zero, it isn't recorded.
8906 This is an internal function.
8907 @returns same value provided in @ref n
8908 @qualifier TLS
8909 *///=========================================================================
8910 int __track_bytes_tx(
8911 /// Number of bytes transferred
8912 const int n) {
8913 if (n > 0) __bytes_tx.fetch_add(n, std::memory_order_relaxed);
8914 return n;
8915 }; // -x- __track_bytes_tx -x-
8916
8917 /*======================================================================*//**
8918 Track encrypted bytes received. When the number of bytes is negative or
8919 zero, it isn't recorded.
8920 This is an internal function.
8921 @returns same value provided in @ref n
8922 @qualifier TLS
8923 *///=========================================================================
8924 int __track_crypt_rx(
8925 /// Number of bytes transferred
8926 const int n) {
8927 if (n > 0) __crypt_rx.fetch_add(n, std::memory_order_relaxed);
8928 return n;
8929 }; // -x- __track_crypt_rx -x-
8930
8931 /*======================================================================*//**
8932 Track encrypted bytes transmitted. When the number of bytes is negative or
8933 zero, it isn't recorded.
8934 This is an internal function.
8935 @returns same value provided in @ref n
8936 @qualifier TLS
8937 *///=========================================================================
8938 int __track_crypt_tx(
8939 /// Number of bytes transferred
8940 const int n) {
8941 if (n > 0) __crypt_tx.fetch_add(n, std::memory_order_relaxed);
8942 return n;
8943 }; // -x- __track_crypt_tx -x-
8944
8945 }; // -x- class rsocket -x-
8946
8947}; // -x- namespace randolf -x-
8948
8949// Save this for a future sendlines() methods.
8950// const void* msg_ptr = msg.c_str(); // Prevent repeated calls to c_str() method
8951// const int len = msg.length(); // Prevent repeated calls to length() method