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/rsocket_io>
5#include <randolf/rsocket_sni>
6#include <randolf/rtools>
7#include <randolf/sockaddr_dl.h>
8
9#include <atomic>
10#include <bit> // std::endian
11#include <cstdarg> // std::va_list
12#include <cstring> // std::strlen
13#include <ctime> // std::strftime
14#include <exception> // std::exception
15#include <filesystem> // std::filesystem::temp_directory_path
16#include <initializer_list>
17#include <iostream> // std::put_date
18#include <map> // std::map
19#include <memory> // std::shared_ptr
20#include <mutex> // std::mutex
21#include <regex>
22#include <set>
23//#include <sstream> // std::ostringstream
24#include <unordered_map> // std::unordered_map
25#include <vector>
26
27#include <ifaddrs.h>
28#include <netdb.h>
29#include <poll.h>
30#include <string.h> // strerror()
31#include <unistd.h>
32
33#include <arpa/inet.h>
34
35#include <linux/fs.h> // Flags for ioctl()
36
37#include <net/if.h> // ifreq structure used by bind()
38
39#include <netinet/icmp6.h>
40#include <netinet/in.h>
41
42#include <netpacket/packet.h> // struct sockaddr_ll
43
44#include <openssl/err.h>
45#include <openssl/ossl_typ.h>
46#include <openssl/ssl.h>
47
48#include <sys/ioctl.h>
49#include <sys/socket.h>
50#include <sys/stat.h> // fchmod()
51#include <sys/time.h> // TIMEVAL_TO_TIMESPEC macro
52#include <sys/types.h>
53#include <sys/un.h>
54
55static_assert((sizeof(__time_t) * CHAR_BIT) >= 64, "64-bit __time_t is required");
56
57namespace randolf {
58
59 /*======================================================================*//**
60 @brief
61 This @ref rsocket class provides an easy-to-use and thoroughy-implemented
62 object-oriented socket I/O interface for C++, intended to make socket I/O
63 programming (with or without TLS encryption) easier and more enjoyable.
64
65 Here's a short list of benefits that are helpful in developing high quality
66 code that's consistent-and-reliable, and improves overall productivity:
67
68 - eliminating the need to repeatedly write blocks of code that check for
69 errors, by throwing exceptions instead (see @ref randolf::rex::rex class
70 for details and the long list of exceptions that are supported)
71 - eliminating the need to track socket descriptors
72 - eliminating the need to handle encrypted I/O separately (most functions)
73 - eliminating the need to manage memory for many common structures used to
74 interface with socket options, etc.
75 - eliminating the need to record socket I/O statistics with every call to
76 underlying socket I/O functions (see @ref randolf::rsocket_io for
77 details)
78 - text-line reading/writing with an adapative approach (invented by
79 Randolf Richardson in the 1980s for a custom BBS software project) to
80 automatically detect EoL (End-of-Line) character sequences (unless the
81 developer provides a specific sequence via an @ref eol method) that can
82 determine whether an endpoint is sending Linux/UNIX (standard), MacOS,
83 DOS CR/LF, or even broken LF/CR (reverse of DOS) EoL sequences
84 - transparent support for encryption with many additional features,
85 including STARTTLS, ingress/egress policy enforcement, and SNI
86 - eliminating the complexity of handling events with poll(), select(), and
87 related functions (see the @ref randolf::rsocket_mux class for details)
88 - providing a variety of other useful features that make it easier to
89 communicate with socket endpoints, such as receiving/sending an entire
90 structure via a single call to the new-and-specialized @ref recv_struct
91 or @ref send_struct methods, respectively
92
93 An rsocket is either the endpoint that our underlying socket will connect to,
94 or it's the server daemon that our underying socket will listen() to and
95 accept() [inbound] connections from.
96
97 @par Use case
98
99 Using the C interface, the programming must check for errors by testing the
100 response codes (most of which are consistent, with a few subtle outliers),
101 which leads to a lot of additional error-checking code with the potential for
102 unintended errors (a.k.a., bugs). This style is necessary in C, but with C++
103 the way to handle errors is with exceptions, so I created this rsocket class
104 to handle all these tedious details behind-the-scenes and, for any socket
105 errors, to generate exceptions so that source code can be greatly simplified
106 (and, as a result, also easier to read and review).
107
108 Pre-allocating buffers is also handled internally, which is particularly
109 useful when making repeated calls to recv() and related methods. These
110 methods return std::shared_ptr<@c structure > (a C++ smart pointer that aids
111 in the prevention of resource leaks) or std::vector<char> (resized to the
112 actual number of bytes received) as appropriate which eliminates the need to
113 track @c size_t separately.
114
115 @par Conventions
116 Lower-case letter "r" is regularly used in partial example code to represent
117 an instantiated rsocket object.
118
119 An ASCIIZ string is a C-string (char* array) that includes a terminating null
120 (0) character at the end.
121
122 The following custom qualifiers are incorporated into headings by Doxygen
123 alongside method titles throughout the documentation:
124 - @c POSIX denotes a method that is based on POSIX functions by the same
125 name and don't deviate significantly from the POSIX function arguments
126 (intended to be helpful to developers transitioning to/from rsocket or
127 working on source code that utilizes @ref rsocket and POSIX functions)
128 - @c TLS denotes that a method works properly with TLS-encrypted sockets
129 (most of the POSIX functions have been made to work properly with TLS,
130 but for the few rare cases of functions that can't be made to work with
131 TLS an effort has also been made to mention this using Doxygen's
132 "warning" sections in addition to omitting the TLS qualifier)
133
134 @par Getting started with a few simple examples
135
136 This is an example of connecting to an HTTP server, using the "GET" command
137 to request the home page (using HTTP/1.0), then receiving-and-displaying the
138 resulting web page's contents via STDOUT (or sending an error message to
139 STDERR). Finally, we exit with an EXIT_SUCCESS (or EXIT_FAILURE) code.
140
141 @code{.cpp}
142 #include <iostream> // std::cout, std::cerr, std::endl, etc.
143 #include <randolf/rex>
144 #include <randolf/rsocket>
145
146 int main(int argc, char *argv[]) {
147 try {
148 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
149 r.connect("www.example.com", 80);
150 r.sendline("GET / HTTP/1.0");
151 r.sendline("Host: www.example.com");
152 r.sendline("Connection: close");
153 r.send_eol();
154 while (r.is_open()) {
155 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
156 } // -x- while data -x-
157 r.close();
158 } catch (const randolf::rex::xALL e) {
159 std::cerr << "Socket exception: " << e.what() << std::endl;
160 return EXIT_FAILURE;
161 } catch (const std::exception e) {
162 std::cerr << "Other exception: " << e.what() << std::endl;
163 return EXIT_FAILURE;
164 }
165 return EXIT_SUCCESS;
166 } // -x- int main -x-
167 @endcode
168
169 Parameter stacking is supported (with methods that return @c rsocket*); in
170 this example, notice that semicolons (";") and "r." references are omittted
171 (when compared with the above):
172
173 @code{.cpp}
174 #include <iostream> // std::cout, std::cerr, std::endl, etc.
175 #include <randolf/rex>
176 #include <randolf/rsocket>
177
178 int main(int argc, char *argv[]) {
179 try {
180 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
181 r.connect("www.example.com", 80)
182 ->sendline("GET / HTTP/1.0")
183 ->sendline("Host: www.example.com")
184 ->sendline("Connection: close")
185 ->send_eol();
186 while (r.is_open()) {
187 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
188 } // -x- while data -x-
189 r.close();
190 } catch (const randolf::rex::xALL e) {
191 std::cerr << "Socket exception: " << e.what() << std::endl;
192 return EXIT_FAILURE;
193 } catch (const std::exception e) {
194 std::cerr << "Other exception: " << e.what() << std::endl;
195 return EXIT_FAILURE;
196 }
197 return EXIT_SUCCESS;
198 } // -x- int main -x-
199 @endcode
200
201 @par Features
202
203 This is meant to be a comprehensive socket class for C++, which is intended
204 to make socket I/O coding easier for developers with some key features:
205
206 - easy conversion from C-style POSIX sockets due to API consistency
207 - transparent TLS support (OpenSSL dependency)
208 - keys and certificate chains can also be loaded from memory
209 - SNI support (see the @ref rsocket_sni class for more information)
210 - underlying socket handle is accessible (via the @ref socket_fd() method)
211 - socket options are easier to get and to set
212 - sensible support for future or unknown socket options
213 - errors are presented as ~100 separate exception classes: @ref rex::rex
214 - one parent exception class makes it easier to catch all socket errors
215 - a few exceptions groups are also provided to catch groups of errors
216 - new buffers are returned as std::shared_ptr's to eliminate memory leaks
217 - constructors with sensible defaults help to simplify coding
218 - documentation includes code samples (with @c \#include lines as needed)
219 - debug output with helpful output (and option to set output file handle)
220 - low-overhead is considered (this is why there's a bit more overloading)
221 - thread-safety is noted where it is absolutely available (or where some
222 caution is warranted)
223 - can send ASCIIZ (C-strings) without needing to specify string length
224 - can send @c std::string (which tracks its own string length)
225 - each socket can optionally have a @ref name
226
227 Additional features that are not part of the typical POSIX standard, but
228 deserve special mention because they are needed so often:
229
230 - easy access to internal I/O counters (see @ref rsocket_io for details)
231 - your rsocket_io structure can be updated automatically by rsocket's
232 destructor after underlying socket is closed (see @c net_io_final()
233 method for details)
234 - printf() // Formatted strings (with automatic EoL sequence substitution)
235 - recvline(), sendline() // ASCII text line I/O operations
236 - class-wide configurable newline sequence (defaults to @e autodetect)
237 - recv_asciiz(), send_asciiz() // ASCIIZ string I/O operations
238 - recv_byte() , send_byte() // 8-bit byte operations (LSB/MSB is N/A)
239 - recv_struct(), send_struct() // Multi-byte operations
240 - recv_uint16(), send_uint16() // 16-bit operations with LSB/MSB variants
241 - recv_uint32(), send_uint32() // 32-bit operations with LSB/MSB variants
242 - recv_uint64(), send_uint64() // 64-bit operations with LSB/MSB variants
243
244 Some advanced features are planned that exceed what the basic socket I/O
245 functions provide, but are also needed:
246
247 - @ref rsocket_group class for grouping rsockets (and automatic inclusion
248 by accept() and accept4() methods; this may be a different group from
249 whichever group the parent rsocket is in, if it's even in one)
250 - option to send data to all rsocket objects in the rsocket_group
251 - with support from the rsocket_mux class (for multiplexing operations)
252 - automatic naming policies (possibly like net_io() formatting style)
253 - recv_uint128()/send_uint128() // 128-bit operations w/ LSB/MSB variants
254 - recv_uint(n)/send_uint(n) where "n" specifies the number of bits (which
255 must be a multiple of 8), with LSB/MSB variants
256 - auto-detection of inbound TLS connection (this is turned off by default)
257 - This is not the same as STARTTLS (an application-level command, for
258 which the @ref tls_do_handshake() method will likely be used)
259 - simple timing tracking options using timing_start() and timing_stop()
260 methods, the results of which can be retrieved with timing_get() or a
261 similarly-named group of methods
262
263 Other features that are not a high priority:
264
265 - internal support for portability to MS-Windows (this is a big headache
266 that I know will be time-consuming because Windows Sockets is not the
267 same as the standard POSIX sockets APIs that are used by Linux, UNIX,
268 MacOS, and pretty much all other Operating Systems. This means that
269 this really isn't a high priority for me without sufficient demand and
270 sufficient funding so I can commit my time without missing mortgage
271 payments, student loan payments {for my kids}, living expenses, etc.)
272
273 @par Notes
274
275 I use the term "ASCIIZ string" to indicate an array of characters that's
276 terminated by a 0 (a.k.a., null). Although this is very much the same as a
277 C-string, the difference is that in many API functions a C-string must often
278 be accompanied by its length value. When referring to an ASCIIZ string, I'm
279 intentionally indicating that the length of the string is not needed because
280 the string is null-terminated. (This term was also commonly used in assembly
281 language programming in the 1970s, 1980s, and 1990s, and as far as I know is
282 still used by machine language programmers today.)
283
284 UTF-8 works without any problems as it is backward-compatible to 8-bit ASCII,
285 and because @c std::string uses 8-bit bytes to store strings internally. Do
286 keep in mind that the manipulation of UTF-8 substrings will require working
287 with UTF-8 codepoints that may consume one or more bytes, which is beyond the
288 scope of the impartial (to UTF-8 and ASCII) functionality that this rsocket
289 class provides.
290
291 UTF-8 newline codepoints NEL (c2 85), LS (e2 80 a8), and PS (e2 80 a9) garner
292 no special handling at this time - unlike CR/LF (0d/0a) - and are merely
293 treated as non-newline codepoints. There is a possibility of adding support
294 for this in the future, but additional research and planning is required to
295 make sure this works properly. What is most likely is that some UTF-8 flags
296 will be added to support each of these (which will probably be disabled by
297 default) that will be integrated into the readline() methods. This also
298 depends on how widely used these particular codepoints are used, and pending
299 further research to determine whether these really are supposed to be used
300 functionally as newlines...
301
302 Examples of two UTF-8 codepoints that are not functional, but which a few
303 people have mistakenly assumed are functional:
304 - <sup>C</sup><sub>R</sub> = superscript "C" with subscript "R" (e2 90 9d)
305 - <sup>N</sup><sub>L</sub> = superscript "N" with subscript "L" (e2 90 a4)
306
307 The above are designed as special characters to represent the Carriage-Return
308 and New-Line respectively on ASCII character reference charts, which were
309 used primarily by IBM in the 1970s and 1980s for instruction manuals and
310 other documentation, and on some keyboard overlays.
311
312 @par Background
313
314 I created this class to make it easier to write internet server daemons. I
315 started out using C-style socket functions (because C++ doesn't come with a
316 socket class), but I found that I didn't enjoy mixing something as important
317 and detailed as socket I/O in a procedural way into the object-oriented
318 paradigm that C++ provides.
319
320 After looking for existing solutions (none of which served as comprehensive
321 replacements for socket I/O), I embarked on creating the rsocket class, and
322 then I began to understand why this probably hadn't been done -- it's a
323 massive untertaking, primarily because there are a lot of functions that are
324 needed to handle socket I/O. Further, [at the time of this writing] the @c
325 sockaddr_storage structure wasn't as widely used as it should be, and so
326 information about it tended to be scarce, incomplete, or incorrect (further
327 research was required to understand this properly, which was worthwhile).
328
329 Moving error codes into exceptions is also a major effort because they are so
330 diverse and plentiful, and there are so many errors that can occur at various
331 stages for many different reasons. There are also a few outlier functions
332 that require slightly different approaches to error handling due to subtly
333 different rules for handling their errors, and so the exception-generation
334 wasn't as straight-forward as one might optimistically expect, but this is
335 one of the many benefits of the object-oriented programming pardigm because a
336 handling edge cases internally results in a consistent error-handling
337 interface using exceptions that also simplifies the source code. (Need to
338 handle a specific set of conditions? Catch the relevant exceptions for those
339 cases in an inner set of exceptions, and just catch all the others in a more
340 general way without the added complexity of repeatedly checking for errors
341 every step along the way.)
342
343 So, I dedicated time to make this work, and with the intention of making it
344 an open source project once I got it into a state that's ready for the
345 general public. This required putting my other C++ projects on hold, which
346 was fine because they didn't have strict deadlines and using this socket
347 class in them will speed up development in the long-term anyway, so it's
348 clearly worth the effort to me.
349
350 My background in programming began when I was a young child, teaching myself
351 BASIC and then machine language (when I found BASIC to be too limited) before
352 moving on to other languages like Perl and Java many years later. Eventually
353 I circled around to C (which I chose to learn the hard way by writing some
354 PostgreSQL extensions) and then C++ a few years after that. I have a lot of
355 experience with socket communications, including fully-featured DNS resolver
356 library code in machine language for Novell's NetWare that used C calling
357 conventions and supported varargs, which worked well for the few developers
358 who needed or wanted it.
359
360 @par History
361 - 2022-Nov-09 v1.00 Initial version
362 - 2022-Nov-26 v1.00 Moved exception handling to a separate class
363 - 2022-Nov-28 v1.00 Completed readline/send functionality
364 - 2022-Dec-03 v1.00 Added endianness transparency
365 - 2022-Dec-04 v1.00 Added printf() support
366 - 2022-Dec-24 v1.00 Added socket MUXing
367 - 2023-Feb-22 v1.00 Added TLS/SSL support
368 - 2023-Mar-10 v1.00 Added TLS-SNI support
369 - 2023-Apr-19 v1.00 Added TLS certificate loading from memory
370 - 2023-May-24 v1.00 Added support for clang++ compilation
371 - 2023-Jun-09 v1.00 Improvements to dynamic internal read buffering
372 - 2023-Oct-31 v1.00 Improvements to various classes
373 - 2024-Feb-21 v1.00 Added is_buffered() method
374 - 2024-Mar-31 v1.00 Completed @ref recvline (implemented a work-around for
375 a subtle SSL_peek failure to extract additional data
376 when a user at an end-point is communicating with
377 "icanon" mode enabled)
378 - 2024-May-24 v1.00 Changed source code to accomodate @c clang++ compiler
379 - 2024-Jun-04 v1.00 Added @c POSIX and @c TLS qualifiers to all applicable
380 methods (Doxygen incorporates into the documentation)
381 @version 1.00
382 @author Randolf Richardson
383
384 *///=========================================================================
385 class rsocket {
386
387 // --------------------------------------------------------------------------
388 // The rsocket_group class needs access to some of our internal variables.
389 // --------------------------------------------------------------------------
390 friend class rsocket_group;
391
392 // --------------------------------------------------------------------------
393 // Socket variables.
394 // --------------------------------------------------------------------------
395 int __socket_fd = 0;
396 int __socket_type = 0;
397 int __socket_protocol = 0;
398 struct sockaddr_storage __socket_addr = {}; // Initialize to all elements to their default values
399 socklen_t __socket_addr_size = sizeof(__socket_addr); // We need to point to this (and it might be modified)
400 int __socket_backlog = SOMAXCONN; // Default backlog is 4096 on Linux, and 128 on older systems
401
402 // --------------------------------------------------------------------------
403 // Socket flags (internal, but with get-methods to provide read-only access).
404 // --------------------------------------------------------------------------
405 std::atomic_bool __socket_open = false; // Socket is open by way of socket() function
406 std::atomic_bool __socket_connected = false; // Socket is connected
407
408 // --------------------------------------------------------------------------
409 // TLS flags and variables.
410 // --------------------------------------------------------------------------
411 std::atomic_bool __tls = false; // Indicates whether socket I/O is encrypted with OpenSSL
412 SSL_CTX* __tls_ctx = nullptr; // TLS context (nullptr == no encryption initialized)
413 rsocket_sni* __tls_sni = nullptr; // SNI maps
414 bool __tls_sni_match = false; // Set to TRUE only if SNI matched in the SNI callback
415 SSL* __tls_fd = nullptr; // TLS connection structure (nullptr == no connection)
416 BIO* __tls_rbio = nullptr; // TLS BIO input stream
417 BIO* __tls_wbio = nullptr; // TLS BIO output stream
418 bool __tls_exclusive = false; // See enum TLS_FLAGS::TLS_EXCLUSIVE
419 bool __tls_egress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_EGRESS
420 bool __tls_ingress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_INGRESS
421 bool __tls_server_mode = true; // See enum TLS_FLAGS::TLS_SERVER
422 inline static std::atomic_int __fn_counter = 0; // Used in generating unique-and-threadsafe temporary filenames
423 rsocket* __tls_new_endpoint = nullptr; // Weak pointer to newly-spawned endpoint's rsocket object
424
425 // --------------------------------------------------------------------------
426 // Keep track of test for whether this host stores integers using big endian.
427 // --------------------------------------------------------------------------
428 inline static const bool __endian_is_msb = htons(42) == 42; // TRUE==MSB / FALSE==LSB
429
430 // --------------------------------------------------------------------------
431 // General variables. Atomic variables are used for variables as needed to
432 // support operations in a thread-safe manner.
433 // --------------------------------------------------------------------------
434 std::atomic_bool __debug = false;
435 std::atomic<std::FILE*> __debug_fd = stderr;
436 std::string __debug_prefix = "rsocket-debug";
437 #define RSOCKET_BUFFER_SIZE 1024 // Default buffer size
438 size_t __buffer_size = RSOCKET_BUFFER_SIZE; // Buffer size used by recv() when a buffer size isn't provided
439 char* __buffer = nullptr; // Initialized automatically when recvline() is used
440 char* __buffer_head = nullptr; // Newest data in buffer (a pointer is faster than calculating offsets)
441 char* __buffer_tail = nullptr; // Oldest data in buffer (a pointer is faster than calculating offsets)
442 char* __buffer_boundary = nullptr; // Size-wrapped boundary (a pointer is faster than calculating lengths)
443 std::string __name; // Arbitrary name of this rsocket (used by some applications)
444 std::string __name_sni; // SNI hostname (when tls_sni is configured)
445
446 // --------------------------------------------------------------------------
447 // EoL sequence variables.
448 // --------------------------------------------------------------------------
449 static const char __CR = (char)13; // CTRL-M / ASCII 13 (0Dh) / "\r" / Used by eol() methods
450 static const char __LF = (char)10; // CTRL-J / ASCII 10 (0Ah) / "\n" / Used by eol() methods
451 static constexpr const char* __CRLF = "\x0d\x0a"; // Used by eol() methods
452 std::string __eol; // Used by recvline() method; maintained by eol() methods
453 bool __eol_adoption = true; // Whether to adopt the dynamically detected EoL sequence when __eol is empty (the default)
454 std::string __eol_out = std::string(__CRLF); // Used by sendline() method; maintained by eol() methods; also used by sendline() method
455 bool __eol_fix_printf = true; // Whether to replace "\n" sequence in printf() methods to EoL sequence
456
457 // --------------------------------------------------------------------------
458 // Fine-tuning for recvline(). This is used with nanosleep().
459 // --------------------------------------------------------------------------
460 struct timespec __recvline_idle = {0, 999999999/3}; // Used by recvline() method; time to sleep between keystrokes (default to 1/8 of a second) to mitigate tight CPU high utilization loops
461 long __recvline_timeout = 0; // Number of seconds (0 = unlimited; maximum for "long" equates to approximately 292 billion years since the UNIX/Linux epoch)
462
463 // --------------------------------------------------------------------------
464 // Statistical variables.
465 // --------------------------------------------------------------------------
466 rsocket_io* __io_final_addr = nullptr; // Used by ~rsocket() destructor, net_io_final(), and net_io_update()
467 std::atomic_ulong __bytes_rx = 0; // Total number of bytes received
468 std::atomic_ulong __bytes_tx = 0; // Total number of bytes transmitted
469 std::atomic_ulong __crypt_rx = 0; // Total number of encrypted bytes recevied
470 std::atomic_ulong __crypt_tx = 0; // Total number of encrypted bytes transmitted
471
472 public:
473 /*======================================================================*//**
474 @brief
475 Optional flags used with various methods to determine whether they will throw
476 an @ref randolf::rex::xETIMEDOUT exception or merely return a @c nullptr, an
477 empty set, or a 0 (zero) when a timeout duration elapses.
478
479 @note
480 You'll know when this is an option because the method will support this.
481 *///=========================================================================
482 enum TIMEOUT_BEHAVIOUR: bool {
483
484 /*----------------------------------------------------------------------*//**
485 Indicate that an @ref randolf::rex::xETIMEDOUT exception should be thrown
486 when the timeout duration elapses.
487 *///-------------------------------------------------------------------------
488 TIMEOUT_EXCEPTION = true,
489
490 /*----------------------------------------------------------------------*//**
491 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
492 when the timeout duration elapses.
493 *///-------------------------------------------------------------------------
494 TIMEOUT_EMPTY = false,
495
496 /*----------------------------------------------------------------------*//**
497 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
498 when the timeout duration elapses.
499 *///-------------------------------------------------------------------------
500 TIMEOUT_NULL = false,
501
502 /*----------------------------------------------------------------------*//**
503 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
504 when the timeout duration elapses.
505 *///-------------------------------------------------------------------------
506 TIMEOUT_ZERO = false,
507
508 }; // -x- enum TIMEOUT_BEHAVIOUR -x-
509
510 /*======================================================================*//**
511 @brief
512 Optional flags used with rsocket's @ref tls() and @ref tls_ctx() methods to
513 specify relevant policies/semantics.
514 *///=========================================================================
515 enum TLS_FLAGS: int {
516
517 /*----------------------------------------------------------------------*//**
518 The TLS_DEFAULT flag isn't necessary, but it's included here for completeness
519 as it accomodates programming styles that prefer to emphasize when defaults
520 are being relied upon.
521 *///-------------------------------------------------------------------------
522 TLS_DEFAULT = 0,
523
524 /*----------------------------------------------------------------------*//**
525 Only encrypted connections are permitted, initially, and all attempts to
526 begin with unencrypted connections will consistently fail.
527
528 Encrypted connections must begin with a cryptographic handshake packet, or
529 else the connection will be rejected as due to being reasonably assumed to be
530 "not encrypted."
531 @post
532 If this flag isn't set, an application-level mechanism can be used to upgrade
533 to TLS encryption (this is common with connections that begin unencrypted,
534 and issue a proprietary command {such as the @c STARTTLS command in SMTP} to
535 upgrade to TLS later on {of which the @ref tls() method is used to affect an
536 ingress; see the @ref TLS_NO_INGRESS flag for additional information}).
537 @note
538 Creating an exclusively @c unencrypted connection is accomplished by not
539 initializing TLS.
540 @see tls()
541 @see TLS_NO_EGRESS to prevent support for application-level downgrades to
542 unencrypted connections
543 *///-------------------------------------------------------------------------
544 TLS_EXCLUSIVE = 1,
545
546 /*----------------------------------------------------------------------*//**
547 Mid-stream upgrades to encrypted connections are not permitted (e.g., via
548 application-level initiations like the @c STARTTLS command as seen in SMTP),
549 which will also cause calls to the @ref tls() method to fail fast after plain
550 non-encrypted data has already been sent or received.
551 @pre
552 This flag is not necessary when the @ref TLS_EXCLUSIVE flag is set.
553 @see is_tls_ingress_okay()
554 @see tls_do_handshake()
555 @see TLS_NO_EGRESS
556 *///-------------------------------------------------------------------------
557 TLS_NO_INGRESS = 2,
558
559 /*----------------------------------------------------------------------*//**
560 Mid-stream downgrades to unencrypted connections are not permitted (e.g., via
561 application-level initiations like a hypothetical @c STOPTLS command as seen
562 in FTPS), which will also cause calls to the @ref tls() method to fail fast
563 after encrypted data has already been sent or received.
564 @pre
565 This flag may be combined with the @ref TLS_EXCLUSIVE flag to prevent a
566 connection from being downgraded programatically.
567 @note
568 Although egress to an unencrypted connection doesn't occur automatically
569 (since egress can only be affected programatically to support commands at the
570 application level), this flag is useful to prevent third-party code from
571 downgrading an encrypted @ref rsocket to unencrypted.
572 @warning
573 Supporting unencrypted communications is strongly discouraged over public
574 networks (e.g., the internet) because unencrypted streams are trivially
575 susceptible to man-in-the-middle attacks that can alter the contents of the
576 data in both directions (which is a particularly dangerous prospect for
577 sending/receiving sensitive information).
578 @see is_tls_egress_okay()
579 @see TLS_NO_INGRESS
580 *///-------------------------------------------------------------------------
581 TLS_NO_EGRESS = 4,
582
583 /*----------------------------------------------------------------------*//**
584 This is a convenience flag that provides an option for developers to be more
585 clear in their use of the @ref tls() and @ref tls_ctx() methods to indicate
586 intent to rely on what is already the default.
587 @see tls_do_handshake()
588 @see TLS_SERVER
589 *///-------------------------------------------------------------------------
590 TLS_CLIENT = 0,
591
592 /*----------------------------------------------------------------------*//**
593 Indicates that this rsocket will be for a server daemon, and to initialize a
594 new TLS context (when one isn't being provided) using OpenSSL's
595 @c TLS_server_method() function instead of OpenSSL's @c TLS_client_method()
596 function (the latter is the default because most code is anticipated to be
597 client-oriented).
598
599 @attention
600 Setting the CLIENT/SERVER flag incorrectly will typically cause the following
601 error that's difficult to track down, which is usually triggered by calling
602 @ref accept, @ref accept4(), or @ref connect(), and so I hope that including
603 this information here in this documentation will be helpful:
604 @verbatim
605 error:140C5042:SSL routines:ssl_undefined_function:called a function you should not call
606 @endverbatim
607
608 The absence of this flag has the same effect as specifying the @ref
609 TLS_CLIENT flag.
610 @see TLS_CLIENT
611 *///-------------------------------------------------------------------------
612 TLS_SERVER = 8,
613
614 }; // -x- enum TLS_FLAGS -x-
615
616 private:
617 /*======================================================================*//**
618 Return Code check, and throws an rsocket-specific exception if an error
619 occurred, which is indicated by @c -1 (a lot of example code shows @c < @c 1
620 comparisons, but we specifically test for @c -1 because the documentation
621 clearly states @c -1. This is important because a system that can support an
622 extremely high number of socket handles might, in theory, assign handles with
623 values that get interpreted as negative integers, and so less-than-zero tests
624 would result in dropped packets or dropped sockets (any such socket code that
625 allocates such handles obviously must not ever allocate @c -1 since this
626 would definitely be misinterpreted as an error).
627
628 If rc is not @c -1, then it is simply returned as is.
629
630 If n is nonzero and rc is 0, then n will override errno. (This option is
631 provided to accomodate the few socket library functions that return values
632 that are never errors, and expect the developer to rely on other means of
633 detecting whether an error occurred. This is an example of where Object
634 Oriented Programming is helpful in making things better.)
635 @returns Original value of @c rc (if no exceptions were thrown)
636 *///=========================================================================
637 const int __rc_check(
638 /// Return code
639 const int rc,
640 /// Override @c errno (if not 0)
641 const int n = 0,
642 /// Exception handling flags (see @ref rex::REX_FLAGS for details)
643 const int flags = rex::rex::REX_FLAGS::REX_DEFAULT) {
644 if (rc == -1 || (rc == 0 && n != 0)) {
645 const int socket_errno = n == 0 ? errno : n; // If "n" is not zero, use it instead
646 if (__debug) debug("__rc_check(" + std::to_string(rc) + ", " + std::to_string(n) + ", " + std::to_string(flags) + ") throwing exception (errno=" + std::to_string(socket_errno) + ")"); // TODO: Remove this
647 randolf::rex::mk_exception("", socket_errno, flags); // This function doesn't return (this code ends here)
648 } // -x- if rc -x-
649 return rc;
650 }; // -x- int __rc_check -x-
651
652 /*======================================================================*//**
653 Similar to @ref __rc_check(), but specialized for handling OpenSSL return
654 codes.
655 *///=========================================================================
656 const int __rc_check_tls(
657 /// Return code (from OpenSSL's API functions)
658 const int rc) {
659 if (rc <= 0) {
660 if (__debug) debug("__rc_check_tls(" + std::to_string(rc) + ") throwing exception"); // TODO: Remove this
661 randolf::rex::mk_exception("", SSL_get_error(__tls_fd, rc), rex::rex::REX_FLAGS::REX_TLS); // This function doesn't return (this code ends here)
662 } // -x- if rc -x-
663 return rc;
664 }; // -x- int __rc_check -x-
665
666 /*======================================================================*//**
667 Internal function that opens the socket. This is used by the constructors
668 and their accompanying socket() methods.
669
670 Throws randolf::rex::xEALREADY exception if the socket is already open to
671 prevent multiple calls to @c open, which can be particularly problematic if
672 any of the additional function calls specify different parameters -- this
673 helps developer(s) to avoid unexpected results if they're inadvertently using
674 the same rsocket object when they intend to use different rsocket objects.
675 *///=========================================================================
676 void __socket(
677 /// Communication domain; usually one of:@n
678 /// AF_INET (IPv4)@n
679 /// AF_INET6 (IPv6)@n
680 /// AF_UNIX (UNIX domain sockets)
681 const int family,
682 /// Communication semantics; usually one of:@n
683 /// SOCK_STREAM (common for TCP)@n
684 /// SOCK_DGRAM (common for UDP)
685 const int type,
686 /// Network protocol; usually one of:@n
687 /// IPPROTO_TCP@n
688 /// IPPROTO_UDP@n
689 /// IPPROTO_IP
690 const int protocol) {
691 if (__debug) debug("socket(" + std::to_string(family)
692 + ", " + std::to_string(type)
693 + ", " + std::to_string(protocol)
694 + ");");
695
696 // --------------------------------------------------------------------------
697 // Syntax checks.
698 // --------------------------------------------------------------------------
699 if (__socket_open) throw randolf::rex::xEALREADY("EALREADY: Socket is already open"); // Socket has already been opened
700 if (family == AF_UNSPEC) throw randolf::rex::xEAFNOSUPPORT("EAFNOSUPPORT: AF_UNSPEC family (SO_DOMAIN) is not supported by socket()");
701
702 // --------------------------------------------------------------------------
703 // Build minimum parts of __socket_addr structure.
704 // --------------------------------------------------------------------------
705 __socket_addr.ss_family = family;
706 __socket_type = type;
707 __socket_protocol = protocol;
708
709 // --------------------------------------------------------------------------
710 // Create new socket handle, and save it, then set internal variable to
711 // indicate that the socket is open.
712 // --------------------------------------------------------------------------
713 __socket_fd = __rc_check(::socket(__socket_addr.ss_family, __socket_type, __socket_protocol));
714 __socket_open = true;
715
716 }; // -x- void __socket -x-
717
718 public:
719 /*======================================================================*//**
720 @brief
721 Instantiate an empty rsocket without opening said socket, and without
722 throwing any exceptions.
723
724 @details
725 Instantiating an empty rsocket is particularly useful for header-file
726 definitions since exceptions can't be handled outside of subroutines, and
727 it's also useful for enabling debug() mode @em before setting the socket's
728 configuration with one of the socket() methods; for example:
729 @code{.cpp}
730 randolf::rsocket r; // Empty rsocket (empty / incomplete initialization)
731 r.debug(true); // Enable debug mode
732 r.socket(...); // Required to complete rsocket initialization
733 @endcode
734
735 @par Notes
736
737 The built-in defaults, when not provided, are as follows ("family" is also
738 known as the "communication domain"):
739 - @c family = AF_INET
740 - @c type = SOCK_STREAM
741 - @c protocol = PF_UNSPEC
742
743 You will need to use one of the socket(...) methods to specify socket details
744 after defining rsocket objects with empty constructors so that you can catch
745 runtime exceptions. (This also provides you with an option to enable debug
746 mode during runtime prior to attempting to open an rsocket.)
747
748 @par Examples
749
750 @code{.cpp}
751 #include <iostream> // std::cout, std::cerr, std::endl, etc.
752 #include <randolf/rex>
753 #include <randolf/rsocket>
754
755 randolf::rsocket r; // <-- Empty rsocket initialization (no exceptions)
756
757 int main(int argc, char *argv[]) {
758 try {
759 r.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Required to complete rsocket initialization
760 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
761 // ... other socket I/O operations
762 r.close();
763 } catch (const randolf::rex::xALL e) {
764 std::cerr << "Socket exception: " << e.what() << std::endl;
765 return EXIT_FAILURE;
766 } catch (const std::exception e) {
767 std::cerr << "Other exception: " << e.what() << std::endl;
768 return EXIT_FAILURE;
769 }
770 return EXIT_SUCCESS;
771 } // -x- int main -x-
772 @endcode
773 @see rsocket()
774 @see socket()
775 @see socket_family()
776 @see socket_fd()
777 @see socket_protocol()
778 @see socket_type()
779 @qualifier TLS
780 *///=========================================================================
781 rsocket() noexcept {}; // -x- constructor rsocket -x-
782
783 /*======================================================================*//**
784 @brief
785 Instantiate an rsocket based on a minimal subset of the settings in the
786 specified rsocket (using it as a template), without actually opening a
787 socket, and therefore also without throwing any exceptions.
788
789 @note
790 This constructor does not suffice as a full clone()-like operation, and is
791 minimal because it's used internally by the @ref accept() and @ref accept4()
792 methods.
793
794 Details that are absorbed from the template/source rsocket (which eliminates
795 the need to assign, set, and configure various parameters (TLS and TLS SNI
796 parameters will be copied in a passive way by default):
797 - Socket family (SO_DOMAIN)
798 - Socket type (SO_TYPE)
799 - Socket protocol (SO_PROTOCOL)
800 - TLS details (status, context {which includes loaded certificates},
801 policies specified with @ref TLS_FLAGS, etc.)
802 - EoL details
803 - TLS context (you'll still need to call `tls_ctx(r->tls_ctx())`)
804 - TLS SNI map (you'll still need to call `tls_sni(r->tls_sni())`)
805
806 @post
807 The TLS Context will not be initialized because it needs a real socket to
808 draw from. If using TLS, you'll need to use the @ref tls() method after
809 the underlying socket has been initiated.
810
811 When @c flag_create_socket is set to FALSE, no exceptions will be thrown.
812
813 @see rsocket()
814 @see socket()
815 @see socket_family()
816 @see socket_fd()
817 @see socket_protocol()
818 @see socket_type()
819 @qualifier TLS
820 *///=========================================================================
821 rsocket(
822 /// Source rsocket object to use as a template to absorb settings from
823 const rsocket* rtemplate,
824 /// TRUE = create a new socket handle (default)@n
825 /// FALSE = don't create a new socket because a new one will be assigned or
826 /// created later (all variants of the @ref accept() methods do this)
827 const bool flag_create_socket = true) {
828
829 // --------------------------------------------------------------------------
830 // General socket variables.
831 // --------------------------------------------------------------------------
832 if (flag_create_socket) {
833 __socket(rtemplate->__socket_addr.ss_family,
834 rtemplate->__socket_type,
835 rtemplate->__socket_protocol);
836 } else { // !flag_create_socket
837 __socket_addr.ss_family = rtemplate->__socket_addr.ss_family;
838 __socket_type = rtemplate->__socket_type;
839 __socket_protocol = rtemplate->__socket_protocol;
840 } // -x- if flag_create_socket -x-
841 __name = rtemplate->__name;
842
843 // --------------------------------------------------------------------------
844 // TLS and SNI settings, but not whether TLS is enabled.
845 // --------------------------------------------------------------------------
846 __tls_ctx = rtemplate->__tls_ctx;
847 __tls_sni = rtemplate->__tls_sni;
848
849 // --------------------------------------------------------------------------
850 // EoL variables.
851 // --------------------------------------------------------------------------
852 __eol.assign( rtemplate->__eol); // Copy source std::string's contents (not a reference)
853 __eol_fix_printf = rtemplate->__eol_fix_printf;
854
855 }; // -x- constructor rsocket -x-
856
857 /*======================================================================*//**
858 @brief
859 Instantiate an rsocket with IP/host address and [optional] port number.
860
861 This is either the endpoint that our underlying socket will be connecting to,
862 or it's the local address of the server daemon that our socket will listen()
863 to and accept() inbound connections from.
864
865 @par Notes
866
867 The built-in defaults, when not provided, are as follows ("family" is also
868 known as the "communication domain"):
869 - @c family = AF_INET
870 - @c type = SOCK_STREAM
871 - @c protocol = PF_UNSPEC
872
873 The socket() methods do the same work as the constructors with matching
874 arguments, and are provided as convenience methods intended to augment
875 empty rsocket constructors used in header files, but do require an address to
876 be specified (for protocols that need port numbers, such as TCP or UDP, a
877 "port" number also needs to be specified since the default port 0 will result
878 in the dynamic allocation of a port number by the system).
879
880 For UNIX domain sockets use family AF_UNIX, type SOCK_STREAM, and protocol
881 IPPROTO_IP when instantiating or opening an rsocket.
882
883 @par Examples
884
885 @code{.cpp}
886 #include <iostream> // std::cout, std::cerr, std::endl, etc.
887 #include <randolf/rex>
888 #include <randolf/rsocket>
889
890 int main(int argc, char *argv[]) {
891 try {
892 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Same as socket() method
893 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
894 r.bind("127.0.0.1", 32768);
895 // ... other socket I/O operations
896 r.close();
897 } catch (const randolf::rex::xALL e) {
898 std::cerr << "Socket exception: " << e.what() << std::endl;
899 return EXIT_FAILURE;
900 } catch (const std::exception e) {
901 std::cerr << "Other exception: " << e.what() << std::endl;
902 return EXIT_FAILURE;
903 }
904 return EXIT_SUCCESS;
905 } // -x- int main -x-
906 @endcode
907
908 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
909 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
910 @throws randolf::rex::xEINVAL Protocal family invalid or not available
911 @throws randolf::rex::xEINVAL Invalid flags in type
912 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
913 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
914 @throws randolf::rex::xENOBUFS Insufficient memory
915 @throws randolf::rex::xENOMEM Insufficient memory
916 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
917 supported within the specified family (a.k.a., communication domain)
918 @see rsocket()
919 @see socket()
920 @qualifier TLS
921 *///=========================================================================
922 rsocket(
923 /// Communication domain; usually one of:@n
924 /// AF_INET (IPv4)@n
925 /// AF_INET6 (IPv6)@n
926 /// AF_UNIX (UNIX domain sockets)
927 const int family,
928 /// Communication semantics; usually one of:@n
929 /// SOCK_STREAM (common for TCP)@n
930 /// SOCK_DGRAM (common for UDP)
931 const int type = SOCK_STREAM,
932 /// Network protocol; usually one of:@n
933 /// IPPROTO_TCP@n
934 /// IPPROTO_UDP@n
935 /// IPPROTO_IP@n
936 /// PF_UNSPEC (auto-detect)
937 const int protocol = PF_UNSPEC) {
938 __socket(family, type, protocol);
939 }; // -x- constructor rsocket -x-
940
941 /*======================================================================*//**
942 @brief
943 Destructor, which closes any underlying sockets, frees any TLS structures
944 that were allocated by OpenSSL, and performs any other necessary clean-up
945 before finally copying the I/O statistics to a designated structure (if one
946 was specified with the @ref net_io_final() method).
947 @attention
948 Developers should take care to check that the @ref rsocket_io::is_final flag
949 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
950 since there's no guarantee that the destructor will necessarily be executed
951 in a timely manner (this flag is set last, after all other statistics are
952 copied into the structure while in a mutex-locked state).
953 @see net_io_final()
954 @qualifier TLS
955 *///=========================================================================
956 ~rsocket() noexcept {
957
958 // --------------------------------------------------------------------------
959 // Debug.
960 // --------------------------------------------------------------------------
961 if (__debug) {
962 debug("destructor(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
963 + ");");
964 debug(false); // Disable debug mode (might trigger extra clean-up in future)
965 } // -x- if _debug -x-
966
967 // --------------------------------------------------------------------------
968 // Free memory and resources and close handles that need to be freed/closed.
969 // --------------------------------------------------------------------------
970 if (__tls_fd != nullptr) SSL_free(__tls_fd);
971// if (__tls_sni != nullptr) tls_sni(nullptr); // Remove any SNI callbacks // WE DON'T NEED THIS
972
973// if (__tls_ctx != nullptr) SSL_CTX_free(__tls_ctx); // TODO: Research using SSL_up_ref(__tls_ctx)
974 if (__socket_fd != 0) ::close(__socket_fd);
975 if (__buffer != nullptr) ::free(__buffer);
976// if (__buffer != nullptr) delete []__buffer;
977
978 // --------------------------------------------------------------------------
979 // Copy statistics to final location. (We do this last in case there's an
980 // issue with locking; there shouldn't be, but if there is then at least we
981 // already we're not inadvertently hanging on to resources at this point that
982 // need to be freed/closed anyway.)
983 // --------------------------------------------------------------------------
984 if (__io_final_addr != nullptr) {
985 __io_final_addr->lock();
986 __io_final_addr->bytes_rx = __bytes_rx;
987 __io_final_addr->bytes_tx = __bytes_tx;
988 // ->bytes_xx = <bytes in buffer; these are lost bytes that should be subtracted from __bytes_rx>
989 __io_final_addr->crypt_rx = __crypt_rx;
990 __io_final_addr->crypt_tx = __crypt_tx;
991 // ->crypt_xx = <bytes in buffer; these are lost bytes that should be subtracted from __crypt_rx>
992 __io_final_addr->is_final = true;
993 __io_final_addr->unlock();
994 } // -x- if __io_final_addr -x-
995
996 }; // -x- destructor ~rsocket -x-
997
998 /*======================================================================*//**
999 @brief
1000 Accept new [inbound] socket connetions. (This is typically used in a loop.)
1001
1002 @pre
1003 The resulting rsocket object is created before the actual call to the @c
1004 accept() function.
1005
1006 @note
1007 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1008 select(), and accept()/accept4() when using multiple sockets.
1009
1010 @post
1011 To prevent resource leaks, the resulting rsocket needs to be deleted after
1012 it's no longer needed.
1013
1014 @throws randolf::rex::xEBADF The underlying socket is not open
1015 @throws randolf::rex::xECONNABORTED The connection was aborted
1016 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1017 part of the user address space
1018 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1019 @throws randolf::rex::xEHOSTUNREACH No route to host
1020 @throws randolf::rex::xEINTR Interrupted by a signal
1021 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1022 length of the address is invalid
1023 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1024 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1025 @throws randolf::rex::xENETUNREACH No route to network
1026 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1027 @throws randolf::rex::xENOBUFS Insufficient memory
1028 @throws randolf::rex::xENOMEM Insufficient memory
1029 @throws randolf::rex::xENONET Requested host is not reachable on the network
1030 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1031 is not supported
1032 @throws randolf::rex::xENOSR System ran out of stream resources
1033 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1034 doesn't refer to a socket
1035 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1036 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1037 @throws randolf::rex::xEPROTO Protocol error
1038 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1039 supported within the specified family (a.k.a., communication domain)
1040 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1041 a system call is restartable and can be intercepted-and-redirected
1042 (there is no need to catch this exception unless you are an advanced
1043 developer, in which case you likely still won't need to catch it in
1044 code at this level)
1045 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1046 supported within the specified family (a.k.a., communication domain)
1047 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1048 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1049 no inbound connections are waiting
1050
1051 @returns Newly-created rsocket object representing the connection received
1052 @see accept_sp()
1053 @see accept4()
1054 @see accept4_sp()
1055 @see listen
1056 @qualifier POSIX
1057 @qualifier TLS
1058 *///=========================================================================
1059 rsocket* accept() {
1060 if (__debug) debug("accept(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1061 + ");");
1062
1063 // --------------------------------------------------------------------------
1064 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1065 // new incoming connection.
1066 // --------------------------------------------------------------------------
1067 __tls_new_endpoint = new rsocket(this, false);
1068
1069 // --------------------------------------------------------------------------
1070 // Wait for incoming connection, and update internal flags in client rsocket.
1071 // --------------------------------------------------------------------------
1072 __tls_new_endpoint->__socket_fd = ::accept(__socket_fd, (sockaddr*)&__tls_new_endpoint->__socket_addr, &__tls_new_endpoint->__socket_addr_size);
1073 if (__tls_new_endpoint->__socket_fd == -1) {
1074 delete __tls_new_endpoint; // Memory management
1075 //__tls_new_endpoint = nullptr;
1076 __rc_check(-1); // This part throws the exception
1077 } // -x- if rc -x-
1078 __tls_new_endpoint->__socket_open = true;
1079 __tls_new_endpoint->__socket_connected = true;
1080
1081 // --------------------------------------------------------------------------
1082 // Perform TLS accept() as well, but only if TLS is enabled.
1083 //
1084 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1085 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1086 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1087 // --------------------------------------------------------------------------
1088 if (__tls) {
1089 __tls_new_endpoint->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1090 __tls_new_endpoint->tls(true); // Make sure __tls_fd is configured correctly
1091 __rc_check_tls(SSL_accept(__tls_new_endpoint->__tls_fd));
1092 } // -x- if __tls -x-
1093
1094 return __tls_new_endpoint;
1095 }; // -x- rsocket* accept -x-
1096
1097 /*======================================================================*//**
1098 @brief
1099 Accept new [inbound] socket connetions. (This is typically used in a loop.)
1100
1101 @pre
1102 The resulting rsocket object is created before the actual call to the @c
1103 accept() function.
1104
1105 @note
1106 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1107 select(), and accept()/accept4() when using multiple sockets.
1108
1109 @post
1110 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1111 aids in the prevention of resource leaks).
1112
1113 @throws randolf::rex::xEBADF The underlying socket is not open
1114 @throws randolf::rex::xECONNABORTED The connection was aborted
1115 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1116 part of the user address space
1117 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1118 @throws randolf::rex::xEHOSTUNREACH No route to host
1119 @throws randolf::rex::xEINTR Interrupted by a signal
1120 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1121 length of the address is invalid
1122 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1123 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1124 @throws randolf::rex::xENETUNREACH No route to network
1125 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1126 @throws randolf::rex::xENOBUFS Insufficient memory
1127 @throws randolf::rex::xENOMEM Insufficient memory
1128 @throws randolf::rex::xENONET Requested host is not reachable on the network
1129 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1130 is not supported
1131 @throws randolf::rex::xENOSR System ran out of stream resources
1132 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1133 doesn't refer to a socket
1134 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1135 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1136 @throws randolf::rex::xEPROTO Protocol error
1137 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1138 supported within the specified family (a.k.a., communication domain)
1139 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1140 a system call is restartable and can be intercepted-and-redirected
1141 (there is no need to catch this exception unless you are an advanced
1142 developer, in which case you likely still won't need to catch it in
1143 code at this level)
1144 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1145 supported within the specified family (a.k.a., communication domain)
1146 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1147 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1148 no inbound connections are waiting
1149
1150 @returns Newly-created rsocket object representing the connection received,
1151 wrapped in std::shared_ptr (a C++ smart pointer)
1152 @see accept()
1153 @see accept4()
1154 @see accept4_sp()
1155 @see listen
1156 @qualifier TLS
1157 *///=========================================================================
1158 std::shared_ptr<rsocket> accept_sp() {
1159 if (__debug) debug("accept_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1160 + ");");
1161
1162 // --------------------------------------------------------------------------
1163 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1164 // new incoming connection.
1165 // --------------------------------------------------------------------------
1166 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1167 __tls_new_endpoint = r.get(); // The SNI callback needs this
1168
1169 // --------------------------------------------------------------------------
1170 // Wait for incoming connection, and update internal flags in client rsocket.
1171 // --------------------------------------------------------------------------
1172 r->__socket_fd = __rc_check(::accept(__socket_fd, (sockaddr*)&r->__socket_addr, &r->__socket_addr_size));
1173 r->__socket_open = true;
1174 r->__socket_connected = true;
1175
1176 // --------------------------------------------------------------------------
1177 // Perform TLS accept() as well, but only if TLS is enabled.
1178 //
1179 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1180 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1181 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1182 // --------------------------------------------------------------------------
1183 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1184 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1185 r->tls(true); // Make sure __tls_fd is configured correctly
1186 __rc_check_tls(SSL_accept(r->__tls_fd));
1187 } // -x- if __tls -x-
1188
1189 return r;
1190 }; // -x- std::shared_ptr<rsocket> accept_sp -x-
1191
1192 /*======================================================================*//**
1193 @brief
1194 Accept new [inbound] socket connetions, with socket flags specified. (This
1195 is typically used in a loop.)
1196
1197 @pre
1198 The resulting rsocket object is created before the actual call to the @c
1199 accept4() function.
1200
1201 @note
1202 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1203 select(), and accept()/accept4() when using multiple sockets.
1204
1205 @post
1206 To prevent resource leaks, the resulting rsocket needs to be deleted after
1207 it's no longer needed.
1208
1209 @throws randolf::rex::xEBADF The underlying socket is not open
1210 @throws randolf::rex::xECONNABORTED The connection was aborted
1211 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1212 part of the user address space
1213 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1214 @throws randolf::rex::xEHOSTUNREACH No route to host
1215 @throws randolf::rex::xEINTR Interrupted by a signal
1216 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1217 length of the address is invalid
1218 @throws randolf::rex::xEINVAL Invalid value in flags
1219 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1220 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1221 @throws randolf::rex::xENETUNREACH No route to network
1222 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1223 @throws randolf::rex::xENOBUFS Insufficient memory
1224 @throws randolf::rex::xENOMEM Insufficient memory
1225 @throws randolf::rex::xENONET Requested host is not reachable on the network
1226 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1227 is not supported
1228 @throws randolf::rex::xENOSR System ran out of stream resources
1229 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1230 doesn't refer to a socket
1231 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1232 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1233 @throws randolf::rex::xEPROTO Protocol error
1234 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1235 supported within the specified family (a.k.a., communication domain)
1236 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1237 a system call is restartable and can be intercepted-and-redirected
1238 (there is no need to catch this exception unless you are an advanced
1239 developer, in which case you likely still won't need to catch it in
1240 code at this level)
1241 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1242 supported within the specified family (a.k.a., communication domain)
1243 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1244 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1245 no inbound connections are waiting
1246
1247 @returns Newly-created rsocket object representing the connection received
1248 @see accept()
1249 @see accept_sp()
1250 @see accept4_sp()
1251 @see listen
1252 @qualifier POSIX
1253 @qualifier TLS
1254 *///=========================================================================
1255 rsocket* accept4(
1256 /// SOCK_NONBLOCK@n SOCK_CLOEXEC
1257 const int flags = 0) {
1258 if (__debug) debug("accept4(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1259 + ", " + std::to_string(flags)
1260 + ");");
1261
1262 // --------------------------------------------------------------------------
1263 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1264 // new incoming connection.
1265 // --------------------------------------------------------------------------
1266 rsocket* __tls_new_endpoint = new rsocket(this, false);
1267
1268 // --------------------------------------------------------------------------
1269 // Wait for incoming connection, and update internal flags in client rsocket.
1270 // --------------------------------------------------------------------------
1271 __tls_new_endpoint->__socket_fd = ::accept4(__socket_fd, (sockaddr*)&__tls_new_endpoint->__socket_addr, &__tls_new_endpoint->__socket_addr_size, flags);
1272 if (__tls_new_endpoint->__socket_fd == -1) {
1273 delete __tls_new_endpoint; // Memory management
1274 __rc_check(-1); // This part throws the exception
1275 } // -x- if rc -x-
1276 __tls_new_endpoint->__socket_open = true;
1277 __tls_new_endpoint->__socket_connected = true;
1278
1279 // --------------------------------------------------------------------------
1280 // Perform TLS accept() as well, but only if TLS is enabled.
1281 //
1282 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1283 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1284 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1285 // --------------------------------------------------------------------------
1286 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1287 __tls_new_endpoint->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1288 __tls_new_endpoint->tls(true); // Make sure __tls_fd is configured correctly
1289 __rc_check_tls(SSL_accept(__tls_new_endpoint->__tls_fd));
1290 } // -x- if __tls -x-
1291
1292 return __tls_new_endpoint;
1293 }; // -x- rsocket* accept4 -x-
1294
1295 /*======================================================================*//**
1296 @brief
1297 Accept new [inbound] socket connetions, with socket flags specified. (This
1298 is typically used in a loop.)
1299
1300 @pre
1301 The resulting rsocket object is created before the actual call to the @c
1302 accept4() function.
1303
1304 @note
1305 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1306 select(), and accept()/accept4() when using multiple sockets.
1307
1308 @post
1309 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1310 aids in the prevention of resource leaks).
1311
1312 @throws randolf::rex::xEBADF The underlying socket is not open
1313 @throws randolf::rex::xECONNABORTED The connection was aborted
1314 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1315 part of the user address space
1316 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1317 @throws randolf::rex::xEHOSTUNREACH No route to host
1318 @throws randolf::rex::xEINTR Interrupted by a signal
1319 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1320 length of the address is invalid
1321 @throws randolf::rex::xEINVAL Invalid value in flags
1322 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1323 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1324 @throws randolf::rex::xENETUNREACH No route to network
1325 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1326 @throws randolf::rex::xENOBUFS Insufficient memory
1327 @throws randolf::rex::xENOMEM Insufficient memory
1328 @throws randolf::rex::xENONET Requested host is not reachable on the network
1329 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1330 is not supported
1331 @throws randolf::rex::xENOSR System ran out of stream resources
1332 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1333 doesn't refer to a socket
1334 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1335 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1336 @throws randolf::rex::xEPROTO Protocol error
1337 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1338 supported within the specified family (a.k.a., communication domain)
1339 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1340 a system call is restartable and can be intercepted-and-redirected
1341 (there is no need to catch this exception unless you are an advanced
1342 developer, in which case you likely still won't need to catch it in
1343 code at this level)
1344 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1345 supported within the specified family (a.k.a., communication domain)
1346 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1347 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1348 no inbound connections are waiting
1349
1350 @returns Newly-created rsocket object representing the connection received,
1351 wrapped in std::shared_ptr (a C++ smart pointer)
1352 @see accept()
1353 @see accept_sp()
1354 @see accept4()
1355 @see listen
1356 @qualifier TLS
1357 *///=========================================================================
1358 std::shared_ptr<rsocket> accept4_sp(
1359 /// SOCK_NONBLOCK@n SOCK_CLOEXEC
1360 const int flags = 0) {
1361 if (__debug) debug("accept4_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1362 + ", " + std::to_string(flags)
1363 + ");");
1364
1365 // --------------------------------------------------------------------------
1366 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1367 // new incoming connection.
1368 // --------------------------------------------------------------------------
1369 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1370 __tls_new_endpoint = r.get(); // The SNI callback needs this
1371
1372 // --------------------------------------------------------------------------
1373 // Wait for incoming connection, and update internal flags in client rsocket.
1374 // --------------------------------------------------------------------------
1375 r->__socket_fd = __rc_check(::accept4(__socket_fd, (sockaddr*)&r->__socket_addr, &r->__socket_addr_size, flags));
1376 r->__socket_open = true;
1377 r->__socket_connected = true;
1378
1379 // --------------------------------------------------------------------------
1380 // Perform TLS accept() as well, but only if TLS is enabled.
1381 //
1382 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1383 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1384 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1385 // --------------------------------------------------------------------------
1386 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1387 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1388 r->tls(true); // Make sure __tls_fd is configured correctly
1389 __rc_check_tls(SSL_accept(r->__tls_fd));
1390 } // -x- if __tls -x-
1391// if (__tls_ctx != nullptr) { r->tls(true); r->__tls = (bool)__tls; } // Make sure __tls_fd is allocated appropriately
1392
1393 return r;
1394 }; // -x- std::shared_ptr<rsocket> accept4_sp -x-
1395
1396 /*======================================================================*//**
1397 @brief
1398 Override the default @ref listen backlog for this rsocket.
1399
1400 @note
1401 The default backlog is SOMAXCONN (4096 on Linux, and 128 on older systems).
1402
1403 @returns The same rsocket object so as to facilitate stacking
1404 @see listen
1405 @qualifier TLS
1406 *///=========================================================================
1407 rsocket* backlog(
1408 /// Backlog queue size (0 = use SOMAXCONN, which is the system default of
1409 /// 4096 on Linux, and 128 on older systems)
1410 int backlog = 0) noexcept {
1411 __socket_backlog = backlog == 0 ? SOMAXCONN : backlog;
1412 return this;
1413 }; // -x- rsocket* backlog -x-
1414
1415 /*======================================================================*//**
1416 @brief
1417 Find out what this rsocket's default listen backlog is.
1418 @returns The default listen backlog
1419 @see listen
1420 @qualifier TLS
1421 *///=========================================================================
1422 int backlog() noexcept {
1423 return __socket_backlog;
1424 }; // -x- int backlog -x-
1425
1426 /*======================================================================*//**
1427 @brief
1428 Bind this socket to the specified network address (and port number if the
1429 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1430 used for server deamons, but can also be used to control the address from
1431 which the local host will make an outbound connection via the @ref connect()
1432 method (this bound address is the address from which the endpoint will
1433 recieve your connection).
1434
1435 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
1436 (or 1023 on some Operating Systems that test only for below 1024) in
1437 the absence of elevated access or the absence of a capability flag
1438 having been set
1439 @throws randolf::rex::xEACCES If binding to an interface on systems that
1440 require elevated access for direct interface binding in absence of
1441 said elevated access
1442 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1443 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
1444 address is not available on any local interface
1445 @throws randolf::rex::xEBADF The underlying socket is not open
1446 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1447 part of the user address space
1448 @throws randolf::rex::xEINVAL Socket is already bound
1449 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
1450 valid for this socket's family (a.k.a., communication domain)
1451 @throws randolf::rex::xENOMEM Insufficient memory
1452 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1453 doesn't refer to a socket
1454 @n@n
1455 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1456 @throws randolf::rex::xEACCES Write permission or search permission denied
1457 on a component of the path prefix (@c AF_UNIX family)
1458 @throws randolf::rex::xELOOP Too many symbolic links encountered while
1459 resolving address (@c AF_UNIX family)
1460 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
1461 @throws randolf::rex::xENOENT One of the path's directory components doesn't
1462 exist (@c AF_UNIX family)
1463 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
1464 directory (@c AF_UNIX family)
1465 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
1466
1467 @returns The same rsocket object so as to facilitate stacking
1468 @see bind(std::string, int)
1469 @see connect
1470 @see listen
1471 @qualifier POSIX
1472 @qualifier TLS
1473 *///=========================================================================
1474 rsocket* bind(
1475 /// Socket address structure
1476 const struct sockaddr* addr,
1477 /// Length of socket structure
1478 const socklen_t addrlen = sizeof(sockaddr)) {
1479 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1480 + ", <sockaddr*>"
1481 + ", size=" + std::to_string(addrlen)
1482 + ");");
1483 if (addrlen > sizeof(sockaddr_storage)) randolf::rex::mk_exception("addlen is too large for bind()", EINVAL);
1484 __rc_check(::bind(__socket_fd, addr, addrlen));
1485 std::memcpy(&__socket_addr, addr, addrlen); // No errors were returned by bind(), so now we'll copy addr to __socket_addr
1486 return this;
1487 } // -x- rsocket* bind -x-
1488
1489 //===========================================================================
1490 //
1491 // TODO: Document usage of IP_BIND_ADDRESS_NO_PORT to support connecting
1492 // from a specific IP address without losing ephemeral port selection.
1493 // Notes: https://idea.popcount.org/2014-04-03-bind-before-connect/
1494 //
1495 // TODO: Support AF_BLUETOOTH addresses
1496 // Reference:
1497 // https://man7.org/linux/man-pages/man7/address_families.7.html
1498 //
1499 // TODO: Support AF_IPX addresses
1500 // Reference:
1501 // https://man7.org/linux/man-pages/man7/address_families.7.html
1502 //
1503 // TODO: Support AF_APPLETALK addresses
1504 // Reference: https://man7.org/linux/man-pages/man7/ddp.7.html
1505 //
1506 // TODO: Support AF_PACKET addresses
1507 // Reference: https://man7.org/linux/man-pages/man7/packet.7.html
1508 //
1509 // TODO: Support AF_X25 addresses
1510 // Reference: https://man7.org/linux/man-pages/man7/x25.7.html
1511 //
1512 // TODO: Support AF_NETLINK addresses
1513 // Reference: https://man7.org/linux/man-pages/man7/netlink.7.html
1514 //
1515 /*======================================================================*//**
1516 @brief
1517 Bind this socket to the specified network address (and port number if the
1518 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1519 used for server deamons, but can also be used to control the address from
1520 which the local host will make an outbound connection via the @ref connect()
1521 method (this bound address is address from which the endpoint will recieve
1522 your connection).
1523
1524 In addition to the standard IPv4 (AF_INET family) and IPv6 (AF_INET6 family)
1525 support, rsocket also supports a few other binding options in a manner that
1526 makes it easy to utilize, without having to handle special implementation
1527 details (because they're handled behind-the-scenese by this rsocket class).
1528
1529 The address string supports a number of different notations and formats,
1530 which are documented, hereunder, with examples:
1531 - IPv4 addresses
1532 - IPv6 addresses
1533 - Network interfaces
1534 - UNIX domain sockets
1535
1536 <hr>
1537 @par IPv4 address notations (address family @c AF_INET)
1538 Takes the form of @c x.x.x.x where each "x" represents an octet in the range
1539 of @c 0 through @c 255 (e.g., @c 127.0.0.1).
1540
1541 Under a proper implenetation of TCP/IP, an IPv4 address can be abbreviated
1542 to fewer octets. The following examples demonstrate this (an unabbreviated
1543 IPv4 address is included for completeness):
1544 - @c 0.0.0.1 may be abbreviated to @c 1
1545 - @c 4.0.0.1 may be abbrevaited to @c 4.1
1546 - @c 4.3.0.1 may be abbreviated to @c 4.3.1
1547 - @c 4.3.2.1 is not abbreviated (this is the most common usage)
1548
1549 @par Example of binding to IPv4 localhost
1550
1551 @code{.cpp}
1552 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1553 #include <randolf/rex>
1554 #include <randolf/rsocket>
1555
1556 int main(int argc, char *argv[]) {
1557 try {
1558 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1559 r.bind("127.0.0.1", 32768); // <-- You are here
1560 // ... other socket I/O operations
1561 r.close();
1562 } catch (const randolf::rex::xEACCES e) {
1563 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1564 return EXIT_FAILURE;
1565 } catch (const randolf::rex::xALL e) {
1566 std::cerr << "Socket exception: " << e.what() << std::endl;
1567 return EXIT_FAILURE;
1568 } catch (const std::exception e) {
1569 std::cerr << "Other exception: " << e.what() << std::endl;
1570 return EXIT_FAILURE;
1571 }
1572 return EXIT_SUCCESS;
1573 } // -x- int main -x-
1574 @endcode
1575
1576 @note
1577 Specifying the IP address of @c 0.0.0.0 will bind to all IPv4 addresses that
1578 are assigned to all of the host machine's network interfaces with IPv4
1579 bindings.
1580 @n@n
1581 Specifying the IP address of @c 127.0.0.1 (localhost) is useful for serving
1582 only those applications that are running on the local host and use an IPv4
1583 socket to communicate.
1584
1585 <hr>
1586 @par IPv6 address notations (address family @c AF_INET6)
1587 Takes the form of @c x:x:x:x:x:x:x:x where each "x" represents a segment in
1588 the range of @c 0 through @c ffff in hexadecimal (e.g., `0:0:0:0:0:0:0:1`),
1589 or `x:x:x:x:x:x:y.y.y.y` where `y.y.y.y` represents an [unabbreviated] IPv4
1590 address (which merely replaces the last two IPv6 segments).
1591
1592 Under a proper implenetation of TCP/IP, an IPv6 address can be abbreviated
1593 to fewer segments by using a sequence of two colons (`::`) to indicate that
1594 the undefined segments are @c 0 (this abbreviation can only be used once,
1595 and may represent segments at the beginning or end, or anywhere in between).
1596 The following examples demonstrate this (an unabbreviated IPv6 address is
1597 included for completeness):
1598 - `0:0:0:0:0:0:0:1` may be abbreviated to `::1`
1599 - `0:0:0:0:0:0:2:1` may be abbreviated to `::2:1`
1600 - `8:0:0:0:0:0:2:1` may be abbreviated to `8::2:1`
1601 - `8:7:0:0:0:0:2:1` may be abbreviated to `8:7::2:1`
1602 - `8:7:0:0:0:0:0:0` may be abbreviated to `8:7::`
1603 - `8:0:0:0:0:0:0:0` may be abbreviated to `8::`
1604 - `8:7:6:5:4:3:2:1` is not abbreviated
1605
1606 @par Example of binding to IPv6 localhost
1607
1608 @code{.cpp}
1609 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1610 #include <randolf/rex>
1611 #include <randolf/rsocket>
1612
1613 int main(int argc, char *argv[]) {
1614 try {
1615 randolf::rsocket r(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1616 r.bind("::1", 32768); // <-- You are here
1617 // ... other socket I/O operations
1618 r.close();
1619 } catch (const randolf::rex::xEACCES e) {
1620 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1621 return EXIT_FAILURE;
1622 } catch (const randolf::rex::xALL e) {
1623 std::cerr << "Socket exception: " << e.what() << std::endl;
1624 return EXIT_FAILURE;
1625 } catch (const std::exception e) {
1626 std::cerr << "Other exception: " << e.what() << std::endl;
1627 return EXIT_FAILURE;
1628 }
1629 return EXIT_SUCCESS;
1630 } // -x- int main -x-
1631 @endcode
1632
1633 @note
1634 Specifying the IP address of @c :: will bind to all IPv6 addresses that are
1635 assigned to all of the host machine's network interfaces with IPv6 bindings.
1636 @n@n
1637 Specifying the IP address of @c ::1 (localhost) is useful for serving only
1638 those applications that are running on the local host and use an IPv6 socket
1639 to communicate.
1640
1641 <hr>
1642 @par Interface address notation (address families @c AF_INET and @c AF_INET6)
1643 Takes the form of @c if=name where "name" represents the name of a local
1644 network interface.
1645
1646 Interface binding is useful when binding to interfaces that aren't configured
1647 with a static IP address because they were dymamically configured via the
1648 Dynamic Host Configuration Protocol (DHCP) or Stateless Address Configuration
1649 (SLAAC), or the configuration was changed manually by an administrator and
1650 you don't want your software to handle such changes gracefully, even if the
1651 new IP address is within the scope of an entirely different CIDR. (Changing
1652 between the IPv4 and IPv6 addresses is not supported, however, due to the
1653 fundamental differences between these two address families that includes
1654 differences beyond that of IP address format, although under a proper
1655 implementation of TCP/IP the binding should survive such changes when the IP
1656 address is reverted to the initial IP address family of the bound interface.)
1657
1658 Examples of interfaces include:
1659 - `if=lo` typical for localhost virtual network adapter
1660 - `if=bond0` typical for the first bonded virtual network adapter (used in
1661 failover and load-balancing network configurations)
1662 - `if=br0` typical for the first bridge virtual network adapter
1663 - `if=eth0` typical for the first ethernet network adapter
1664 - `if=tap0` typical for the first virtual layer 2 ethernet switch (used by
1665 VPNs to extend a remote network into the local network stack)
1666 - `if=tun0` typical for the first virtual layer 3 ethernet tunnel (used by
1667 VPNs to provide access to a remote network)
1668 - `if=wlo1` typical for the first wireless network adapter
1669
1670 @par Example of binding to interface localhost
1671
1672 @code{.cpp}
1673 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1674 #include <randolf/rex>
1675 #include <randolf/rsocket>
1676
1677 int main(int argc, char *argv[]) {
1678 try {
1679 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1680 r.bind("if=lo", 32768); // <-- You are here
1681 // ... other socket I/O operations
1682 r.close();
1683 } catch (const randolf::rex::xEACCES e) {
1684 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1685 return EXIT_FAILURE;
1686 } catch (const randolf::rex::xALL e) {
1687 std::cerr << "Socket exception: " << e.what() << std::endl;
1688 return EXIT_FAILURE;
1689 } catch (const std::exception e) {
1690 std::cerr << "Other exception: " << e.what() << std::endl;
1691 return EXIT_FAILURE;
1692 }
1693 return EXIT_SUCCESS;
1694 } // -x- int main -x-
1695 @endcode
1696
1697 @note
1698 This is not a standard feature of the POSIX bind() function. This rsocket
1699 class uses the setsockopt() function behind-the-scenes to configure (enable)
1700 the @c SO_BINDTODEVICE option, and then the bind() function is called with
1701 the IPv4 address @c 0.0.0.0 if the underlying socket was initialized to the
1702 @c AF_INET family, or the IPv6 address @c :: if the underlying socket was
1703 initialized to the @c AF_INET6 family.
1704 @n@n
1705 Specifying the interface address of `if=lo` (localhost) is useful for serving
1706 only those applications that are running on the local host and use an IPv4 or
1707 IPv6 socket to communicate.
1708
1709 <hr>
1710 @par UNIX Domain Sockets address-notation (address family @c AF_UNIX)
1711 Takes the form of @c /path where "/path" represents an absolute path on the
1712 local file system (the path can also be relative, in which case it doesn't
1713 begin with a slash (`/`) character, but extra care is needed to ensure that
1714 the path will actually be at its expected location).
1715
1716 @par Example of binding to UNIX domain sockets
1717
1718 @code{.cpp}
1719 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1720 #include <randolf/rex>
1721 #include <randolf/rsocket>
1722
1723 int main(int argc, char *argv[]) {
1724 try {
1725 randolf::rsocket r(AF_UNIX, SOCK_STREAM);
1726 r.bind("/var/run/rsocket-test-socket.tmp"); // <-- You are here
1727 // ... other socket I/O operations
1728 r.close();
1729 } catch (const randolf::rex::xEACCES e) {
1730 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1731 return EXIT_FAILURE;
1732 } catch (const randolf::rex::xALL e) {
1733 std::cerr << "Socket exception: " << e.what() << std::endl;
1734 return EXIT_FAILURE;
1735 } catch (const std::exception e) {
1736 std::cerr << "Other exception: " << e.what() << std::endl;
1737 return EXIT_FAILURE;
1738 }
1739 return EXIT_SUCCESS;
1740 } // -x- int main -x-
1741 @endcode
1742
1743 @note
1744 Normal socket I/O functionality is used to exchange data with another process
1745 that can open the same UNIX domain socket (normally on the same host,
1746 although it may not be outside the realm of possibility for future Linux
1747 kernels to support UNIX domain sockets on remote hosts).
1748
1749 <hr>
1750 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
1751 (or 1023 on some Operating Systems that test only for below 1024) in
1752 the absence of elevated access or the absence of a capability flag
1753 having been set
1754 @throws randolf::rex::xEACCES If binding to an interface on systems that
1755 require elevated access for direct interface binding in absence of
1756 said elevated access
1757 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1758 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
1759 address is not available on any local interface
1760 @throws randolf::rex::xEBADF The underlying socket is not open
1761 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1762 part of the user address space
1763 @throws randolf::rex::xEINVAL Socket is already bound
1764 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
1765 valid for this socket's family (a.k.a., communication domain)
1766 @throws randolf::rex::xENOMEM Insufficient memory
1767 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1768 doesn't refer to a socket
1769 @n@n
1770 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1771 @throws randolf::rex::xEACCES Write permission or search permission denied
1772 on a component of the path prefix (@c AF_UNIX family)
1773 @throws randolf::rex::xELOOP Too many symbolic links encountered while
1774 resolving address (@c AF_UNIX family)
1775 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
1776 @throws randolf::rex::xENOENT One of the path's directory components doesn't
1777 exist (@c AF_UNIX family)
1778 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
1779 directory (@c AF_UNIX family)
1780 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
1781
1782 @returns The same rsocket object so as to facilitate stacking
1783 @see bind(const struct sockaddr*, const socklen_t)
1784 @see connect
1785 @see listen
1786 @qualifier TLS
1787 *///=========================================================================
1788 rsocket* bind(
1789 /// IPv4 address, IPv6 address, hostname, UNIX domain sockets path, or specialized variant (see notes, above)
1790 const std::string address,
1791 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
1792 const int port = 0) {
1793 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1794 + ", " + address
1795 + " port=" + std::to_string(port) + (port == 0 ? " (emphemeral or not-applicable)" : "")
1796 + ", size=" + std::to_string(__socket_addr_size)
1797 + ");");
1798
1799//std::cout << " address: " << address << std::endl;
1800 if (address.starts_with("if=")) { // Bind to interface
1801// TODO: Use family() to simplify this section of code
1802 struct ifreq ifr{};
1803 if (address.size() - 3 >= sizeof(ifr.ifr_name)) {} // Throw exception because ASCIIZ name will be too long
1804 address.copy(ifr.ifr_name, address.size() - 3, 3); // Copy address, excluding the "if=" portion
1805 __rc_check(::setsockopt(__socket_fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)));
1806 // TODO: Figure out how to use INADDR_ANY instead of 0 or :: (assuming this is even possible)
1807 // TODO: Test IPv6 more (telnet doesn't seem to be able to connect, and continues to work on IPv4 address)
1808 __socket_addr = *mk_sockaddr_storage(__socket_addr.ss_family == AF_INET6 ? "::" : "0", port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
1809 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
1810
1811// } else if (address.starts_with("if_cidr=")) { // Bind to interface based on matching CIDR
1812// // REMINDER: Update static family() method as well
1813// getifaddrs: https://man7.org/linux/man-pages/man3/getifaddrs.3.html
1814// } else if (address.starts_with("if_mac=")) { // Bind to interface based on its physical/hardware machine address
1815// // REMINDER: Update static family() method as well
1816// // Resolve interface name, and then just do what if= does (above)
1817
1818 } else {
1819 __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
1820 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
1821// __rc_check(::bind(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
1822 }
1823 return this;
1824 } // -x- rsocket* bind -x-
1825
1826 /*======================================================================*//**
1827 @brief
1828 Find out what buffer size is used by the various recv() methods.
1829 @returns Buffer size (in bytes)
1830 @see buffer_size(const size_t nbytes)
1831 @see is_buffered
1832 @qualifier TLS
1833 *///=========================================================================
1834 const int buffer_size() noexcept {
1835 return __buffer_size;
1836 }; // -x- int buffer_size -x-
1837
1838 /*======================================================================*//**
1839 @brief
1840 Override the default buffer size (typically 1024) used by the various recv()
1841 methods.
1842
1843 If resetting to the compiled-in default, use the buffer_size_reset() method
1844 instead of setting the value directly. This ensures that future versions,
1845 with a different compiled-in default, will be reset to the compiled-in value.
1846 @returns The same rsocket object so as to facilitate stacking
1847 @see buffer_size
1848 @see buffer_size_reset
1849 @qualifier TLS
1850 *///=========================================================================
1851 rsocket* buffer_size(
1852 /// Size of the new buffer (in bytes)
1853 const size_t nbytes) noexcept {
1854 __buffer_size = nbytes;
1855 if (__debug) debug("buffer_size(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1856 + " nbytes=" + std::to_string(nbytes)
1857 + ");");
1858 return this;
1859 }; // -x- rsocket* buffer_size -x-
1860
1861 /*======================================================================*//**
1862 @brief
1863 Reset the default buffer size (typically 1024) used by the various recv()
1864 methods.
1865
1866 This method is preferred for resetting to the compiled-in default instead of
1867 setting the value directly. This ensures that future versions, with a
1868 different compiled-in default, will be reset to the compiled-in value.
1869 @returns The same rsocket object so as to facilitate stacking
1870 @see buffer_size(const size_t nbytes)
1871 @qualifier TLS
1872 *///=========================================================================
1873 rsocket* buffer_size_reset() noexcept {
1874 __buffer_size = RSOCKET_BUFFER_SIZE;
1875 if (__debug) debug("buffer_size_reset(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1876 + " nbytes=" + std::to_string(RSOCKET_BUFFER_SIZE)
1877 + ");");
1878 return this;
1879 }; // -x- rsocket* buffer_size_reset -x-
1880
1881 //===========================================================================
1882 //
1883 // TODO: If family is AF_UNIX then also unlink the domain socket file.
1884 //
1885 /*======================================================================*//**
1886 @brief
1887 Close this rsocket. (If this rsocket was already closed, then calling this
1888 method additional times will have no effect, and will not cause exceptions to
1889 be thrown.)
1890 @warning
1891 This method may throw exceptions in some circumstances, which is extremely
1892 rare. If you prefer to close without having to contend with any exceptions
1893 (e.g., while calling close() from within a destructor), the close_passive()
1894 method will return an integer indicating success/failure instead of throwing
1895 an exception, and then you'll have to handle errno manually (which is useful
1896 in destructors because any error can merely be handled procedurally).
1897
1898 @throws randolf::rex::xEBADF The underlying socket is not open
1899 @throws randolf::rex::xEINTR Interrupted by a signal
1900 @throws randolf::rex::xEIO An I/O error occurred
1901
1902 @returns The same rsocket object so as to facilitate stacking
1903 @see is_closed()
1904 @qualifier POSIX
1905 @qualifier TLS
1906 *///=========================================================================
1907 rsocket* close() {
1908 if (__socket_open) {
1909 __rc_check(::close(__socket_fd));
1910 __socket_open = false;
1911 } // -x- if __socket_open -x-
1912 return this;
1913 }; // -x- rsocket* close -x-
1914
1915 /*======================================================================*//**
1916 @brief
1917 Close this rsocket without throwing any exceptions (an error code is returned
1918 instead, which is useful while calling close_passive() from within a
1919 destructor).
1920 @returns 0 = success
1921 @returns -1 = error (errno will be set accordingly)
1922 @see is_closed()
1923 @qualifier TLS
1924 *///=========================================================================
1925 int close_passive() noexcept {
1926 if (__socket_open) {
1927 int rc = ::close(__socket_fd);
1928 if (rc == 0) __socket_open = false;
1929 return rc;
1930 } // -x- if __socket_open -x-
1931 return 0; // Indicate success (because the socket was previously closed successfully)
1932 }; // -x- int close_passive -x-
1933
1934 /*======================================================================*//**
1935 @brief
1936 Connect this socket to a specific endpoint (which may differ from this
1937 rsocket's address that was previously configured by the @ref bind() method).
1938
1939 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1940 @throws randolf::rex::xEADDRNOTAVAIL No ephemeral ports are available for
1941 assignment to unbound socket
1942 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
1943 @throws randolf::rex::xEAGAIN Insufficient entries in the routing cache
1944 @throws randolf::rex::xEALREADY Previous connection attempt not completed on
1945 nonblocking socket
1946 @throws randolf::rex::xEBADF The underlying socket is not open
1947 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
1948 connections
1949 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1950 part of the user address space
1951 @throws randolf::rex::xEINPROGRESS Connection cannot be completed immediately
1952 on nonblocking socket (@c AF_UNIX family fails with xEAGAIN instead)
1953 @throws randolf::rex::xEINTR Interrupted by a signal
1954 @throws randolf::rex::xEISCONN Socket is already connected
1955 @throws randolf::rex::xENETUNREACH No route to network
1956 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1957 doesn't refer to a socket
1958 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1959 @throws randolf::rex::xEPERM Can't connect to a broadcast address when the
1960 socket's broadcast flag isn't enabled
1961 @throws randolf::rex::xEPROTOTYPE Socket type doesn't support the requested
1962 communications protocol (e.g., connecting a UNIX domain socket of
1963 type @c SOCK_STREAM to an endpoint expecting type @c SOCK_DGRAM)
1964 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1965 @n@n
1966 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1967 @throws randolf::rex::xEACCES Write permission or search permission denied
1968 on a component of the path prefix (@c AF_UNIX family)
1969 @throws randolf::rex::xEAGAIN Connection cannot be completed immediately on
1970 nonblocking socket (@c AF_UNIX family)
1971
1972 @returns The same rsocket object so as to facilitate stacking
1973 @see tls_do_handshake()
1974 @qualifier POSIX
1975 @qualifier TLS
1976 *///=========================================================================
1977 rsocket* connect(
1978 /// IPv4 address, IPv6 address, hostname, or UNIX domain sockets path
1979 const std::string address,
1980 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
1981 const int port = 0) {
1982 if (__debug) debug("connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1983 + ", " + address
1984 + " (port=" + std::to_string(port) + ")"
1985 + ", " + std::to_string(__socket_addr_size)
1986 + ");");
1987
1988 __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
1989 __rc_check(::connect(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size), 0, rex::rex::REX_FLAGS::REX_ALT_EAGAIN);
1990
1991// __rc_check(::connect(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
1992 __socket_connected = true;
1993
1994 // --------------------------------------------------------------------------
1995 // TLS.
1996 // --------------------------------------------------------------------------
1997 if (__tls) {
1998 if (__debug) debug("tls_connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1999 + ");");
2000 __rc_check_tls(SSL_connect(__tls_fd));
2001 } // -x- if __tls -x-
2002
2003 return this;
2004 }; // -x- rsocket* connect -x-
2005
2006 /*======================================================================*//**
2007 @brief
2008 Find out whether debug mode is enabled.
2009
2010 @par Threads
2011 This method is threadsafe.
2012 @returns TRUE = enabled
2013 @returns FALSE = not enabled
2014 @qualifier TLS
2015 *///=========================================================================
2016 const bool debug() noexcept { return __debug; };
2017
2018 /*======================================================================*//**
2019 @brief
2020 Debug mode. When debug mode is enabled, output is sent to stderr by default,
2021 unless a second parameter specifies a different file handle (e.g., stdout, or
2022 even a socket).
2023
2024 debug(...) returns -1 if fd can't be written to (errno will be set in
2025 accordance with std::fprintf's behaviour since std::fprintf is used for
2026 writing debug output). Normally we'd throw an exception for such errors, but
2027 with debug is a special case because debugging needs to be as quick and
2028 convenient as possible for developers.
2029
2030 debug(...) returns -2 if writing to stderr failed when attempting to announce
2031 that fd can't be written to (as described above).
2032
2033 debug(...) returns -3 if some other error occurs (e.g., internal formatting
2034 error {which should not happen} or memory allocation failed, in which case
2035 there's a more serious problem that will be causing other problems elsewhere
2036 anyway).
2037
2038 Developers may add their own debug messages by using debug(std::string),
2039 which will only be written out if debug mode is enabled. This same method is
2040 used internally, so messages will be indistinguishable from developer's
2041 messages.
2042
2043 @par Threads
2044 This method is thread-safe.
2045 @returns 0 = success
2046 @returns -1 = error writing to stream (errno will be set accordingly)
2047 @returns -2 = error writing to stderr (errno will be set accordingly)
2048 @returns -3 = error (errno will be set accordingly)
2049 @qualifier TLS
2050 *///=========================================================================
2051 const int debug(
2052 /// TRUE = enable debug mode@n
2053 /// FALSE = disable debug mode (does not close any file handles)
2054 const bool debug_flag,
2055 /// File descriptor/handle to use for debug output
2056 std::FILE* fd = stderr) noexcept {
2057
2058 int rc = debug_fd(fd); // Attempt to change debug handle
2059 if (rc != 0) return rc; // Return error id new debug handle failed
2060 time_t tm = std::time(nullptr);
2061 std::string dt(27, 0);
2062 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2063 try {
2064 if (debug_flag || __debug) std::fprintf(__debug_fd,
2065 "[%s] %s: Debug mode is %s\n",
2066 dt.c_str(),
2067 __debug_prefix.c_str(),
2068 debug_flag ? "ON" : "OFF");
2069 } catch (const std::system_error& e) { // Error writing to fd
2070 try {
2071 std::fprintf(stderr,
2072 "[%s] %s: error writing to stream\n",
2073 dt.c_str(),
2074 __debug_prefix.c_str());
2075 } catch (const std::exception& e) {
2076 return -2;
2077 }
2078 return -1;
2079 } catch (const std::exception& e) {
2080 return -3;
2081 }
2082 __debug = debug_flag; // Save debug flag
2083
2084 return 0; // Indicate success (no errors)
2085 }; // -x- int debug -x-
2086
2087 /*======================================================================*//**
2088 @brief
2089 Send the specified message as debug output (as long as debug mode is enabled;
2090 if disabled, no debug output will be sent).
2091 @returns 0 = success
2092 @returns -1 = error writing to stream (errno will be set accordingly)
2093 @returns -2 = error writing to stderr (errno will be set accordingly)
2094 @returns -3 = error (errno will be set accordingly)
2095 @qualifier TLS
2096 *///=========================================================================
2097 const int debug(
2098 /// Debug message as an ASCIIZ string
2099 const char* msg) noexcept {
2100 return __debug ? __debug_out(std::string(msg)) : 0;
2101 }; // -x- int debug -x-
2102
2103 /*======================================================================*//**
2104 @copydoc debug(const char*)
2105 @qualifier TLS
2106 *///=========================================================================
2107 const int debug(
2108 /// Debug message as an std::string object
2109 const std::string msg) noexcept {
2110 return __debug ? __debug_out(msg) : 0;
2111 }; // -x- int debug -x-
2112
2113 /*======================================================================*//**
2114 @brief
2115 Find out which file descriptor/handle is used for debug output.
2116 @returns file handle/descriptor currently used for debug output
2117 *///=========================================================================
2118 const std::FILE* debug_fd() noexcept { return __debug_fd; }; // Get debug-output file handle
2119
2120 /*======================================================================*//**
2121 @brief
2122 Specify a different file descriptor/handle to use for debug output
2123 @returns 0 = success
2124 @returns -1 = error writing to stream (errno will be set accordingly)
2125 @returns -2 = error writing to stderr (errno will be set accordingly)
2126 @returns -3 = error (errno will be set accordingly)
2127 *///=========================================================================
2128 const int debug_fd(
2129 /// File descriptor/handle to use for debug output
2130 std::FILE* fd) noexcept { // Set debug-output file handle
2131 if (__debug_fd != fd) {
2132 debug("Current debug-output file handle unset"); // For this message, we only want to send this out if debug mode is enabled
2133 __debug_fd = fd; // Save new debug-output file handle
2134 return debug("New debug-output file handle set"); // For this message, we only want to send this out if debug mode is enabled
2135 } // -x- if ~fd -x-
2136 return 0; // Indicate success (no errors)
2137 }; // -x- int debug_fd -x-
2138
2139 /*======================================================================*//**
2140 @brief
2141 Find out what the current prefix is set to that's used in debug output.
2142
2143 This may be set at any time, including before or after enabling or disabling
2144 debug mode.
2145 @returns debug prefix string
2146 @qualifier TLS
2147 *///=========================================================================
2148 const std::string debug_prefix() noexcept { return __debug_prefix; }; // -x- std::string debug_prefix -x-
2149
2150 /*======================================================================*//**
2151 @brief
2152 Change the prefix used in debug output.
2153 @returns The same rsocket object so as to facilitate stacking
2154 @qualifier TLS
2155 *///=========================================================================
2156 rsocket* debug_prefix(
2157 /// New debug prefix string
2158 const std::string prefix) noexcept {
2159 __debug_prefix = prefix;
2160 return this;
2161 }; // -x- std::string debug_prefix -x-
2162
2163 private:
2164 /*----------------------------------------------------------------------*//**
2165 @brief
2166 Internal debug-output function. This doesn't check __debug flag because it's
2167 expected that the normal debug() methods are taking care of this.
2168 @returns 0 = success
2169 @returns -1 = error writing to stream (errno will be set accordingly)
2170 @returns -2 = error writing to stderr (errno will be set accordingly)
2171 @returns -3 = error (errno will be set accordingly)
2172 *///-------------------------------------------------------------------------
2173 const int __debug_out(
2174 /// Debugging message
2175 const std::string msg) noexcept {
2176
2177 time_t tm = std::time(nullptr);
2178 std::string dt(27, 0);
2179 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2180 try {
2181 std::fprintf(__debug_fd,
2182 "[%s] %s: %s\n",
2183 dt.c_str(),
2184 __debug_prefix.c_str(),
2185 msg.c_str());
2186 } catch (const std::system_error& e) { // Error writing to fd
2187 try {
2188 std::fprintf(stderr,
2189 "[%s] %s: error writing to stream\n",
2190 dt.c_str(),
2191 __debug_prefix.c_str());
2192 } catch (const std::exception& e) {
2193 return -2;
2194 }
2195 return -1;
2196 } catch (const std::exception& e) {
2197 return -3;
2198 }
2199
2200 return 0; // Indicate success (no errors)
2201 }; // -x- int __debug_out -x-
2202
2203 // --------------------------------------------------------------------------
2204 // These are specialized internal debug output methods for presenting socket
2205 // options when they are get or set. These methods are marked as "private"
2206 // because they are really aren't useful outside of internal-use contexts.
2207 //
2208 // @par Threads
2209 // Some of these methods are thread-safe.
2210 //
2211 // These methods are thread-safe when they don't reference structures (e.g.,
2212 // integer {int}, unsigned integer {u_int}, and unsigned character {u_char}).
2213 //
2214 // These methods are not necessarily thread-safe if they reference structures
2215 // unless those structures are defined with std::atomic, or they were
2216 // constructed on-the-fly as anonymous parameters.
2217 // --------------------------------------------------------------------------
2218 const int __debug_sockopt(
2219 /// Name of function or method
2220 const std::string func,
2221 /// The level at which the option resides; typically @c SOL_SOCKET
2222 const int level,
2223 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2224 const int option,
2225 /// The structure that this socket option will be set to
2226 const int value) {
2227 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2228 + ", " + std::to_string(level)
2229 + ", " + std::to_string(option)
2230 + ", " + std::to_string(value)
2231 + ", size=" + std::to_string(sizeof(value))
2232 + ");");
2233 }; // -x- int __debug_sockopt -x-
2234 const int __debug_sockopt(
2235 /// Name of function or method
2236 const std::string func,
2237 /// The level at which the option resides; typically @c SOL_SOCKET
2238 const int level,
2239 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2240 const int option,
2241 /// The structure that this socket option will be set to
2242 const u_int value) {
2243 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2244 + ", " + std::to_string(level)
2245 + ", " + std::to_string(option)
2246 + ", u_int{" + std::to_string(value) + "}"
2247 + ", size=" + std::to_string(sizeof(value))
2248 + ");");
2249 }; // -x- int __debug_sockopt -x-
2250 const int __debug_sockopt(
2251 /// Name of function or method
2252 const std::string func,
2253 /// The level at which the option resides; typically @c SOL_SOCKET
2254 const int level,
2255 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2256 const int option,
2257 /// The structure that this socket option will be set to
2258 const u_char value) {
2259 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2260 + ", " + std::to_string(level)
2261 + ", " + std::to_string(option)
2262 + ", u_char{" + std::to_string(value) + "}"
2263 + ", size=" + std::to_string(sizeof(value))
2264 + ");");
2265 }; // -x- int __debug_sockopt -x-
2266 const int __debug_sockopt(
2267 /// Name of function or method
2268 const std::string func,
2269 /// The level at which the option resides; typically @c SOL_SOCKET
2270 const int level,
2271 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2272 const int option,
2273 /// The structure that this socket option will be set to
2274 const linger* value) {
2275 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2276 + ", " + std::to_string(level)
2277 + ", " + std::to_string(option)
2278 + ", linger{l_onoff=" + std::to_string(value->l_onoff)
2279 + ", l_linger=" + std::to_string(value->l_linger) + "}"
2280 + ", " + std::to_string(sizeof(value))
2281 + ");");
2282 }; // -x- int __debug_sockopt -x-
2283 const int __debug_sockopt(
2284 /// Name of function or method
2285 const std::string func,
2286 /// The level at which the option resides; typically @c SOL_SOCKET
2287 const int level,
2288 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2289 const int option,
2290 /// The structure that this socket option will be set to
2291 const timeval* value) {
2292 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2293 + ", " + std::to_string(level)
2294 + ", " + std::to_string(option)
2295 + ", timeval{tv_sec=" + std::to_string(value->tv_sec)
2296 + ", tv_usec=" + std::to_string(value->tv_usec) + "}"
2297 + ", " + std::to_string(sizeof(value))
2298 + ");");
2299 }; // -x- int __debug_sockopt -x-
2300 const int __debug_sockopt(
2301 /// Name of function or method
2302 const std::string func,
2303 /// The level at which the option resides; typically @c SOL_SOCKET
2304 const int level,
2305 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2306 const int option,
2307 /// The structure that this socket option will be set to
2308 const in_addr* value) {
2309 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2310 + ", " + std::to_string(level)
2311 + ", " + std::to_string(option)
2312 + ", in_addr{" + std::to_string(value->s_addr) + "}"
2313 + ", " + std::to_string(sizeof(value))
2314 + ");");
2315 }; // -x- int __debug_sockopt -x-
2316 const int __debug_sockopt(
2317 /// Name of function or method
2318 const std::string func,
2319 /// The level at which the option resides; typically @c SOL_SOCKET
2320 const int level,
2321 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2322 const int option,
2323 /// The structure that this socket option will be set to
2324 const ip_mreq* value) {
2325 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2326 + ", " + std::to_string(level)
2327 + ", " + std::to_string(option)
2328 + ", ip_mreq{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2329 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2330 + ", " + std::to_string(sizeof(value))
2331 + ");");
2332 }; // -x- int __debug_sockopt -x-
2333 const int __debug_sockopt(
2334 /// Name of function or method
2335 const std::string func,
2336 /// The level at which the option resides; typically @c SOL_SOCKET
2337 const int level,
2338 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2339 const int option,
2340 /// The structure that this socket option will be set to
2341 const ip_mreq_source* value) {
2342 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2343 + ", " + std::to_string(level)
2344 + ", " + std::to_string(option)
2345 + ", ip_mreq_source{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2346 + ", source=" + std::to_string(value->imr_sourceaddr.s_addr)
2347 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2348 + ", " + std::to_string(sizeof(value))
2349 + ");");
2350 }; // -x- int __debug_sockopt -x-
2351 const int __debug_sockopt(
2352 /// Name of function or method
2353 const std::string func,
2354 /// The level at which the option resides; typically @c SOL_SOCKET
2355 const int level,
2356 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2357 const int option,
2358 /// The structure that this socket option will be set to
2359 const icmp6_filter* value) {
2360 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2361 + ", " + std::to_string(level)
2362 + ", " + std::to_string(option)
2363 + ", icmp6_filter{[0]=" + std::to_string(value->icmp6_filt[0])
2364 + ", [1]=" + std::to_string(value->icmp6_filt[1])
2365 + ", [2]=" + std::to_string(value->icmp6_filt[2])
2366 + ", [3]=" + std::to_string(value->icmp6_filt[3])
2367 + ", [4]=" + std::to_string(value->icmp6_filt[4])
2368 + ", [5]=" + std::to_string(value->icmp6_filt[5])
2369 + ", [6]=" + std::to_string(value->icmp6_filt[6])
2370 + ", [7]=" + std::to_string(value->icmp6_filt[7]) + "}"
2371 + ", " + std::to_string(sizeof(value))
2372 + ");");
2373 }; // -x- int __debug_sockopt -x-
2374 const int __debug_sockopt(
2375 /// Name of function or method
2376 const std::string func,
2377 /// The level at which the option resides; typically @c SOL_SOCKET
2378 const int level,
2379 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2380 const int option,
2381 /// The structure that this socket option will be set to
2382 const sockaddr_in6* value) {
2383 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2384 + ", " + std::to_string(level)
2385 + ", " + std::to_string(option)
2386 + ", sockaddr_in6{family=" + std::to_string(value->sin6_family)
2387 + ", port=" + std::to_string(value->sin6_port)
2388 + ", flowinfo=" + std::to_string(value->sin6_flowinfo)
2389 + ", addr=" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[0])
2390 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[1])
2391 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[2])
2392 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[3])
2393 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[4])
2394 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[5])
2395 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[6])
2396 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[7])
2397 + ", scope_id=" + std::to_string(value->sin6_scope_id) + "}"
2398 + ", " + std::to_string(sizeof(value))
2399 + ");");
2400 }; // -x- int __debug_sockopt -x-
2401 const int __debug_sockopt(
2402 /// Name of function or method
2403 const std::string func,
2404 /// The level at which the option resides; typically @c SOL_SOCKET
2405 const int level,
2406 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2407 const int option,
2408 /// The structure that this socket option will be set to
2409 const ip6_mtuinfo* value) {
2410 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2411 + ", " + std::to_string(level)
2412 + ", " + std::to_string(option)
2413 + ", ip6_mtuinfo{addr=" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[0])
2414 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[1])
2415 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[2])
2416 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[3])
2417 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[4])
2418 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[5])
2419 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[6])
2420 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[7])
2421 + ", mtu=" + std::to_string(value->ip6m_mtu) + "}"
2422 + ", " + std::to_string(sizeof(value))
2423 + ");");
2424 }; // -x- int __debug_sockopt -x-
2425 const int __debug_sockopt(
2426 /// Name of function or method
2427 const std::string func,
2428 /// The level at which the option resides; typically @c SOL_SOCKET
2429 const int level,
2430 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2431 const int option,
2432 /// The structure that this socket option will be set to
2433 const ipv6_mreq* value) {
2434 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2435 + ", " + std::to_string(level)
2436 + ", " + std::to_string(option)
2437 + ", ipv6_mreq{addr=" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[0])
2438 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[1])
2439 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[2])
2440 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[3])
2441 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[4])
2442 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[5])
2443 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[6])
2444 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[7])
2445 + ", iface=" + std::to_string(value->ipv6mr_interface) + "}"
2446 + ", " + std::to_string(sizeof(value))
2447 + ");");
2448 }; // -x- int __debug_sockopt -x-
2449 const int __debug_sockopt(
2450 /// Name of function or method
2451 const std::string func,
2452 /// The level at which the option resides; typically @c SOL_SOCKET
2453 const int level,
2454 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2455 const int option,
2456 /// The structure that this socket option will be set to
2457 const group_req* value) {
2458 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2459 + ", " + std::to_string(level)
2460 + ", " + std::to_string(option)
2461 + ", group_req{iface=" + std::to_string(value->gr_interface)
2462 + ", <group_req>}"
2463 + ", " + std::to_string(sizeof(value))
2464 + ");");
2465 }; // -x- int __debug_sockopt -x-
2466 const int __debug_sockopt(
2467 /// Name of function or method
2468 const std::string func,
2469 /// The level at which the option resides; typically @c SOL_SOCKET
2470 const int level,
2471 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2472 const int option,
2473 /// The structure that this socket option will be set to
2474 const group_source_req* value) {
2475 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2476 + ", " + std::to_string(level)
2477 + ", " + std::to_string(option)
2478 + ", group_source_req{iface=" + std::to_string(value->gsr_interface)
2479 + ", <group_source_req>}"
2480 + ", " + std::to_string(sizeof(value))
2481 + ");");
2482 }; // -x- int __debug_sockopt -x-
2483 const int __debug_sockopt_other(
2484 /// Name of function or method
2485 const std::string func,
2486 /// The level at which the option resides; typically @c SOL_SOCKET
2487 const int level,
2488 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2489 const int option,
2490 /// The structure that this socket option will be set to
2491 const socklen_t size) {
2492 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2493 + ", " + std::to_string(level)
2494 + ", " + std::to_string(option)
2495 + ", other=?"
2496 + ", " + std::to_string(size)
2497 + ");");
2498 }; // -x- int __debug_sockopt_other -x-
2499
2500 public:
2501 /*======================================================================*//**
2502 @brief
2503 Find out what the current EoL (End of Line) sequence is set to.
2504 @warning
2505 To send an EoL sequence do not use @c send(r.eol()) because it may not be
2506 initialized yet and the endpoint you're sending to may seem unresponsive or
2507 other unexpected behaviour may occur.
2508 @n
2509 To send an EoL sequence properly, use @ref sendline() (no parameters is more
2510 efficient than specifying an empty string with ""), or use the specialized
2511 @ref send_eol() method (which is the most efficient option).
2512 @attention
2513 An empty EoL sequence means that CRLF will be sent by @ref sendline(), and
2514 @ref recvline() will automatically detect from one of CR, CRLF, LF, and LFCR.
2515 @returns Current EoL sequence
2516 @see eol_adoption
2517 @see eol_fix_printf
2518 @see printfline
2519 @see recvline
2520 @see sendline
2521 @see send_eol
2522 @see vprintfline
2523 @qualifier TLS
2524 *///=========================================================================
2525 const std::string eol() noexcept { return __eol; }; // -x- std::string eol -x-
2526
2527 /*======================================================================*//**
2528 @brief
2529 Set EoL (End of Line) sequence. This sequence is used by recvline(),
2530 sendline(), and related functions, and it defaults to an empty string which
2531 means that the EoL sequence will be automatically detected on-the-fly.
2532
2533 - @c "" (empty string) = automatically detect on-the-fly (default)
2534 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
2535 - @c \\r (CR) = Carriage Return (typical for MacOS)
2536 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
2537
2538 @note
2539 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
2540 IMAP4, FINGER, NICNAME/WHOIS, etc.).
2541 @returns The same rsocket object so as to facilitate stacking
2542 @see eol_adoption
2543 @see eol_fix_printf
2544 @see printfline
2545 @see vprintfline
2546 @see sendline
2547 @see send_eol
2548 @qualifier TLS
2549 *///=========================================================================
2550 rsocket* eol(
2551 /// EoL sequence as an ASCIIZ string
2552 const char* eol) noexcept {
2553 __eol = std::string(eol);
2554 __eol_out = __eol.empty() ? __CRLF : __eol;
2555 return this;
2556 }; // -x- rsocket* eol -x-
2557
2558 /*======================================================================*//**
2559 @brief
2560 Set EoL (End of Line) sequence. This sequence is used by recvline(),
2561 sendline(), and related functions, and it defaults to an empty string which
2562 means that the EoL sequence will be automatically detected on-the-fly.
2563
2564 - @c "" (empty string) = automatically detect on-the-fly (default)
2565 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
2566 - @c \\r (CR) = Carriage Return (typical for MacOS)
2567 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
2568
2569 @note
2570 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
2571 IMAP4, FINGER, NICNAME/WHOIS, etc.).
2572 @returns The same rsocket object so as to facilitate stacking
2573 @see eol_fix_printf
2574 @see printfline
2575 @see vprintfline
2576 @see sendline
2577 @see send_eol
2578 @qualifier TLS
2579 *///=========================================================================
2580 rsocket* eol(
2581 /// EoL sequence as an std::string object
2582 const std::string eol) noexcept {
2583 __eol = eol;
2584 return this;
2585 }; // -x- rsocket* eol -x-
2586
2587 /*======================================================================*//**
2588 @brief
2589 Configure EoL adoption policy for the @ref recvline() method. By default,
2590 @ref rsocket is configured with the EoL adoption policy enabled alongside an
2591 empty @ref eol() sequence, which results in the default operation being that
2592 the EoL sequence automatically gets detected and updated internally upon the
2593 first use of the @ref recvline() method.
2594
2595 The EoL adoption policy is only effective when the @ref eol() sequence is not
2596 defined (which is indicated by an empty EoL sequence string).
2597
2598 The EoL sequence is updated only when the EoL sequence string is empty, and
2599 when this EoL adoption policy is enabled.
2600 @returns The same rsocket object so as to facilitate stacking
2601 @see eol
2602 @see eol_index
2603 @see is_eol_adoption
2604 @see recvline
2605 @see sendline
2606 @see send_eol
2607 @qualifier TLS
2608 *///=========================================================================
2609 rsocket* eol_adoption(
2610 /// TRUE = enable EoL adoption (default)@n
2611 /// FALSE = disable EoL adoption
2612 const bool flag) noexcept {
2613 __eol_fix_printf = flag;
2614 return this;
2615 }; // -x- rsocket* eol_adoption -x-
2616
2617 /*======================================================================*//**
2618 @brief
2619 Configure EoL substitution policy for the @ref printf(), @ref printfline(),
2620 @ref vprintf(), and @ref vprintfline() methods. By default, @ref rsocket
2621 substitutes printf's @c \\n sequence with the EoL sequence (if defined), and
2622 this method can be used to disable this behaviour.
2623 @note
2624 The @c \\n sequence used in the printf format string normally coincides with
2625 the local Operating System's newline standard, which is very likely different
2626 from the @ref rsocket endpoint's newline standard, and/or the newline
2627 standard of the protocol being implemented. This policy setting makes it
2628 possible to control whether to use the configured EoL sequence when sending a
2629 formatted string to the endpoint.
2630 @returns The same rsocket object so as to facilitate stacking
2631 @see eol
2632 @see is_eol_fix_printf
2633 @see printf
2634 @see printfline
2635 @see vprintf
2636 @see vprintfline
2637 @qualifier TLS
2638 *///=========================================================================
2639 rsocket* eol_fix_printf(
2640 /// TRUE = enable EoL substitution (default)@n
2641 /// FALSE = disable EoL substitution
2642 const bool flag) noexcept {
2643 __eol_fix_printf = flag;
2644 return this;
2645 }; // -x- rsocket* eol_fix_printf -x-
2646
2647 /*======================================================================*//**
2648 @brief
2649 Finds the first instance of the EoL sequence and returns its offset (which is
2650 effectively the same as the size of the text, not including the characters
2651 that the EoL sequence is comprised of).
2652
2653 @note
2654 This method is specialized primarily for internal use by the @ref recvline()
2655 method.
2656 @returns Size of EoL sequence
2657 @returns -1 if EoL sequence wasn't found
2658 @see eol
2659 @see eol_adoption
2660 @qualifier TLS
2661 *///=========================================================================
2662 const int eol_index(
2663 /// Buffer that probably contains at least one EoL sequence
2664 const std::string buffer,
2665 /// Size of string with EoL character sequence included (will be updated by this method)
2666 int* with_eol_size) noexcept {
2667
2668 // --------------------------------------------------------------------------
2669 // An EoL character sequence was specified, so a simple search will suffice.
2670 // --------------------------------------------------------------------------
2671 if (!__eol.empty()) {
2672//std::cout << "!__eol.empty() ------------------------------ String is not empty" << std::endl;
2673 int pos = buffer.find(__eol);
2674 if (pos >= 0) *with_eol_size = pos + __eol.size(); // Update size of EoL sequence
2675//std::cout << "------1------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2676 return pos;
2677 } // -x- if !__eol.empty() -x-
2678//std::cout << "__eol.empty() ------------------------------ String is empty" << std::endl;
2679
2680 // --------------------------------------------------------------------------
2681 // Automatic detection of EoL sequence.
2682 //
2683 // Search for all four possible EoL sequences (as indicated by an empty EoL
2684 // character sequence string):
2685 //
2686 // CRLF: Common for text lines with internet protocols such as SMTP, POP3,
2687 // IMAP4, HTTP, FINGER, WHOIS, etc. Also: DOS and OS/2 EoL sequence.
2688 //
2689 // CR: MacOS EoL character.
2690 //
2691 // LF: UNIX/Linux EoL character.
2692 //
2693 // LFCR: Extremely rare, but I've encountered multiple instances where the
2694 // intended CRLF was reversed to LFCR, possibly due to a programming
2695 // error or an artifact of inappropriate/unintended endian conversion.
2696 // --------------------------------------------------------------------------
2697 int pos = buffer.find(__CR); // CR or CRLF
2698 if (pos == (buffer.size() - 1)) { // EoL sequence not found (this handles a specific empty-buffer edge case caused by SSL background chatter that's very rare but causes problems that are nearly impossible to debug when they do occur)
2699 *with_eol_size = pos + 1;
2700// *with_eol_size = 1;
2701//std::cout << "------2------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2702 return pos;
2703 } else if (pos >= 0) { // EoL sequence found
2704 *with_eol_size = pos + (buffer[pos + 1] == __LF ? 2 : 1); // 2=CRLF, 1=CR
2705// *with_eol_size = buffer[pos + 1] == __LF ? 2 : 1; // 2=CRLF, 1=CR
2706 if (__eol_adoption) {
2707 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
2708 __eol.resize(*with_eol_size - pos); // Truncate extra characters
2709 } // -x- if __eol_adoption -x-
2710//std::cout << "------3------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2711 return pos; // Either way, we're done
2712 } // -x- if pos -x-
2713
2714 pos = buffer.find(__LF); // LF or LFCR
2715 if (pos == (buffer.size() - 1)) { // EoL sequence not found (this handles a specific empty-buffer edge case caused by SSL background chatter that's very rare but causes problems that are nearly impossible to debug when they do occur)
2716 *with_eol_size = pos + 1;
2717// *with_eol_size = 1;
2718//std::cout << "------4------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2719 return pos;
2720 } else if (pos >= 0) { // EoL sequence found
2721 *with_eol_size = pos + (buffer[pos + 1] == __CR ? 2 : 1); // 2=LFCR, 1=LF
2722// *with_eol_size = buffer[pos + 1] == __CR ? 2 : 1; // 2=LFCR, 1=LF
2723 if (__eol_adoption) {
2724 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
2725 __eol.resize(*with_eol_size - pos); // Truncate extra characters
2726 } // -x- if __eol_adoption -x-
2727//std::cout << "------5------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2728 return pos; // Either way, we're done
2729 } // -x- if pos -x-
2730
2731//std::cout << "------6------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
2732 return pos;
2733 }; // -x- int eol_index -x-
2734
2735 /*======================================================================*//**
2736 @brief
2737 Find out if the stream is at its end and that this @ref rsocket's internal
2738 buffer (if one had been set up by the @ref recvline() method) is empty. This
2739 doesn't necessarily mean that the stream is closed; but rather that the
2740 endpoint just hasn't sent any more data (yet).
2741
2742 If the stream isn't open, then this method will always return @c true to
2743 implicitly indicate that there's no more data.
2744
2745 @pre
2746 For this method to work properly, you need to specify a timeout either with
2747 the timeout parameter, or by setting the @ref timeout() for this rsocket (you
2748 can still override this rsocket's timeout by specifying a timeout here).
2749
2750 It's better (more efficient) to use this method instead of the @c MSG_PEEK
2751 flag (with the various @c recv methods) to determine whether any data is
2752 waiting on the stream (e.g., data that's received by the sockets, but not by
2753 any @c recv methods yet) because this method is specialized in handling this
2754 particular condition and responds with an easy-to-use boolean flag.
2755
2756 @note
2757 EoS is an acronym for: End of Stream
2758
2759 @throws randolf::rex::xEBADF The underlying socket is not open
2760 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2761 part of the user address space
2762 @throws randolf::rex::xEINTR Interrupted by a signal
2763 @throws randolf::rex::xENOMEM Insufficient memory
2764 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2765 doesn't refer to a socket
2766 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
2767 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
2768 is a highly improbable chance that a timeout could still occur if the
2769 data is read by another thread before the `recv(..., MSG_PEEK)` call)
2770 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
2771 there's no new data
2772
2773 @returns TRUE = End of Stream (no data, or the stream @ref is_closed())
2774 @returns FALSE = More data is ready to be received
2775 @see is_closed
2776 @see is_open
2777 @qualifier TLS
2778 *///=========================================================================
2779// TODO: Include exceptions from recv() in documentation (above) method (once they're ready)
2780 const bool eos(
2781 /// Number of milliseconds to wait
2782 const int timeout = 0) {
2783
2784 // --------------------------------------------------------------------------
2785 // Check internal buffer first.
2786 // --------------------------------------------------------------------------
2787 if (__buffer != nullptr && __buffer_head != __buffer_tail) return false;
2788
2789 // --------------------------------------------------------------------------
2790 // Check for data that's not stored in internal buffers.
2791 // --------------------------------------------------------------------------
2792 if (__socket_open) {
2793
2794 // --------------------------------------------------------------------------
2795 // Timeout polling (in case a timeout was specified).
2796 // --------------------------------------------------------------------------
2797 try {
2798 if (timeout != 0) {
2799 poll(POLLIN, timeout);
2800 } else {
2801 std::shared_ptr<timeval> tv = getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO);
2802 ppoll(POLLIN, tv->tv_sec, tv->tv_usec * 1000); // 1 microsecond = 1,000 nanoseconds
2803 }
2804 } catch (const randolf::rex::xETIMEDOUT e) {
2805 return true;
2806 } // This is a specialized catch situation -- the ::recv function could still return an ETIMEDOUT error
2807
2808 // --------------------------------------------------------------------------
2809 // Non-blocking socket will return -1 and set errno == EAGAIN or EWOULDBLOCK
2810 // if there is simply no new data. In case of graceful connection shutdown
2811 // by the remote peer, recv() will return 0.
2812 // --------------------------------------------------------------------------
2813 char z; // Temporary throw-away variable
2814 if (::recv(__socket_fd, &z, 1, MSG_PEEK | MSG_DONTWAIT) == 0) return true; // No more data; this is EoS
2815
2816 } // -x- if __socket_open -x-
2817
2818 return !__socket_open; // Effectively, this is: return closed();
2819 }; // -x- bool eos -x-
2820
2821 /*======================================================================*//**
2822 @brief
2823 Find out what the specified IP address string's family (@c SO_DOMAIN) is.
2824 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
2825 address is not available on any local interface
2826 @throws randolf::rex::xEAI_NONAME If @c address is an empty string
2827 @returns @c AF_UNSPEC (if family couldn't be determined)
2828 @returns @c AF_INET (IPv4 address)
2829 @returns @c AF_INET6 (IPv6 address)
2830 @returns @c AF_UNIX (UNIX Domain address)
2831 @returns ...or other family as applicable
2832 @qualifier TLS
2833 *///=========================================================================
2834 int static family(
2835 /// Address, similar to @ref bind() addressing, including non-standard "if="
2836 /// variant that names a network interface
2837 const std::string address,
2838 /// Preferred family to return first (used only with interface mode where the
2839 /// network interface is specified after the "if=" prefix); the default value
2840 /// of @c AF_UNSPEC will return the first family interface found
2841 const int preferred_family = AF_UNSPEC) {
2842
2843 // --------------------------------------------------------------------------
2844 // Simple checks first.
2845 // --------------------------------------------------------------------------
2846 if (address.size() == 0) randolf::rex::mk_exception("", EAI_NONAME);
2847 if (address.front() == '/') return AF_UNIX;
2848
2849 // --------------------------------------------------------------------------
2850 // if=<interface-name>: Same "interface" option that we support in bind()
2851 // --------------------------------------------------------------------------
2852 if (address.starts_with("if=")) {
2853 //std::cout << "address=" << address.substr(3).c_str() << std::endl; // Debug
2854 struct ifaddrs *ifaddr; // Chained interface addresses structure
2855 if (::getifaddrs(&ifaddr) == -1) { // Error occurred, so we're done here
2856 freeifaddrs(ifaddr); // Memory management
2857 randolf::rex::mk_exception("getifaddrs() error (prequisite to searching for " + address + ")", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
2858 } // -x- if getifaddrs -x
2859
2860 // --------------------------------------------------------------------------
2861 // Iterate through interface addresses. Each address should have a different
2862 // address family/domain, and is never AF_UNSPEC (this should never happen
2863 // because it wouldn't make sense, but if the network implementation was
2864 // broken and including an AF_UNSPEC family/domain, it would be useless for
2865 // the purposes of calling the ::socket() function so our code will just
2866 // ignore it anyway.
2867 // --------------------------------------------------------------------------
2868 int first_family = AF_UNSPEC; // We're using AF_UNSPEC (0) to indicate that there isn't a family/domain
2869 for (struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
2870 if (ifa->ifa_addr == nullptr) continue; // No address structure, so this isn't useful to us at all
2871 if (ifa->ifa_addr->sa_family == AF_UNSPEC) continue; // Skip AF_UNSPEC family/domain, as we can't use this
2872 if (address.compare(3, -1, ifa->ifa_name) != 0) continue; // Not the interface name we're looking for
2873
2874 // --------------------------------------------------------------------------
2875 // Return current address's family if preferred_family is not specified (if
2876 // it's set to AF_UNSPEC {0}), or if it matches the current address.
2877 // --------------------------------------------------------------------------
2878 //std::cout << "if=" << ifa->ifa_name << " family=" << ifa->ifa_addr->sa_family << std::endl; // Debug
2879 if (preferred_family == AF_UNSPEC || preferred_family == ifa->ifa_addr->sa_family) {
2880 first_family = ifa->ifa_addr->sa_family; // Arbitrarily re-using first_family variable
2881 freeifaddrs(ifaddr); // Memory management
2882 return first_family; // Return current family/domain (re-used first_family variable)
2883 } // -x- if preferred_family -x-
2884
2885 // --------------------------------------------------------------------------
2886 // Keep track of only the first_family/domain that we found; if we can't find
2887 // the preferred_family, then we'll be able to return the first one we found.
2888 // --------------------------------------------------------------------------
2889 if (first_family == AF_UNSPEC && ifa->ifa_addr->sa_family != AF_UNSPEC) first_family = ifa->ifa_addr->sa_family;
2890 //std::cout << " first_family=" << first_family << std::endl; // Debug
2891 } // -x- for *ifa -x-
2892
2893 // --------------------------------------------------------------------------
2894 // Return first_family/domain, if there was one; otherwise, throw exception.
2895 // --------------------------------------------------------------------------
2896 freeifaddrs(ifaddr); // Memory management
2897 if (first_family != AF_UNSPEC) return first_family;
2898 randolf::rex::mk_exception("Interface (" + address.substr(3) + ") doesn't exist" + (preferred_family == AF_UNSPEC ? "" : " or the preferred family/domain (" + std::to_string(preferred_family) + ") is not supported"), EADDRNOTAVAIL);
2899 } // -x- if /^if=/ -x-
2900
2901 // --------------------------------------------------------------------------
2902 // Attempt to detect IPv4 or IPv6 using a simple shortcut with ::inet_pton(),
2903 // which requires some additional memory allocation (that the simple checks
2904 // from earlier don't need).
2905 // --------------------------------------------------------------------------
2906 char buf[sizeof(struct in6_addr)]; // Binary address storage
2907 if (::inet_pton(AF_INET, address.c_str(), buf) == 1) return AF_INET;
2908 if (::inet_pton(AF_INET6, address.c_str(), buf) == 1) return AF_INET6;
2909
2910 // --------------------------------------------------------------------------
2911 // Throw xEAI_FAMILY exception because no family/domain was found.
2912 // --------------------------------------------------------------------------
2913 randolf::rex::mk_exception("No family/domain available for: " + address, EAI_FAMILY);
2914 }; // -x- int family -x-
2915
2916 /*======================================================================*//**
2917 @brief
2918 Get peer name returns the address of the socket as a sockaddr_storage
2919 structure.
2920
2921 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
2922 that aids in the prevention of resource leaks).
2923
2924 @throws randolf::rex::xEBADF The underlying socket is not open
2925 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2926 part of the user address space
2927 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
2928 valid for this socket's family (a.k.a., communication domain)
2929 @throws randolf::rex::xENOBUFS Insufficient memory
2930 @throws randolf::rex::xENOTCONN Underlying socket is not connected
2931 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2932 doesn't refer to a socket
2933
2934 @returns sockaddr_storage structure
2935 @qualifier POSIX
2936 @qualifier TLS
2937 *///=========================================================================
2938 const std::shared_ptr<sockaddr_storage> getpeername() {
2939 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
2940 socklen_t len = sizeof(sockaddr_storage);
2941 __rc_check(::getpeername(__socket_fd, (struct sockaddr*)sa.get(), &len));
2942 return sa;
2943 }; // -x- std::shared_ptr<sockaddr_storage> getpeername -x-
2944
2945 /*======================================================================*//**
2946 @brief
2947 Get peer name returns the address of the socket as a std::string object.
2948
2949 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
2950 (e.g., because the @c family doesn't utilize or support an address
2951 {or the format isn't known}
2952 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
2953 @throws randolf::rex::xEBADF The underlying socket is not open
2954 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2955 part of the user address space
2956 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
2957 valid for this socket's family (a.k.a., communication domain)
2958 @throws randolf::rex::xENOBUFS Insufficient memory
2959 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
2960 @throws randolf::rex::xENOTCONN Underlying socket is not connected
2961 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2962 doesn't refer to a socket
2963
2964 @returns string representation of peer name
2965 @qualifier TLS
2966 *///=========================================================================
2967 const std::string getpeername_ntop() {
2968 sockaddr_storage sa;
2969 socklen_t len = sizeof(sockaddr_storage);
2970 __rc_check(::getpeername(__socket_fd, (sockaddr*)&sa, &len));
2971 return inet_ntop(&sa);
2972 }; // -x- std::string getpeername_ntop -x-
2973
2974 /*======================================================================*//**
2975 @brief
2976 Get specified "sockaddr_storage" structure's address as a "sockaddr"
2977 structure, for sockets in one of the supported families:
2978
2979 - AF_INET (IPv4)
2980 - AF_INET6 (IPv6)
2981 - AF_UNIX (Domain socket path)
2982 - AF_PACKET (Ethernet node/mac. address)
2983
2984 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
2985 (e.g., because the @c family doesn't utilize or support an address
2986 {or the format isn't known}
2987
2988 @returns pointer to sockaddr structure within provided sockaddr_storage
2989 @see bind
2990 @see mk_sockaddr_storage
2991 @see recvfrom
2992 @see sendto
2993 @see sendzto
2994 @qualifier POSIX
2995 @qualifier TLS
2996 *///=========================================================================
2997 static sockaddr* getsockaddr(
2998 /// The @c sockaddr_storage structure wherein @c sockaddr will be referenced from
2999 const sockaddr_storage* sa) {
3000 switch (sa->ss_family) {
3001 case AF_INET: // IPv4 address
3002 return (sockaddr*)&(((struct sockaddr_in*)sa)->sin_addr);//->sin_addr);
3003 case AF_INET6: // IPv6 address
3004 return (sockaddr*)&(((struct sockaddr_in6*)sa)->sin6_addr);//->sin6_addr);
3005 case AF_UNIX: // UNIX (path) domain socket
3006 return (sockaddr*)&(((struct sockaddr_un*)sa)->sun_path);//->sun_path);
3007 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
3008 // case /*AF_LINK* /18: // Link layer interface (arp)
3009 // break;
3010 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3011 return (sockaddr*)&(((struct sockaddr_ll*)sa)->sll_addr);//->sll_addr);
3012 } // -x- switch family -x-
3013 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY"); // Everything else
3014 }; // -x- sockaddr* getsockaddr -x-
3015
3016 /*======================================================================*//**
3017 @brief
3018 Get socket name returns the address of the socket as a "sockaddr_storage"
3019 structure.
3020
3021 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
3022 that aids in the prevention of resource leaks).
3023
3024 @throws randolf::rex::xEBADF The underlying socket is not open
3025 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3026 part of the user address space
3027 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3028 valid for this socket's family (a.k.a., communication domain)
3029 @throws randolf::rex::xENOBUFS Insufficient memory
3030 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3031 doesn't refer to a socket
3032
3033 @returns sockaddr_storage structure
3034 @qualifier POSIX
3035 @qualifier TLS
3036 *///=========================================================================
3037 const std::shared_ptr<sockaddr_storage> getsockname() {
3038 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3039 socklen_t len = sizeof(sockaddr_storage);
3040 __rc_check(::getsockname(__socket_fd, (struct sockaddr*)sa.get(), &len));
3041 return sa;
3042 }; // -x- std::string getsockname -x-
3043
3044 /*======================================================================*//**
3045 @brief
3046 Get socket name returns the name of the socket as a std::string object.
3047
3048 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3049 (e.g., because the @c family doesn't utilize or support an address
3050 {or the format isn't known}
3051 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3052 @throws randolf::rex::xEBADF The underlying socket is not open
3053 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3054 part of the user address space
3055 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3056 valid for this socket's family (a.k.a., communication domain)
3057 @throws randolf::rex::xENOBUFS Insufficient memory
3058 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3059 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3060 doesn't refer to a socket
3061
3062 @returns string representation of socket name
3063 @qualifier TLS
3064 *///=========================================================================
3065 const std::string getsockname_ntop() {
3066 sockaddr_storage sa;
3067 socklen_t len = sizeof(sockaddr_storage);
3068 __rc_check(::getsockname(__socket_fd, (sockaddr*)&sa, &len));
3069 return inet_ntop(&sa);
3070 }; // -x- std::string getsockname_ntop -x-
3071
3072 /*======================================================================*//**
3073 @brief
3074 Get socket option details in the form of an integer.
3075
3076 Most options return an integer, with the remaining options returning a
3077 pointer to a structure wrapped in a std::shared_ptr (a C++ smart pointer that
3078 aids in the prevention of resource leaks); the primitive types int, u_int,
3079 and u_char are not wrapped in C++ smart pointers because returning them by
3080 value is more efficient since allocating memory for an entire structure isn't
3081 needed.
3082
3083 @post
3084 It is up to the developer to know which return type is needed according to
3085 the socket option, otherwise an exception will likely be thrown -- in some
3086 cases where the wrong type will seem to work, this is due to the wrong type
3087 providing a minimally sufficient amount of memory for the storage of the
3088 resulting structure.
3089
3090 @par Notes
3091 The returned values/structures are not marked as "const" because they may
3092 need to be modified for unforseen purposes. Modifying the returend values or
3093 structures is fine because they are intended to be independent and are
3094 expected to have no direct impact on the rsocket's internal variables and
3095 structures.
3096
3097 Templates in C++ aren't used here because they don't work properly for our
3098 needs due to neccesity to handle both fundamental types and structures; it
3099 turns out that mixing these is impossible when using the same function name,
3100 so this just doesn't work as well as we'd like it to. (We may try to work on
3101 this again in the future as time permits to provide an additional method for
3102 obtaining socket options, but with the intention of never removing this
3103 current set of methods so as to ensure backward compatibility in the future.)
3104
3105 @throws randolf::rex::xEBADF The underlying socket is not open
3106 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3107 part of the user address space
3108 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3109 valid for this socket's family (a.k.a., communication domain)
3110 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
3111 is not supported
3112 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3113 doesn't refer to a socket
3114
3115 @returns socket option value
3116 @qualifier TLS
3117 *///=========================================================================
3118 int getsockopt_int(
3119 /// The level at which the option resides; typically @c SOL_SOCKET
3120 const int level,
3121 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3122 const int option) {
3123 int value = 0;
3124 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3125 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3126 return value;
3127 }; // -x- int getsockopt_int -x-
3128
3129 /*======================================================================*//**
3130 @brief
3131 Get socket option details in the form of an unsigned integer.
3132 @copydetails getsockopt_int(const int, const int)
3133 @returns socket option value
3134 @qualifier TLS
3135 *///=========================================================================
3136 u_int getsockopt_u_int(
3137 /// The level at which the option resides; typically @c SOL_SOCKET
3138 const int level,
3139 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3140 const int option) {
3141 u_int value = 0;
3142 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3143 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3144 return value;
3145 }; // -x- u_int getsockopt_u_int -x-
3146
3147 /*======================================================================*//**
3148 @brief
3149 Get socket option details in the form of an unsigned character.
3150 @copydetails getsockopt_int(const int, const int)
3151 @returns socket option value
3152 @qualifier TLS
3153 *///=========================================================================
3154 u_char getsockopt_u_char(
3155 /// The level at which the option resides; typically @c SOL_SOCKET
3156 const int level,
3157 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3158 const int option) {
3159 u_char value = 0;
3160 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3161 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3162 return value;
3163 }; // -x- u_char getsockopt_u_char -x-
3164
3165 /*======================================================================*//**
3166 @brief
3167 Get socket option details in the form of a structure.
3168 @copydetails getsockopt_int(const int, const int);
3169 @returns socket option structure wrapped in std::shared_ptr
3170 @qualifier TLS
3171 *///=========================================================================
3172 std::shared_ptr<linger> getsockopt_linger(
3173 /// The level at which the option resides; typically @c SOL_SOCKET
3174 const int level,
3175 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3176 const int option) {
3177 std::shared_ptr value = std::make_shared<linger>();
3178 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3179 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3180 return value;
3181 }; // -x- linger getsockopt_linger -x-
3182
3183 /*======================================================================*//**
3184 @copydoc getsockopt_linger(const int, const int)
3185 @qualifier TLS
3186 *///=========================================================================
3187 std::shared_ptr<timeval> getsockopt_timeval(
3188 /// The level at which the option resides; typically @c SOL_SOCKET
3189 const int level,
3190 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3191 const int option) {
3192 std::shared_ptr value = std::make_shared<timeval>();
3193 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3194 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3195 return value;
3196 }; // -x- timeval getsockopt_timeval -x-
3197
3198 /*======================================================================*//**
3199 @copydoc getsockopt_linger(const int, const int)
3200 @qualifier TLS
3201 *///=========================================================================
3202 std::shared_ptr<in_addr> getsockopt_in_addr(
3203 /// The level at which the option resides; typically @c SOL_SOCKET
3204 const int level,
3205 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3206 const int option) {
3207 std::shared_ptr value = std::make_shared<in_addr>();
3208 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3209 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3210 return value;
3211 }; // -x- in_addr getsockopt_in_addr -x-
3212
3213 /*======================================================================*//**
3214 @copydoc getsockopt_linger(const int, const int)
3215 @qualifier TLS
3216 *///=========================================================================
3217 std::shared_ptr<ip_mreq> getsockopt_ip_mreq(
3218 /// The level at which the option resides; typically @c SOL_SOCKET
3219 const int level,
3220 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3221 const int option) {
3222 std::shared_ptr value = std::make_shared<ip_mreq>();
3223 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3224 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3225 return value;
3226 }; // -x- ip_mreq getsockopt_ip_mreq -x-
3227
3228 /*======================================================================*//**
3229 @copydoc getsockopt_linger(const int, const int)
3230 @qualifier TLS
3231 *///=========================================================================
3232 std::shared_ptr<ip_mreq_source> getsockopt_ip_mreq_source(
3233 /// The level at which the option resides; typically @c SOL_SOCKET
3234 const int level,
3235 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3236 const int option) {
3237 std::shared_ptr value = std::make_shared<ip_mreq_source>();
3238 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3239 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3240 return value;
3241 }; // -x- ip_mreq_source getsockopt_ip_mreq_source -x-
3242
3243 /*======================================================================*//**
3244 @copydoc getsockopt_linger(const int, const int)
3245 @qualifier TLS
3246 *///=========================================================================
3247 std::shared_ptr<icmp6_filter> getsockopt_icmp6_filter(
3248 /// The level at which the option resides; typically @c SOL_SOCKET
3249 const int level,
3250 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3251 const int option) {
3252 std::shared_ptr value = std::make_shared<icmp6_filter>();
3253 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3254 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3255 return value;
3256 }; // -x- icmp6_filter getsockopt_icmp6_filter -x-
3257
3258 /*======================================================================*//**
3259 @copydoc getsockopt_linger(const int, const int)
3260 @qualifier TLS
3261 *///=========================================================================
3262 std::shared_ptr<sockaddr_in6> getsockopt_sockaddr_in6(
3263 /// The level at which the option resides; typically @c SOL_SOCKET
3264 const int level,
3265 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3266 const int option) {
3267 std::shared_ptr value = std::make_shared<sockaddr_in6>();
3268 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3269 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3270 return value;
3271 }; // -x- sockaddr_in6 getsockopt_sockaddr_in6 -x-
3272
3273 /*======================================================================*//**
3274 @copydoc getsockopt_linger(const int, const int)
3275 @qualifier TLS
3276 *///=========================================================================
3277 std::shared_ptr<ip6_mtuinfo> getsockopt_ip6_mtuinfo(
3278 /// The level at which the option resides; typically @c SOL_SOCKET
3279 const int level,
3280 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3281 const int option) {
3282 std::shared_ptr value = std::make_shared<ip6_mtuinfo>();
3283 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3284 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3285 return value;
3286 }; // -x- ip6_mtuinfo getsockopt_ip6_mtuinfo -x-
3287
3288 /*======================================================================*//**
3289 @copydoc getsockopt_linger(const int, const int)
3290 @qualifier TLS
3291 *///=========================================================================
3292 std::shared_ptr<ipv6_mreq> getsockopt_ipv6_mreq(
3293 /// The level at which the option resides; typically @c SOL_SOCKET
3294 const int level,
3295 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3296 const int option) {
3297 std::shared_ptr value = std::make_shared<ipv6_mreq>();
3298 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3299 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3300 return value;
3301 }; // -x- ipv6_mreq getsockopt_ipv6_mreq -x-
3302
3303 /*======================================================================*//**
3304 @copydoc getsockopt_linger(const int, const int)
3305 @qualifier TLS
3306 *///=========================================================================
3307 std::shared_ptr<group_req> getsockopt_group_req(
3308 /// The level at which the option resides; typically @c SOL_SOCKET
3309 const int level,
3310 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3311 const int option) {
3312 std::shared_ptr value = std::make_shared<group_req>();
3313 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3314 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3315 return value;
3316 }; // -x- group_req getsockopt_group_req -x-
3317
3318 /*======================================================================*//**
3319 @copydoc getsockopt_linger(const int, const int)
3320 @qualifier TLS
3321 *///=========================================================================
3322 std::shared_ptr<group_source_req> getsockopt_group_source_req(
3323 /// The level at which the option resides; typically @c SOL_SOCKET
3324 const int level,
3325 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3326 const int option) {
3327 std::shared_ptr value = std::make_shared<group_source_req>();
3328 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3329 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3330 return value;
3331 }; // -x- group_source_req getsockopt_group_source_req -x-
3332
3333 /*======================================================================*//**
3334 @copydoc getsockopt_linger(const int, const int)
3335 @qualifier TLS
3336 *///=========================================================================
3337 template<class T> std::shared_ptr<T> getsockopt_other(
3338 /// The level at which the option resides; typically @c SOL_SOCKET
3339 const int level,
3340 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3341 const int option) {
3342 std::shared_ptr value = std::make_shared<T>();
3343 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3344 if (__debug) __debug_sockopt_other("getsockopt_other(socket{0x", level, option, socklen_t(sizeof(value)));
3345 return value;
3346 }; // -x- T getsockopt_other -x-
3347
3348 /*======================================================================*//**
3349 @brief
3350 Get underlying socket's address as a std::string, for sockets in one of the
3351 supported families:
3352
3353 - AF_INET (IPv4)
3354 - AF_INET6 (IPv6)
3355 - AF_UNIX (Domain socket path)
3356 - AF_PACKET (Ethernet node/mac. address)
3357
3358 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3359 (e.g., because the @c family doesn't utilize or support an address
3360 {or the format isn't known}
3361 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3362 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3363
3364 @returns string representation of underlying socket's address
3365 @see inet_ntop(sockaddr_storage*) static method
3366 @qualifier POSIX
3367 @qualifier TLS
3368 *///=========================================================================
3369 const std::string inet_ntop() { return inet_ntop((sockaddr_storage*)&__socket_addr); } // -x- std::string inet_ntop -x-
3370
3371 /*======================================================================*//**
3372 @brief
3373 Get specified "sockaddr_storage" structure's address as a std::string, for
3374 sockets in one of the supported families:
3375
3376 - AF_INET (IPv4)
3377 - AF_INET6 (IPv6)
3378 - AF_UNIX (Domain socket path)
3379 - AF_PACKET (Ethernet node/mac. address)
3380
3381 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3382 (e.g., because the @c family doesn't utilize or support an address
3383 {or the format isn't known}
3384 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3385 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3386
3387 @returns string representation of underlying socket's address
3388 @see inet_ntop() non-static method
3389 @qualifier POSIX
3390 @qualifier TLS
3391 *///=========================================================================
3392 static const std::string inet_ntop(
3393 /// Source structure that [should] contain address data
3394 sockaddr_storage* sa) {
3395 std::string str;
3396 switch (sa->ss_family) {
3397 case AF_INET: // IPv4 address
3398 {
3399 char ntop[sizeof(sockaddr_in)]{0};
3400 const char* rc = ::inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), ntop, sizeof(ntop));
3401 str = std::string(ntop);
3402 }
3403 break;
3404 case AF_INET6: // IPv6 address
3405 { // Debug
3406 char ntop[sizeof(sockaddr_in6)]{0};
3407 const char* rc = ::inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), ntop, sizeof(ntop));
3408 str = std::string(ntop);
3409 }
3410 break;
3411 case AF_UNIX: // UNIX (path) domain socket
3412 str = std::string(((struct sockaddr_un *)sa)->sun_path);
3413 break;
3414 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
3415 // case /*AF_LINK* /18: // Link layer interface (arp)
3416 // break;
3417 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3418 str = to_mac(((struct sockaddr_ll *)sa)->sll_addr);
3419 break;
3420 default: // Everything else
3421 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY");
3422 } // -x- switch family -x-
3423 return str;
3424 }; // -x- std::string inet_ntop -x-
3425
3426 /*======================================================================*//**
3427 @brief
3428 Find out whether an internal read buffer was allocated (this is most likely
3429 triggered by an attempt to read a line of text).
3430 @note
3431 The buffer_size() methods report on how much memory was allocated for the
3432 internal read buffer or to set its size (in bytes).
3433 @returns TRUE = an internal read buffer was allocated
3434 @returns FALSE = an internal read buffer was not allocated
3435 @see buffer_size
3436 @see buffer_size(const size_t nbytes)
3437 @qualifier TLS
3438 *///=========================================================================
3439 const bool is_buffered() noexcept { return __buffer != nullptr; }; // -x- bool is_buffered -x-
3440
3441 /*======================================================================*//**
3442 @brief
3443 Find out whether the underlying socket is not open (which may not be the same
3444 as specifically "closed" since a newly instantiated empty socket begins in a
3445 "not open" state despite the underlying socket not having been closed).
3446 @returns TRUE = not open
3447 @returns FALSE = open
3448 @see is_open()
3449 @qualifier TLS
3450 *///=========================================================================
3451 const bool is_closed() noexcept { return !__socket_open; }; // -x- bool is_closed -x-
3452
3453 /*======================================================================*//**
3454 @brief
3455 Find out whether the underlying socket is connected with/to an endpoint.
3456 @returns TRUE = open
3457 @returns FALSE = not open
3458 @qualifier TLS
3459 *///=========================================================================
3460 const bool is_connected() noexcept { return __socket_connected; }; // -x- bool is_connected -x-
3461
3462 /*======================================================================*//**
3463 @brief
3464 Find out whether the default byte order for this host is LSB (small endian).
3465 @note
3466 If you're trying to choose which endian type to use when designing a new
3467 internet protocol, then big endian is normally the better option. However,
3468 if your new protocol will only be used by hardware that all share the same
3469 endianness, then that endianness is probably the more optimal option since it
3470 will translate to an overall lesser consumption of CPU cycles by reducing or
3471 eliminating endianness conversions.
3472 @returns TRUE = LSB (little endian)
3473 @returns FALSE = MSB (big endian / network byte order)
3474 @qualifier TLS
3475 *///=========================================================================
3476 const bool is_endian_lsb() noexcept { return !__endian_is_msb; }; // -x- bool is_endian_lsb -x-
3477
3478 /*======================================================================*//**
3479 @brief
3480 Find out whether the default byte order for this host is MSB (big endian).
3481 @note
3482 Big endian is the standard known as "network byte order" that's also used in
3483 various header fields in internet packets.
3484 @n@n
3485 If you're trying to choose which endian type to use when designing a new
3486 internet protocol, then big endian is normally the better option. However,
3487 if your new protocol will only be used by hardware that all share the same
3488 endianness, then that endianness is probably the more optimal option since it
3489 will translate to an overall lesser consumption of CPU cycles by reducing or
3490 eliminating endianness conversions.
3491 @returns TRUE = MSB (big endian / network byte order)
3492 @returns FALSE = LSB (little endian)
3493 @qualifier TLS
3494 *///=========================================================================
3495 const bool is_endian_msb() noexcept { return __endian_is_msb; }; // -x- bool is_endian_msb -x-
3496
3497 /*======================================================================*//**
3498 @brief
3499 Find out if the EoL adoption policy is enabled for the @ref recvline() method
3500 (see the @ref eol_adoption method to find out how the dynamically-detected
3501 EoL sequence gets adopted, and under what conditions).
3502 @returns TRUE = EoL adoption is enabled
3503 @returns FALSE = EoL adoption is disabled
3504 @see eol
3505 @see eol_adoption
3506 @see eol_index
3507 @see recvline
3508 @see sendline
3509 @see send_eol
3510 @qualifier TLS
3511 *///=========================================================================
3512 const bool is_eol_adoption() noexcept {
3513 return __eol_adoption;
3514 }; // -x- bool is_eol_adoption -x-
3515
3516 /*======================================================================*//**
3517 @brief
3518 Find out if the EoL substitution policy is enabled for the @ref printf(),
3519 @ref printfline(), @ref vprintf(), and @ref vprintfline() methods.
3520 @returns TRUE = EoL substitution is enabled
3521 @returns FALSE = EoL substitution is disabled
3522 @see eol
3523 @see eol_fix_printf
3524 @see printf
3525 @see printfline
3526 @see sendline
3527 @see send_eol
3528 @see vprintf
3529 @see vprintfline
3530 @qualifier TLS
3531 *///=========================================================================
3532 const bool is_eol_fix_printf() noexcept {
3533 return __eol_fix_printf;
3534 }; // -x- bool is_eol_fix_printf -x-
3535
3536 /*======================================================================*//**
3537 @brief
3538 Find out whether the underlying socket is open.
3539 @returns TRUE = open
3540 @returns FALSE = not open
3541 @see is_closed()
3542 @qualifier TLS
3543 *///=========================================================================
3544 const bool is_open() noexcept { return __socket_open; }; // -x- bool is_open -x-
3545
3546 /*======================================================================*//**
3547 @brief
3548 Find out whether encrypted communications is enabled or disabled.
3549 @returns TRUE = encrypted communications is enabled
3550 @returns FALSE = encrypted communications is disabled
3551 @see tls(bool, TLS_FLAGS)
3552 @qualifier TLS
3553 *///=========================================================================
3554 const bool is_tls() noexcept { return __tls; }; // -x- bool is_tls -x-
3555
3556 /*======================================================================*//**
3557 @brief
3558 Find out whether TLS context is in TLS_CLIENT mode.
3559 @returns TRUE = TLS context is in TLS_CLIENT mode
3560 @returns FALSE = TLS context is in TLS_SERVER mode
3561 @see TLS_CLIENT
3562 @see tls()
3563 @qualifier TLS
3564 *///=========================================================================
3565 const bool is_tls_client_mode() noexcept { return !__tls_server_mode; }; // -x- bool is_tls_client_mode -x-
3566
3567 /*======================================================================*//**
3568 @brief
3569 Find out whether egress from encryption (to unencrypted mode) is allowed.
3570 @returns TRUE = egress from encrypted communications is allowed
3571 @returns FALSE = egress from encrypted communications is not allowed
3572 @see TLS_NO_EGRESS
3573 @see tls()
3574 @qualifier TLS
3575 *///=========================================================================
3576 const bool is_tls_egress_okay() noexcept { return __tls_egress; }; // -x- bool is_tls_egress_okay -x-
3577
3578 /*======================================================================*//**
3579 @brief
3580 Find out whether encrypted communications is exclusive.
3581 @returns TRUE = encrypted communications is exclusive
3582 @returns FALSE = encrypted communications is not exclusive
3583 @see tls()
3584 @qualifier TLS
3585 *///=========================================================================
3586 const bool is_tls_exclusive() noexcept { return __tls_exclusive; }; // -x- bool is_tls_exclusive -x-
3587
3588 /*======================================================================*//**
3589 @brief
3590 Find out whether ingress to encryption (from unencrypted mode) is allowed.
3591 @returns TRUE = ingress to encrypted communications is allowed
3592 @returns FALSE = ingress to encrypted communications is not allowed
3593 @see TLS_NO_INGRESS
3594 @see tls()
3595 @qualifier TLS
3596 *///=========================================================================
3597 const bool is_tls_ingress_okay() noexcept { return __tls_ingress; }; // -x- bool is_tls_ingress_okay -x-
3598
3599 /*======================================================================*//**
3600 @brief
3601 Find out whether TLS context is in TLS_SERVER mode.
3602 @returns TRUE = TLS context is in TLS_SERVER mode
3603 @returns FALSE = TLS context is in TLS_CLIENT mode
3604 @see TLS_SERVER
3605 @see tls()
3606 @qualifier TLS
3607 *///=========================================================================
3608 const bool is_tls_server_mode() noexcept { return __tls_server_mode; }; // -x- bool is_tls_server_mode -x-
3609
3610 /*======================================================================*//**
3611 @brief
3612 Find out whether SNI (Server Name Identifier) is enabled (configured, which
3613 implies that an internal callback function was also set up).
3614 @returns TRUE = SNI is enabled
3615 @returns FALSE = SNI is disabled
3616 @see tls_sni()
3617 @qualifier TLS
3618 *///=========================================================================
3619 const bool is_tls_sni() noexcept { return __tls_sni != nullptr; }; // -x- bool is_tls_sni -x-
3620
3621 /*======================================================================*//**
3622 @brief
3623 Find out whether SNI (Server Name Identifier) was matched, which means that
3624 we're using one of the supplementary TLS certificates that are included in
3625 the associated @ref rsocket_sni object as separate TLS contexts.
3626
3627 When this method returns @c TRUE, it means the @c OpenSSL callback (which
3628 occurs during the TLS handshake process) completed the TLS handshake with one
3629 of the TLS certificates that's included in this @c rsocket's @ref rsocket_sni
3630 object instead of the primary TLS certificate assigned to this @c rsocket,
3631 and this @c rsocket is using the respective TLS context instead of the
3632 primary (default) one.
3633 @returns TRUE = SNI was matched
3634 @returns FALSE = SNI wasn't matched
3635 @see name_sni
3636 @see tls_sni
3637 @qualifier TLS
3638 *///=========================================================================
3639 const bool is_tls_sni_match() noexcept { return __tls_sni_match; }; // -x- bool is_tls_sni_match -x-
3640
3641 /*======================================================================*//**
3642 @brief
3643 Enable listening mode for this rsocket to prepare it to accept() new inbound
3644 connections.
3645
3646 The backlog defaults to SOMAXCONN (4096 on Linux, and 128 on older systems),
3647 which is common on most systems. If a higher value is supplied that exceeds
3648 @c kern.somaxconn, the kernel will automatically override the backlog with
3649 the value stored in @c kern.somaxconn without generating any errors or
3650 warnings.
3651
3652 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
3653 @throws randolf::rex::xEADDRINUSE No ephemeral ports are available for
3654 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
3655 but it really isn't)
3656 @throws randolf::rex::xEBADF The underlying socket is not open
3657 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3658 doesn't refer to a socket
3659 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
3660 @returns The same rsocket object so as to facilitate stacking
3661 @see accept()
3662 @see accept_sp()
3663 @see accept4()
3664 @see accept4_sp()
3665 @see backlog()
3666 @see bind()
3667 @qualifier POSIX
3668 @qualifier TLS
3669 *///=========================================================================
3670 rsocket* listen(
3671 /// Backlog queue size (0 = uses rsocket's default; see @ref backlog for more
3672 /// details about this); specifying a non-zero backlog also updates rocket's
3673 /// internal default (SOMAXCONN is 4096 on Linux, and 128 on older systems)
3674 int backlog = 0) {
3675 if (__debug) debug("listen(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3676 + ", " + std::to_string(backlog)
3677 + ");");
3678
3679 // --------------------------------------------------------------------------
3680 // Use rsocket's default backlog if not specified, otherwise update it
3681 // with the new value specified here.
3682 // --------------------------------------------------------------------------
3683 if (backlog = 0) backlog = __socket_backlog;
3684 else __socket_backlog = backlog;
3685
3686 // --------------------------------------------------------------------------
3687 // Configure underlying socket to queue up to "backlog" inbound connections.
3688 // --------------------------------------------------------------------------
3689 __rc_check(::listen(__socket_fd, backlog));
3690
3691 return this;
3692 }; // -x- rsocket* listen -x-
3693
3694 /*======================================================================*//**
3695 @brief
3696 Convert an IPv4 address, IPv6 address, ethernet packet, or UNIX domain
3697 socket to a sockaddr_storage structure.
3698
3699 If service_name is an absolute path (that begins with a "/" charcter) and the
3700 family is set to AF_UNSPEC (the default), then the resulting family will be
3701 set to AF_UNIX.
3702
3703 @par Notes
3704 This method utilizes the results of getaddrinfo().
3705
3706 Other families like AF_LINK and AF_PACKET should work, but haven't been
3707 tested thoroughly. The additional support we provide for IPv4 and IPv6
3708 addresses is to copy the port number into the resulting structure (as a
3709 convenience).
3710
3711 @post
3712 The resulting sockaddr_storage structure is wrapped in std::shared_ptr (a C++
3713 smart pointer that aids in the prevention of resource leaks).
3714
3715 @par Threads
3716 This method is thread-safe.
3717
3718 @throws randolf::rex::xEAI_ADDRFAMILY If specified network host doesn't have
3719 any addresses in the specified address family
3720 @throws randolf::rex::xEAI_AGAIN Temporary failure code from DNS server (try
3721 again later)
3722 @throws randolf::rex::xEAI_BADFLAGS Invalid flags in @c hints.ai_flags (or
3723 `hints.ai_flags` included @c AI_CANONNAME with @c nullptr as @c name)
3724 @throws randolf::rex::xEAI_FAIL Permanent failure code from DNS server
3725 @throws randolf::rex::xEAI_FAMILY The specified family is not supported
3726 @throws randolf::rex::xEAI_MEMORY Out of memory
3727 @throws randolf::rex::xEAI_NONAME If node_name is nullptr or an empty string
3728 @throws randolf::rex::xEAI_SERVICE The specified service is not available
3729 for the specified socket type
3730 @throws randolf::rex::xEAI_SOCKTYPE The specified socket type is not
3731 supported
3732 @throws randolf::rex::xEAI_SYSTEM Other system error (use errno to determine
3733 what the error is, then run use @ref randolf::rex::rex::mk_exception
3734 to throw the correct exception)
3735
3736 @returns sockaddr_storage structure
3737 @see getsockaddr
3738 *///=========================================================================
3739 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
3740 /// IP address or UNIX domain socket address to convert
3741 const char* node_name,
3742 /// Port number (or service name used by some other families)
3743 const char* service_name = nullptr,
3744 /// Optional pointer to a helpful addrinfo structure
3745 const addrinfo* hints = nullptr) {
3746
3747 // --------------------------------------------------------------------------
3748 // Initial sanity checks.
3749 // --------------------------------------------------------------------------
3750 if (node_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME); // if (node_name == nullptr && service_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME);
3751
3752 // --------------------------------------------------------------------------
3753 // Handle any special cases.
3754 // --------------------------------------------------------------------------
3755 switch (hints == nullptr ? AF_UNSPEC : hints->ai_family) { // Default to AF_UNSPEC in the absence of hints
3756 case AF_UNSPEC:
3757 if (node_name[0] != '/') break;
3758 // Next entry MUST be "case AF_UNIX" for this fall-through to work
3759 case AF_UNIX:
3760 {
3761 // For some unknown reason, clang++ can't compile this line that
3762 // g++ has absolutely no trouble with at all:
3763 // std::shared_ptr sa = std::make_shared<sockaddr_storage>(AF_UNIX);
3764 // So, after wasting more than a year trying to figure out what the
3765 // hundreds of lines of cryptic errors meant, I eventually gave up
3766 // on clang++ for being such a dumbfuck, and used this work-around:
3767 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3768 sa->ss_family = AF_UNIX;
3769 std::strcpy(((struct sockaddr_un *)sa.get())->sun_path, node_name); // Copy path
3770 return sa;
3771 }
3772 break;
3773 } // -x- switch hints->family -x-
3774
3775 // --------------------------------------------------------------------------
3776 // Acquire addrinfo[] linked-list array.
3777 // --------------------------------------------------------------------------
3778 addrinfo* __addr_result; // This is temporary
3779//std::cout << " Making socket structure... node_name=" << node_name << " ...hints: ai_family=" << hints->ai_family << " ai_socktype=" << hints->ai_socktype << " ai_flags=" << hints->ai_flags << std::endl;
3780 randolf::rex::mk_exception("", getaddrinfo(node_name,
3781 service_name,
3782 hints,
3783 &__addr_result));
3784
3785 // --------------------------------------------------------------------------
3786 // Find first valid addrinfo[] array by trying to open a socket with it.
3787 // --------------------------------------------------------------------------
3788 for (addrinfo* ar = __addr_result; ar != nullptr; ar = ar->ai_next) {
3789//std::cout << " ... ai_family=" << ar->ai_family << " ai_socktype=" << ar->ai_socktype << " ai_flags=" << ar->ai_flags << std::endl; // *******************
3790// ***************************** TODO: Make sure this loop is working properly
3791 } // -x- for ar -x-
3792//std::cout << " !!! ai_family=" << __addr_result->ai_family << std::endl; // *******************
3793
3794 // --------------------------------------------------------------------------
3795 // Copy the address to "sa" and return it. The "addr" data for IPv4 and IPv6
3796 // addresses include the TCP/UDP port number (service) in first two bytes as
3797 // as an unsigned integer (u_int16), followed immediately by the IP address.
3798 //
3799 // We're taking the first record only. We assume that hints{} is defined on
3800 // an as-needed basis.
3801 //
3802 // Note: AF_LINK and AF_PACKET are not widely supported, but we've made an
3803 // effort to accomodate their usage.
3804 // --------------------------------------------------------------------------
3805 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3806 switch (__addr_result->ai_family) {
3807 case AF_INET: // IPv4 address
3808 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_in));
3809 break;
3810 case AF_INET6: // IPv6 address
3811 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_in6));
3812 break;
3813 /* // We're handling this earlier as a special case: TODO: Move special handling to here
3814 case AF_UNIX: // UNIX (path) domain socket
3815 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_un));
3816 break;
3817 */
3818 case /*AF_LINK*/18: // Link layer interface (arp)
3819 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_dl));
3820 break;
3821 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3822 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_ll));
3823 break;
3824 default: // Everything else
3825 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_storage));
3826 } // -x- switch family -x-
3827 freeaddrinfo(__addr_result); // We don't need this anymore
3828 return sa;
3829 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
3830
3831 /*======================================================================*//**
3832 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
3833 *///=========================================================================
3834 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
3835 /// IP address or UNIX domain socket address to convert
3836 const char* node_name,
3837 /// Port number
3838 const u_int16_t service_name,
3839 /// Optional pointer to a helpful addrinfo structure
3840 const addrinfo* hints = nullptr) {
3841 std::string port = std::to_string(service_name);
3842 return mk_sockaddr_storage(node_name, port.c_str(), hints);
3843 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
3844
3845 /*======================================================================*//**
3846 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
3847 *///=========================================================================
3848 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
3849 /// IP address or UNIX domain socket address to convert
3850 const std::string node_name,
3851 /// Port number
3852 const u_int16_t service_name,
3853 /// Optional pointer to a helpful addrinfo structure
3854 const addrinfo* hints = nullptr) {
3855 std::string port = std::to_string(service_name);
3856 return mk_sockaddr_storage(node_name.c_str(), port.c_str(), hints);
3857 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
3858
3859 /*======================================================================*//**
3860 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
3861 *///=========================================================================
3862 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
3863 /// IP address or UNIX domain socket address to convert
3864 const std::string node_name,
3865 /// Port number (or server name used by some other families)
3866 const std::string service_name,
3867 /// Optional pointer to a helpful addrinfo structure
3868 const addrinfo* hints = nullptr) {
3869 return mk_sockaddr_storage(node_name.c_str(), service_name.c_str(), hints);
3870 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
3871
3872 /*======================================================================*//**
3873 @brief
3874 Specify a name for this rsocket.
3875
3876 This is an arbitrary name that is entirely optional, and should be regarded
3877 as similar to the naming of threads.
3878 @returns The same rsocket object so as to facilitate stacking
3879 *///=========================================================================
3880 rsocket* name(
3881 /// Name to assign to this @c rsocket
3882 const std::string name) noexcept {
3883 // const std::lock_guard<std::mutex> lock(__name);
3884 __name = name;
3885 return this;
3886 }; // -x- rsocket* name -x-
3887
3888 /*======================================================================*//**
3889 @brief
3890 Find out what this rsocket's name is.
3891
3892 The built-in SNI mechanism will overwrite this data to indicate the hostname
3893 that was specified by the TLS-encrypted endpoint that triggered an internal
3894 SNI callback -- use the @ref name_sni() method to find out which hostname is
3895 actually being used by TLS.
3896 @returns The name of this rsocket (or an empty @c std::string if this rsocket
3897 doesn't have a name)
3898 @see name_sni
3899 @see tls_sni
3900 *///=========================================================================
3901 std::string name() noexcept {
3902 // const std::lock_guard<std::mutex> lock(__name);
3903 return __name;
3904 }; // -x- std::string name -x-
3905
3906 /*======================================================================*//**
3907 @brief
3908 Find out what this rsocket's actual TLS SNI hostname is.
3909
3910 This is the exact - or closest-matching (in the case of wildcards) - hostname
3911 associated with an actual TLS certificate that was provided in the configured
3912 @ref rsocket_sni object.
3913 @returns The hostname associated with the TLS certificate selected with SNI
3914 @see name
3915 @see tls_sni
3916 @qualifier TLS
3917 *///=========================================================================
3918 std::string name_sni() noexcept {
3919 return __name_sni;
3920 }; // -x- std::string name_sni -x-
3921
3922 /*======================================================================*//**
3923 @brief
3924 Get socket I/O statistics from internally-tracked socket I/O counters.
3925
3926 The number of bytes transmitted and received is tracked internally, so that
3927 the information can be used later in logging. These statistics are available
3928 at all times, but for logging purposes it makes the most sense to copy this
3929 information after the rsocket is closed, at which time the final statistics
3930 will continue to be available until the rsocket's destructor takes over.
3931
3932 @par Threads
3933 This method is threadsafe.
3934 @returns rsocket_io wrapped in a std::shared_ptr object to help ease memory
3935 management efforts
3936 @see net_io_final
3937 @see net_io_update
3938 @see printf
3939 @qualifier TLS
3940 *///=========================================================================
3941 std::shared_ptr<rsocket_io> net_io() noexcept {
3942 std::shared_ptr stats = std::make_shared<rsocket_io>();
3943 stats->bytes_rx = __bytes_rx;
3944 stats->bytes_tx = __bytes_tx;
3945 stats->crypt_rx = __crypt_rx;
3946 stats->crypt_tx = __crypt_tx;
3947 stats->is_final = false;
3948 return stats;
3949 }; // -x- std::shared_ptr<rsocket_io> net_io -x-
3950
3951 /*======================================================================*//**
3952 @brief
3953 Get socket I/O statistics from internally-tracked socket I/O counters as a
3954 pre-formatted std::string object.
3955
3956 The number of bytes transmitted and received is tracked internally, so that
3957 the information can be used later in logging. These statistics are available
3958 at all times, but for logging purposes it makes the most sense to copy this
3959 information after the rsocket is closed, at which time the final statistics
3960 will continue to be available until the rsocket's destructor takes over.
3961
3962 @par Formatting
3963 The format string may contain any characters, with only instances of the
3964 following case-sensitive command sequences to be interpolated accordingly
3965 (invalid commands will be ignored and will remain in place, unmodified):
3966
3967 <table cellpadding=8 cellspacing=0 border=1>
3968 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
3969 <tr><td>$$</td><td>One dollar sign ("$")</td><td>@e N/A</td></tr>
3970 <tr><td>$aR</td><td>Total (all) bytes received</td><td>bytes_rx @c + crypt_rx</td></tr>
3971 <tr><td>$aT</td><td>Total (all) bytes transmitted</td><td>bytes_tx @c + crypt_tx</td></tr>
3972 <tr><td>$bR</td><td>Unencrypted bytes received</td><td>bytes_rx</td></tr>
3973 <tr><td>$bT</td><td>Unencrypted bytes transmitted</td><td>bytes_tx</td></tr>
3974 <tr><td>$cR</td><td>Encrypted bytes recevied</td><td>crypt_rx</td></tr>
3975 <tr><td>$cT</td><td>Encrypted bytes transmitted</td><td>crypt_tx</td></tr>
3976 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
3977 </table>
3978
3979 Why do we use dollar signs instead of percent symbols, like other formatting
3980 functions like printf() does? So that the format string won't be in conflict
3981 with any percent-prefixed commands in printf() and similar funcions. This
3982 means that a printf() format string can be put through a first pass here to
3983 get the needed statistics interpolated into the needed positions.
3984
3985 @par Threads
3986 This method is threadsafe.
3987 @returns An interpolated format string as an std::string object.
3988 @see net_io_final
3989 @see net_io_update
3990 @see printf
3991 @qualifier TLS
3992 *///=========================================================================
3993 std::string net_io(
3994 /// Format string
3995 const char* format,
3996 /// Length of format string (in bytes), or 0 to auto-detect length if format string is an ASCIIZ string
3997 size_t len = 0,
3998 /// Pointer to an @ref rsocket_io structure to read the statistics from (nullptr = use internal copy of current statistics)
3999 // TODO: Convert this to a static method and create a new method without the "len" parameter that just calls the static method with the pointer to the internal "addr" buffer (this will make it easier to call this method in a static fashion later without incurring any of the overhead of instantiation)
4000 rsocket_io* addr = nullptr) noexcept {
4001
4002 // --------------------------------------------------------------------------
4003 // Measure size of format string if an ASCIIZ string was indicated.
4004 // --------------------------------------------------------------------------
4005 if (len == 0) len = std::strlen(format);
4006
4007 // --------------------------------------------------------------------------
4008 // Internal variables.
4009 // --------------------------------------------------------------------------
4010 rsocket_io* data = addr == nullptr ? net_io().get() : addr; // Copy current statistics so that
4011 std::string stats; // Formatted result
4012 ulong val; // Value (to be inserted)
4013 char c; // Current character
4014 std::string cmd; // Accumulated command (may be subsituted)
4015 bool flag_commas = false; // Thousands separators flag
4016 bool flag_right = false; // Flush-right flag
4017
4018 // --------------------------------------------------------------------------
4019 // Process format string and build resulting formatted stats string.
4020 //
4021 // This is designed to be fast, and I'm taking huge shortcuts here since the
4022 // commands are all the same size (except for the first one, which is only
4023 // two dollar sign characters). Processing in this loop should be quite fast
4024 // compared to using library functions to search for dollar sign characters
4025 // since data needs to be copied anyway -- why run a loop over the text twice
4026 // when can get away with doing it faster by doing it only once? This is an
4027 // optimized approach, although it probably could be even faster by not using
4028 // std::string for temporary command storage (there are other ways to do this
4029 // but I think this should suffice for now since it isn't expected to be used
4030 // very often).
4031 // --------------------------------------------------------------------------
4032 for (int i = 0; i < len; i++) {
4033
4034 // --------------------------------------------------------------------------
4035 // First character (potentially).
4036 // --------------------------------------------------------------------------
4037 if ((c = format[i]) != '$') { // Not a dollar sign -- save it and move on
4038 stats.push_back(c);
4039 continue;
4040 } // -x- if !$ -x-
4041 cmd = c; // Start building up the command string
4042
4043 // --------------------------------------------------------------------------
4044 // Second character: Part 1 of 2
4045 //
4046 // TODO: Add support for "," commas, and "r" right-alignment.
4047 // --------------------------------------------------------------------------
4048 if (++i == len) continue; // End of format string, so we're done
4049 cmd.push_back(c = format[i]);
4050 if (c == '$') { // Consume the previous dollar sign so that $$ turns into $
4051 stats.push_back(c); // Add $ to stats string (we're only keeping one dollar sign)
4052 continue;
4053 } // -x- if $ -x-
4054 flag_commas = flag_right = false; // Reset these flags for a clean start
4055
4056 // --------------------------------------------------------------------------
4057 // Flag checks:
4058 // , = Include thousands separators (commas)
4059 // r = Flush right (leading spaces will be added)
4060 // --------------------------------------------------------------------------
4061 net_io_flags_loop:
4062 if (c == ',') {
4063 if (++i == len) continue; // End of format string, so we're done
4064 cmd.push_back(c = format[i]);
4065 flag_commas = true;
4066 goto net_io_flags_loop;
4067 } else if (c == 'r') {
4068 if (++i == len) continue; // End of format string, so we're done
4069 cmd.push_back(c = format[i]);
4070 flag_right = true;
4071 goto net_io_flags_loop;
4072 } // -x- if [,r] -x-
4073 // TODO: Do more
4074
4075 // --------------------------------------------------------------------------
4076 // Second character: Part 1 of 2
4077 // --------------------------------------------------------------------------
4078 if (c < 'a' || c > 'c') { // Not a, b, or c, so treat it as a regular character
4079 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4080 continue;
4081 } // -x- if !~[abc] -x-
4082
4083 // --------------------------------------------------------------------------
4084 // Third character.
4085 // --------------------------------------------------------------------------
4086 if (++i == len) continue; // End of format string, so we're done
4087 cmd.push_back(c = format[i]);
4088 if (c != 'R' && c != 'T') { // Not R or T, so treat it as a regular character
4089 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4090 continue;
4091 } // -x- if !~[RT] -x-
4092
4093 // --------------------------------------------------------------------------
4094 // Command processing. If the command is valid, then it will be interpolated
4095 // with its expected result by replacing it with the resulting value.
4096 // --------------------------------------------------------------------------
4097 //std::cout << "[" << cmd << "]"; // Debug
4098 if (cmd.ends_with("aR")) val = data->bytes_rx + data->crypt_rx;
4099 else if (cmd.ends_with("aT")) val = data->bytes_tx + data->crypt_tx;
4100 else if (cmd.ends_with("bR")) val = data->bytes_rx ;
4101 else if (cmd.ends_with("bT")) val = data->bytes_tx ;
4102 else if (cmd.ends_with("cR")) val = data->crypt_rx;
4103 else if (cmd.ends_with("cT")) val = data->crypt_tx;
4104 else { stats.append(cmd); continue; } // This is wrong, so ignore it
4105
4106 // --------------------------------------------------------------------------
4107 // Re-use cmd to generate formatted value.
4108 // --------------------------------------------------------------------------
4109 cmd.clear();
4110 cmd.resize(21); // Maximum length without commas (plus character zero): 18446744073709551615
4111 cmd.resize(sprintf(cmd.data(), flag_right ? "%20lu" : "%lu", val)); // The ::sprintf function can't use cmd.c_str() here
4112 if (flag_commas) cmd = randolf::rtools::insert_commas(cmd.c_str()); // Insert commas (this makes the string longer)
4113
4114 // --------------------------------------------------------------------------
4115 // Convert to std::string and add to the stats string.
4116 // --------------------------------------------------------------------------
4117 stats.append(cmd);
4118
4119 }; // -x- for i -x-
4120
4121 return stats;
4122 }; // -x- std::string net_io -x-
4123
4124 /*======================================================================*//**
4125 @brief
4126 Where the destructor should save final I/O statistics before this rsocket's
4127 resources are completely freed/deallocated.
4128 @attention
4129 Developers should take care to check that the @ref rsocket_io::is_final flag
4130 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
4131 since there's no guarantee that the destructor will necessarily be executed
4132 in a timely manner (this flag is set last, after all other statistics are
4133 copied into the structure while in a mutex-locked state).
4134 @returns The same rsocket object so as to facilitate stacking
4135 @see ~rsocket
4136 @see net_io
4137 @see net_io_update
4138 @see printf
4139 @qualifier TLS
4140 *///=========================================================================
4141 rsocket* net_io_final(
4142 /// Pointer to @ref rsocket_io structure (nullptr = disabled)
4143 rsocket_io* addr) noexcept {
4144 __io_final_addr = addr;
4145 return this;
4146 }; // -x- rsocket* net_io_final -x-
4147
4148 /*======================================================================*//**
4149 @brief
4150 Where the destructor should save current I/O statistics.
4151 @attention
4152 Statistics are copied into the structure while in a mutex-locked state, but
4153 the copy itself is not an overall atomic snapshot of the I/O statistics even
4154 though each statistic is copied atomically. This means that the actual
4155 statistics could be slightly different if updates occur independently (e.g.,
4156 due to activities in a different thread), but this likely won't matter since
4157 the anticipated use for these statistics is to display or otherwise present
4158 general statistical information to a user at regular intervals.
4159 @returns The same rsocket object so as to facilitate stacking
4160 @see ~rsocket
4161 @see net_io
4162 @see net_io_final
4163 @see printf
4164 @qualifier TLS
4165 *///=========================================================================
4166 rsocket* net_io_update(
4167 /// Pointer to @ref rsocket_io structure (nullptr = do nothing)
4168 rsocket_io* addr) noexcept {
4169
4170 // --------------------------------------------------------------------------
4171 // Copy statistics to final location. (We do this last in case there's an
4172 // issue with locking; there shouldn't be, but if there is then at least we
4173 // already we're not inadvertently hanging on to resources at this point that
4174 // need to be freed/closed anyway.)
4175 // --------------------------------------------------------------------------
4176 if (addr != nullptr) {
4177 addr->lock();
4178 addr->bytes_rx = __bytes_rx;
4179 addr->bytes_tx = __bytes_tx;
4180 addr->crypt_rx = __crypt_rx;
4181 addr->crypt_tx = __crypt_tx;
4182 addr->is_final = false;
4183 addr->unlock();
4184 } // -x- if addr -x-
4185
4186 return this;
4187 }; // -x- rsocket* net_io_update -x-
4188
4189 /*======================================================================*//**
4190 @brief
4191 Poll the underlying socket using the poll() method for data that's ready for
4192 receiving (default), etc.
4193
4194 @note
4195 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4196 select(), and accept()/accept4() when using multiple sockets.
4197
4198 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4199 part of the user address space
4200 @throws randolf::rex::xEINTR Interrupted by a signal
4201 @throws randolf::rex::xENOMEM Insufficient memory
4202 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4203 doesn't refer to a socket
4204 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4205 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4206 is a highly improbable chance that a timeout could still occur if the
4207 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4208 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4209 there's no new data
4210
4211 @returns The resulting (short)pollfd.revents bitfield
4212 @returns 0 if @c timeout_behaviour is set to TIMEOUT_ZERO
4213 @qualifier POSIX
4214 @qualifier TLS
4215 *///=========================================================================
4216 short poll(
4217 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4218 const short events = POLLIN,
4219 /// Number of milliseconds to wait
4220 const int timeout = 0,
4221 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4222 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4223 if (__debug) debug("poll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4224 + ", pollfd.events=" + std::to_string(events)
4225 + ", timeout=" + std::to_string(timeout)
4226 + ");");
4227 struct pollfd fds{__socket_fd, events, 0};
4228 if (__rc_check(::poll(&fds, 1, timeout)) == 0) {
4229 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4230 else return 0;
4231 } // -x- if 0 -x-
4232 return fds.revents;
4233 }; // -x- short poll -x-
4234
4235 /*======================================================================*//**
4236 @brief
4237 Get port number associated with underlying socket descriptor/handle.
4238
4239 Returns 0 if:
4240 - an emphemeral port number assignment (dynamic) is intended
4241 - the underlying socket details are not defined (e.g., in the case of an
4242 empty rsocket instantiation)
4243 - port numbers are not supported/utilized by the current family (e.g., not
4244 AF_INET {IPv4} or AF_INET6 {IPv6})
4245
4246 The port number can be set in most constructors, or via one of the socket()
4247 or bind() methods.
4248 @returns Port number (typically TCP and UDP, although some other families
4249 such as SCTP and DCCP also utilize port numbers)
4250 @see socket_family()
4251 @see socket_protocol()
4252 @see socket_type()
4253 @qualifier TLS
4254 *///=========================================================================
4255 const uint16_t port() noexcept {
4256 switch (__socket_addr.ss_family) {
4257 case AF_INET: // IPv4
4258 return ntohs(((sockaddr_in*)&__socket_addr)->sin_port);
4259 case AF_INET6: // IPv6
4260 return ntohs(((sockaddr_in6*)&__socket_addr)->sin6_port);
4261 default: // Everything else
4262 return 0;
4263 } // -x- switch __socket_addr.ss_family -x-
4264 }; // -x- uint16_t port -x-
4265
4266 /*======================================================================*//**
4267 @brief
4268 Poll the underlying socket using the ppoll() method for data that's ready for
4269 receiving (default), etc.
4270
4271 @note
4272 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4273 select(), and accept()/accept4() when using multiple sockets.
4274
4275 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4276 part of the user address space
4277 @throws randolf::rex::xEINTR Interrupted by a signal
4278 @throws randolf::rex::xENOMEM Insufficient memory
4279 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4280 doesn't refer to a socket
4281 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4282 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4283 is a highly improbable chance that a timeout could still occur if the
4284 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4285 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4286 there's no new data
4287
4288 @returns The resulting (short)pollfd.revents bitfield
4289 @qualifier POSIX
4290 @qualifier TLS
4291 *///=========================================================================
4292 short ppoll(
4293 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4294 const short events = POLLIN,
4295 /// Timeout
4296 const struct timespec* tmo_p = nullptr,
4297 /// Signal mask
4298 const sigset_t* sigmask = nullptr,
4299 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4300 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4301 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4302 + ", pollfd.events=" + std::to_string(events)
4303 + ", timespec{tv_sec=" + std::to_string(tmo_p->tv_sec)
4304 + ", tv_nsec=" + std::to_string(tmo_p->tv_nsec) + "}"
4305 + ", sigset_t"
4306 + ");");
4307 struct pollfd fds{__socket_fd, events, 0};
4308 if (__rc_check(::ppoll(&fds, 1, tmo_p, sigmask)) == 0) {
4309 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4310 else return 0;
4311 } // -x- if 0 -x-
4312 return fds.revents;
4313 }; // -x- short ppoll -x-
4314
4315 /*======================================================================*//**
4316 @brief
4317 Poll the underlying socket using the ppoll() method for data that's ready for
4318 receiving (default), etc.
4319
4320 @note
4321 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4322 select(), and accept()/accept4() when using multiple sockets.
4323
4324 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4325 part of the user address space
4326 @throws randolf::rex::xEINTR Interrupted by a signal
4327 @throws randolf::rex::xENOMEM Insufficient memory
4328 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4329 doesn't refer to a socket
4330 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4331 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4332 is a highly improbable chance that a timeout could still occur if the
4333 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4334 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4335 there's no new data
4336
4337 @returns The resulting (short)pollfd.revents bitfield
4338 @qualifier TLS
4339 *///=========================================================================
4340 short ppoll(
4341 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4342 const short events = POLLIN,
4343 /// Timeout in seconds
4344 const int tv_sec = 0,
4345 /// Timeout in nanoseconds
4346 const long tv_nsec = 0,
4347 /// Signal mask
4348 const sigset_t* sigmask = nullptr,
4349 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4350 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4351 struct timespec tmo_p{tv_sec, tv_nsec};
4352 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4353 + ", pollfd.events=" + std::to_string(events)
4354 + ", timespec{tv_sec=" + std::to_string(tmo_p.tv_sec)
4355 + ", tv_nsec=" + std::to_string(tmo_p.tv_nsec) + "}"
4356 + ", sigset_t"
4357 + ");");
4358 struct pollfd fds{__socket_fd, events, 0};
4359 if (__rc_check(::ppoll(&fds, 1, &tmo_p, sigmask)) == 0) {
4360 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4361 else return 0;
4362 } // -x- if 0 -x-
4363 return fds.revents;
4364 }; // -x- short ppoll -x-
4365
4366 /*======================================================================*//**
4367 @brief
4368 Send a formatted string to the @ref rsocket endpoint.
4369
4370 The @c format is described in the documentation for the POSIX or Standard C
4371 Library @c printf() function.
4372 @throws randolf::rex::xEBADF The underlying socket is not open
4373 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
4374 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
4375 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
4376 @throws randolf::rex::xENOMEM Insufficient memory
4377 @returns The same rsocket object so as to facilitate stacking
4378 @see eol_fix_printf
4379 @see is_eol_fix_printf
4380 @see net_io
4381 @see printfline
4382 @see vprintf
4383 @see vprintfline
4384 @qualifier POSIX
4385 @qualifier TLS
4386 *///=========================================================================
4387 rsocket* printf(
4388 /// Format string to use
4389 const char* format,
4390 /// Variadic arguments
4391 ...) {
4392 char* buf;
4393 ::va_list args;
4394 ::va_start(args, format);
4395 int rc = ::vasprintf(&buf, format, args);
4396 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
4397 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
4398 if (__eol_fix_printf && !__eol.empty()) {
4399 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
4400 ::free(buf);
4401 __send(str.c_str(), str.length());
4402 } else {
4403 try {
4404 __send(buf, rc);
4405 } catch (std::exception& e) { // Free buf then re-throw the exception
4406 ::free(buf); // Prevent memory leak when an exception is thrown
4407 throw;
4408 }
4409 } // -x- if __eol_fix_printf -x-
4410 return this;
4411 }; // -x- rsocket* printf -x-
4412
4413 /*======================================================================*//**
4414 @brief
4415 Send a formatted string to the @ref rsocket endpoint, and append an EoL
4416 sequence.
4417
4418 The @c format is described in the documentation for the POSIX or Standard C
4419 Library @c printf() function.
4420 @throws randolf::rex::xEBADF The underlying socket is not open
4421 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
4422 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
4423 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
4424 @throws randolf::rex::xENOMEM Insufficient memory
4425 @returns The same rsocket object so as to facilitate stacking
4426 @see eol
4427 @see eol_fix_printf
4428 @see is_eol_fix_printf
4429 @see net_io
4430 @see printf
4431 @see sendline
4432 @see vprintf
4433 @see vprintfline
4434 @qualifier TLS
4435 *///=========================================================================
4436 rsocket* printfline(
4437 /// Format string to use
4438 const char* format,
4439 /// Variadic arguments
4440 ...) {
4441 char* buf;
4442 ::va_list args;
4443 ::va_start(args, format);
4444 int rc = ::vasprintf(&buf, format, args);
4445 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
4446 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
4447 if (__eol_fix_printf && !__eol.empty()) {
4448 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
4449 .append(__eol);
4450 ::free(buf);
4451 __send(str.c_str(), str.length());
4452 } else {
4453 try {
4454 __sendline(buf, rc);
4455 } catch (std::exception& e) { // Free buf then re-throw the exception
4456 ::free(buf); // Prevent memory leak when an exception is thrown
4457 throw;
4458 }
4459 } // -x- if __eol_fix_printf -x-
4460 return this;
4461 }; // -x- rsocket* printfline -x-
4462
4463 /*======================================================================*//**
4464 @brief
4465 Receive data from the endpoint into a @c std::vector<char> that is allocated
4466 on-the-fly.
4467
4468 If nbytes is 0, then the internal @ref buffer_size() will be used as the
4469 default, which can also be changed from its compiled-in default of 1024 by
4470 using one of the buffer_size() methods.
4471
4472 @par Notes
4473 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4474 reception, but it's important to note that it does implement temporary
4475 blocking while waiting for data.
4476
4477 @throws randolf::rex::xEBADF The underlying socket is not open
4478 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4479 connections
4480 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4481 part of the user address space
4482 @throws randolf::rex::xEINTR Interrupted by a signal
4483 @throws randolf::rex::xEINVAL Invalid argument passed
4484 @throws randolf::rex::xENOMEM Insufficient memory
4485 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4486 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4487 doesn't refer to a socket
4488 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4489 there's no new data
4490
4491 @returns appropriately-sized vector of characters
4492 @see recv(std::vector<char>, const int)
4493 @see recvz(const size_t, const int)
4494 @qualifier TLS
4495 *///=========================================================================
4496 std::vector<char> recv(
4497 /// Maximum number of bytes to receive
4498 const size_t nbytes = 0,
4499 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4500 const int flags = 0) {
4501 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
4502 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4503 + ", <buf>"
4504 + ", " + std::to_string(buf_size)
4505 + ", " + std::to_string(flags)
4506 + ");");
4507 std::vector<char> buf(buf_size);
4508 buf.resize(__recv(buf.data(), buf.size(), flags));
4509 return buf;
4510 }; // -x- std::vector<char> recv -x-
4511
4512 /*======================================================================*//**
4513 @brief
4514 Receive data from the endpoint into the @c std::vector object supplied in the
4515 @c buf parameter.
4516
4517 The maximum number of bytes that can be received won't exceed the number of
4518 bytes that the supplied @c std::vector<char> was initialized or resized to.
4519
4520 @pre
4521 For @c std::vector it's important that the @c resize() method is used to
4522 pre-allocate the underlying char[] array, instead of the unfortunately-named
4523 @c reserve() method that doesn't pre-allocate, to avoid causing segmentation
4524 faults or other undefined behaviours.
4525
4526 @par Notes
4527 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4528 reception, but it's important to note that it does implement temporary
4529 blocking while waiting for data.
4530
4531 @throws randolf::rex::xEBADF The underlying socket is not open
4532 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4533 connections
4534 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4535 part of the user address space
4536 @throws randolf::rex::xEINTR Interrupted by a signal
4537 @throws randolf::rex::xEINVAL Invalid argument passed
4538 @throws randolf::rex::xENOMEM Insufficient memory
4539 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4540 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4541 doesn't refer to a socket
4542 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4543 there's no new data
4544
4545 @returns The same array that was specified in the @c buf parameter
4546 @see recv(const size_t, const int)
4547 @see recvz(const size_t, const int)
4548 @qualifier POSIX
4549 @qualifier TLS
4550 *///=========================================================================
4551 std::vector<char> recv(
4552 /// Target std::vector<char> to receive data into
4553 std::vector<char> buf,
4554 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4555 const int flags = 0) {
4556 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4557 + ", <buf>"
4558 + ", " + std::to_string(buf.size())
4559 + ", " + std::to_string(flags)
4560 + ");");
4561 buf.resize(__recv(buf.data(), buf.size(), flags));
4562 return buf;
4563 }; // -x- std::vector<char> recv -x-
4564
4565 /*======================================================================*//**
4566 @brief
4567 Receive an ASCIIZ string from the endpoint, including the NULL terminator.
4568
4569 @throws randolf::rex::xEBADF The underlying socket is not open
4570 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4571 connections
4572 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4573 part of the user address space
4574 @throws randolf::rex::xEINTR Interrupted by a signal
4575 @throws randolf::rex::xEINVAL Invalid argument passed
4576 @throws randolf::rex::xENOMEM Insufficient memory
4577 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4578 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4579 doesn't refer to a socket
4580 @throws randolf::rex::xERANGE if no NULL terminator is detected (this may
4581 occur before the underlying ASCIIZ string char* array is allocated)
4582 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4583 there's no new data
4584
4585 @returns Pointer to ASCIIZ string (will need to be deleted by caller)
4586 @see send_asciiz(const char*, const int)
4587 @qualifier TLS
4588 *///=========================================================================
4589 char* recv_asciiz(
4590 /// Maximum number of bytes to receive
4591 const size_t nbytes = 0,
4592 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4593 const int flags = 0) {
4594 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
4595 if (__debug) debug("recv_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4596 + ", " + std::to_string(buf_size)
4597 + ", " + std::to_string(flags)
4598 + ");");
4599
4600 // --------------------------------------------------------------------------
4601 // Calculate size of buffer (includes NULL terminator since we'll also be
4602 // receiving this from the endpoint).
4603 // --------------------------------------------------------------------------
4604 char buf[buf_size];
4605
4606 // --------------------------------------------------------------------------
4607 // Reduce buffer size to what is actually read (remember: we don't actually
4608 // know where the EoL sequence is yet, or if there even is one).
4609 // --------------------------------------------------------------------------
4610 int max = __recv(&buf, buf_size, MSG_PEEK | flags); // Look-ahead at socket stream (buffer was inflated to include EoL sequence)
4611 int len = -1;
4612 for (int i = 0; i < max; i++) {
4613 if (buf[i] == 0) {
4614 len = i;
4615 break;
4616 } // -x- if v[i] -x-
4617 } // -x- for i -x-
4618 if (len < 0) throw randolf::rex::xERANGE("ERANGE: NULL terminator not found");
4619
4620 // --------------------------------------------------------------------------
4621 // I'd love to use std::shared_ptr<char*> for this next part, but it leads
4622 // to intermittent segmentation faults and/or "malloc(): corrupted top size
4623 // occurs" errors outside of this method. So, my conclusion is that char*
4624 // (and char[], as I've tried with this too) are not properly supported by:
4625 // std::shared_ptr<char*> v = std::make_shared<char*>(new char[len + 1]);
4626 //
4627 // Using std::string() is really not what we want because there is confusion
4628 // concerning the NULL terminator -- with char* strings, it's crystal clear
4629 // that there must be a NULL terminator (since a length isn't tracked). Now,
4630 // if you want an std::string, just initialize as follows:
4631 //
4632 // char* temp_string = r.recv_asciiz();
4633 // std::string mystr = std::string(temp_string);
4634 // delete temp_string;
4635 // --------------------------------------------------------------------------
4636 char* v = new char[len + 1];
4637 int rc = __recv(v, len + 1, flags); // Consume with NULL terminator from socket stream
4638// TODO: Free "v" in a catch-and-rethrow block (check other calls to __recv and __send too)
4639 return v;
4640
4641 }; // -x- char* recv_asciiz -x-
4642
4643 /*======================================================================*//**
4644 @brief
4645 Receive one byte (unsigned 8-bit byte) of data from the endpoint.
4646 @par Notes
4647 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4648 reception, but it's important to note that it does implement temporary
4649 blocking while waiting for data.
4650
4651 @throws randolf::rex::xEBADF The underlying socket is not open
4652 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4653 connections
4654 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4655 part of the user address space
4656 @throws randolf::rex::xEINTR Interrupted by a signal
4657 @throws randolf::rex::xEINVAL Invalid argument passed
4658 @throws randolf::rex::xENOMEM Insufficient memory
4659 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4660 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4661 doesn't refer to a socket
4662 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4663 there's no new data
4664
4665 @returns one unsigned character
4666 @see recv(std::vector<char>, const int)
4667 @see recvz(const size_t, const int)
4668 @see recv_char
4669 @see send_byte
4670 @see send_char
4671 @qualifier TLS
4672 *///=========================================================================
4673 u_char recv_byte(
4674 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4675 const int flags = 0) {
4676 char buf(0);
4677 if (__debug) debug("recv_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4678 + ", <buf>"
4679 + ", " + std::to_string(sizeof(buf))
4680 + ", " + std::to_string(flags)
4681 + ");");
4682 int rc = __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4683 return buf;
4684 }; // -x- byte recv_byte -x-
4685
4686 /*======================================================================*//**
4687 @brief
4688 Receive one character (signed 8-bit byte) of data from the endpoint.
4689 @par Notes
4690 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4691 reception, but it's important to note that it does implement temporary
4692 blocking while waiting for data.
4693
4694 @throws randolf::rex::xEBADF The underlying socket is not open
4695 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4696 connections
4697 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4698 part of the user address space
4699 @throws randolf::rex::xEINTR Interrupted by a signal
4700 @throws randolf::rex::xEINVAL Invalid argument passed
4701 @throws randolf::rex::xENOMEM Insufficient memory
4702 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4703 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4704 doesn't refer to a socket
4705 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4706 there's no new data
4707
4708 @returns one signed character
4709 @see recv(std::vector<char>, const int)
4710 @see recvz(const size_t, const int)
4711 @see recv_byte
4712 @see send_byte
4713 @see send_char
4714 @qualifier TLS
4715 *///=========================================================================
4716 char recv_char(
4717 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4718 const int flags = 0) {
4719 char buf(0);
4720 if (__debug) debug("recv_char(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4721 + ", <buf>"
4722 + ", " + std::to_string(sizeof(buf))
4723 + ", " + std::to_string(flags)
4724 + ");");
4725 int rc = __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4726 // How to detect disconnected stream when telnet user presses CTRL-C?
4727 //std::cout << "rc=" << std::to_string(rc) << std::endl; // Debug (to be removed)
4728// TODO: Investigate checking platform's char size and moving signing bit accordingly
4729 return buf;
4730 }; // -x- char recv_char -x-
4731
4732 /*======================================================================*//**
4733 @brief
4734 Receive a data structure from the endpoint.
4735 @post
4736 MSB/LSB considerations are important for any integers within your structure,
4737 so be sure to sanitize them with htons(), ntohs(), and related methods prior
4738 to sending, and then after receiving. This way, your data will be protected
4739 against corruption resulting from byte order differences when communicating
4740 between hardware architectures that differ in MSB and LSB byte ordering.
4741 @par Notes
4742 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4743 reception, but it's important to note that it does implement temporary
4744 blocking while waiting for data.
4745
4746 @throws randolf::rex::xEBADF The underlying socket is not open
4747 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4748 connections
4749 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4750 part of the user address space
4751 @throws randolf::rex::xEINTR Interrupted by a signal
4752 @throws randolf::rex::xEINVAL Invalid argument passed
4753 @throws randolf::rex::xENOMEM Insufficient memory
4754 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4755 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4756 doesn't refer to a socket
4757 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4758 there's no new data
4759
4760 @returns Data structure
4761 @qualifier TLS
4762 *///=========================================================================
4763 template <typename T> T recv_struct(
4764 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4765 const int flags = 0) {
4766 T buf{0};
4767 if (__debug) debug("recv_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4768 + ", <buf>"
4769 + ", " + std::to_string(sizeof(buf))
4770 + ", " + std::to_string(flags)
4771 + ");");
4772 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4773 return buf;
4774 }; // -x- T recv_struct -x-
4775
4776 /*======================================================================*//**
4777 @brief
4778 Receive one 16-bit unsigned integer of data in LSB (little endian) order from
4779 the endpoint.
4780 @par Notes
4781 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
4782 reception, which implements temporary blocking while waiting for data.
4783
4784 @throws randolf::rex::xEBADF The underlying socket is not open
4785 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4786 connections
4787 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4788 part of the user address space
4789 @throws randolf::rex::xEINTR Interrupted by a signal
4790 @throws randolf::rex::xEINVAL Invalid argument passed
4791 @throws randolf::rex::xENOMEM Insufficient memory
4792 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4793 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4794 doesn't refer to a socket
4795 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4796 there's no new data
4797
4798 @returns uint16_t (converted to local endianness)
4799 @qualifier TLS
4800 *///=========================================================================
4801 uint16_t recv_uint16_lsb(
4802 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4803 const int flags = 0) {
4804 uint16_t buf(0);
4805 if (__debug) debug("recv_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4806 + ", <buf>"
4807 + ", " + std::to_string(sizeof(buf))
4808 + ", " + std::to_string(flags)
4809 + ");");
4810 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4811 return !__endian_is_msb ? buf : ntohs(buf);
4812 }; // -x- uint16_t recv_uint16_lsb -x-
4813
4814 /*======================================================================*//**
4815 @brief
4816 Receive one 16-bit unsigned integer of data in MSB (big endian) order from
4817 the endpoint.
4818 @par Notes
4819 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
4820 reception, which implements temporary blocking while waiting for data.
4821
4822 @throws randolf::rex::xEBADF The underlying socket is not open
4823 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4824 connections
4825 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4826 part of the user address space
4827 @throws randolf::rex::xEINTR Interrupted by a signal
4828 @throws randolf::rex::xEINVAL Invalid argument passed
4829 @throws randolf::rex::xENOMEM Insufficient memory
4830 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4831 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4832 doesn't refer to a socket
4833 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4834 there's no new data
4835
4836 @returns uint16_t (converted to local endianness)
4837 @qualifier TLS
4838 *///=========================================================================
4839 uint16_t recv_uint16_msb(
4840 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4841 const int flags = 0) {
4842 uint16_t buf(0);
4843 if (__debug) debug("recv_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4844 + ", <buf>"
4845 + ", " + std::to_string(sizeof(buf))
4846 + ", " + std::to_string(flags)
4847 + ");");
4848 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4849 return __endian_is_msb ? buf : htons(buf);
4850 }; // -x- uint16_t recv_uint16_msb -x-
4851
4852 /*======================================================================*//**
4853 @brief
4854 Receive one 32-bit unsigned integer of data in LSB (little endian) order from
4855 the endpoint.
4856 @par Notes
4857 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
4858 reception, which implements temporary blocking while waiting for data.
4859
4860 @throws randolf::rex::xEBADF The underlying socket is not open
4861 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4862 connections
4863 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4864 part of the user address space
4865 @throws randolf::rex::xEINTR Interrupted by a signal
4866 @throws randolf::rex::xEINVAL Invalid argument passed
4867 @throws randolf::rex::xENOMEM Insufficient memory
4868 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4869 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4870 doesn't refer to a socket
4871 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4872 there's no new data
4873
4874 @returns uint32_t (converted to local endianness)
4875 @qualifier TLS
4876 *///=========================================================================
4877 uint32_t recv_uint32_lsb(
4878 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4879 const int flags = 0) {
4880 uint32_t buf(0);
4881 if (__debug) debug("recv_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4882 + ", <buf>"
4883 + ", " + std::to_string(sizeof(buf))
4884 + ", " + std::to_string(flags)
4885 + ");");
4886 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4887 return !__endian_is_msb ? buf : htonl(buf);
4888 }; // -x- uint32_t recv_uint32_lsb -x-
4889
4890 /*======================================================================*//**
4891 @brief
4892 Receive one 32-bit unsigned integer of data in MSB (big endian) order from
4893 the endpoint.
4894 @par Notes
4895 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
4896 reception, which implements temporary blocking while waiting for data.
4897
4898 @throws randolf::rex::xEBADF The underlying socket is not open
4899 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4900 connections
4901 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4902 part of the user address space
4903 @throws randolf::rex::xEINTR Interrupted by a signal
4904 @throws randolf::rex::xEINVAL Invalid argument passed
4905 @throws randolf::rex::xENOMEM Insufficient memory
4906 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4907 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4908 doesn't refer to a socket
4909 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4910 there's no new data
4911
4912 @returns uint32_t (converted to local endianness)
4913 @qualifier TLS
4914 *///=========================================================================
4915 uint32_t recv_uint32_msb(
4916 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4917 const int flags = 0) {
4918 uint32_t buf(0);
4919 if (__debug) debug("recv_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4920 + ", <buf>"
4921 + ", " + std::to_string(sizeof(buf))
4922 + ", " + std::to_string(flags)
4923 + ");");
4924 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4925 return __endian_is_msb ? buf : ntohl(buf);
4926 }; // -x- uint32_t recv_uint32_msb -x-
4927
4928 /*======================================================================*//**
4929 @brief
4930 Receive one 64-bit unsigned integer of data in LSB (little endian) order from
4931 the endpoint.
4932 @par Notes
4933 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
4934 reception, which implements temporary blocking while waiting for data.
4935
4936 @throws randolf::rex::xEBADF The underlying socket is not open
4937 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4938 connections
4939 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4940 part of the user address space
4941 @throws randolf::rex::xEINTR Interrupted by a signal
4942 @throws randolf::rex::xEINVAL Invalid argument passed
4943 @throws randolf::rex::xENOMEM Insufficient memory
4944 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4945 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4946 doesn't refer to a socket
4947 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4948 there's no new data
4949
4950 @returns uint64_t (converted to local endianness)
4951 @qualifier TLS
4952 *///=========================================================================
4953 uint64_t recv_uint64_lsb(
4954 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4955 const int flags = 0) {
4956 uint64_t buf(0);
4957 if (__debug) debug("recv_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4958 + ", <buf>"
4959 + ", " + std::to_string(sizeof(buf))
4960 + ", " + std::to_string(flags)
4961 + ");");
4962 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
4963 return !__endian_is_msb ? buf : ((((uint64_t)ntohl((buf) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((buf) >> 32)));
4964 }; // -x- uint64_t recv_uint64_lsb -x-
4965
4966 /*======================================================================*//**
4967 @brief
4968 Receive one 64-bit unsigned integer of data in MSB (big endian) order from
4969 the endpoint.
4970 @par Notes
4971 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
4972 reception, which implements temporary blocking while waiting for data.
4973
4974 @throws randolf::rex::xEBADF The underlying socket is not open
4975 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4976 connections
4977 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4978 part of the user address space
4979 @throws randolf::rex::xEINTR Interrupted by a signal
4980 @throws randolf::rex::xEINVAL Invalid argument passed
4981 @throws randolf::rex::xENOMEM Insufficient memory
4982 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4983 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4984 doesn't refer to a socket
4985 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4986 there's no new data
4987
4988 @returns uint64_t (converted to local endianness)
4989 @qualifier TLS
4990 *///=========================================================================
4991 uint64_t recv_uint64_msb(
4992 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
4993 const int flags = 0) {
4994 uint64_t buf(0);
4995 if (__debug) debug("recv_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4996 + ", <buf>"
4997 + ", " + std::to_string(sizeof(buf))
4998 + ", " + std::to_string(flags)
4999 + ");");
5000 __recv(&buf, sizeof(buf), MSG_WAITALL | flags);
5001 return __endian_is_msb ? buf : ((((uint64_t)htonl((buf) & 0xffffffffUL)) << 32) | htonl((uint32_t)((buf) >> 32)));
5002 }; // -x- uint64_t recv_uint64_msb -x-
5003
5004 /*======================================================================*//**
5005 @brief
5006 Receive data from a specific endpoint.
5007
5008 @par Notes
5009 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5010 reception, but it's important to note that it does implement temporary
5011 blocking while waiting for data.
5012
5013 @warning
5014 This method is not compatible with TLS.
5015
5016 @throws randolf::rex::xEBADF The underlying socket is not open
5017 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5018 connections
5019 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5020 part of the user address space
5021 @throws randolf::rex::xEINTR Interrupted by a signal
5022 @throws randolf::rex::xEINVAL Invalid argument passed
5023 @throws randolf::rex::xENOMEM Insufficient memory
5024 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5025 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5026 doesn't refer to a socket
5027 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5028 there's no new data
5029
5030 @returns Appropriately-sized vector of characters
5031 @qualifier TLS
5032 *///=========================================================================
5033 std::vector<char> recvfrom(
5034 /// Maximum number of bytes to receive
5035 const size_t nbytes,
5036 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5037 const int flags,
5038 /// Target endpoint address structure
5039 struct sockaddr *from,
5040 /// Size of target endpoint structure
5041 socklen_t fromlen = sizeof(sockaddr)) {
5042 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5043 if (__debug) debug("recvfrom(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5044 + ", <buf>"
5045 + ", " + std::to_string(buf_size)
5046 + ", " + std::to_string(flags)
5047 + ", <sockaddr>"
5048 + ", " + std::to_string(fromlen)
5049 + ");");
5050 std::vector<char> v(buf_size);
5051 v.resize(__track_bytes_rx(__rc_check(::recvfrom(__socket_fd, v.data(), v.size(), flags, from, &fromlen))));
5052 return v;
5053 }; // -x- std::vector<char> recvfrom -x-
5054
5055 private:
5056 /*======================================================================*//**
5057 @brief
5058 This is an internal function that:
5059 1. receives data from the endpoint:
5060 - via underlying socket when TLS is not enabled
5061 - via the OpenSSL socket API when TLS is enabled
5062 2. checks for a socket I/O error (and throws an exception)
5063 3. tracks number of bytes received (if there were no errors)
5064
5065 @throws randolf::rex::xEAGAIN The underlying socket timed out
5066 @throws randolf::rex::xEBADF The underlying socket is not open
5067 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5068 connections
5069 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5070 part of the user address space
5071 @throws randolf::rex::xEINTR Interrupted by a signal
5072 @throws randolf::rex::xEINVAL Invalid argument passed
5073 @throws randolf::rex::xENOMEM Insufficient memory
5074 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5075 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5076 doesn't refer to a socket
5077 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5078 there's no new data
5079
5080 @returns Number of bytes that were successfully received
5081 @qualifier TLS
5082 *///=========================================================================
5083 int __recv(
5084 /// Pointer to data
5085 void* data,
5086 /// Length of message data
5087 const size_t len,
5088 /// Flags (ignored with encrypted streams, except for the MSG_PEEK flag)
5089 const int flags = 0) {
5090
5091// TODO: For MSG_PEEK with OpenSSL, try this suggestion: You can get the underlying file descriptor with BIO_get_fd and then call recv() with the MSG_PEEK flag
5092
5093 // --------------------------------------------------------------------------
5094 // When internal buffering is not set up (which is the default operation), we
5095 // simply passing data directly.
5096 // --------------------------------------------------------------------------
5097 if (__buffer == nullptr) {
5098 return __tls ? (flags & MSG_PEEK ? __rc_check_tls(SSL_peek(__tls_fd, data, len))
5099 : __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, data, len))))
5100 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, data, len, flags)));
5101 } // -x- if !__buffer -x-
5102
5103 // --------------------------------------------------------------------------
5104 // Consume data from socket to make up for OpenSSL's inability to read beyond
5105 // one packet of data in its SSL_peek() and SSL_read() methods, and also some
5106 // inconsistencies with certain raw (unencrypted) socket implementations that
5107 // occasionally refuse to read beyond one packet of data (at least on a first
5108 // attempt to read with or without the MSG_PEEK flag).
5109 // --------------------------------------------------------------------------
5110
5111/*
5112std::cout << "-(0)- __buffer=" << (long)__buffer << std::endl
5113 << " __buffer_head=" << (long)__buffer_head << std::endl
5114 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5115 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5116 << " __buffer_size=" << __buffer_size << std::endl
5117 << " len=" << len << std::endl;
5118*/
5119 // --------------------------------------------------------------------------
5120 // Fill internal buffer. It may be more than len is set to, but this is okay
5121 // because we're committed to using internal buffering now anyway (it's far
5122 // more likely that a protocol that requires the use of recvline() once is
5123 // going to be using it repeatedly).
5124 // --------------------------------------------------------------------------
5125 int n;
5126
5127 // --------------------------------------------------------------------------
5128 // If the amount of data contained in the internal buffer is sufficient to
5129 // satisfy the amount of data that "len" represents, then skip the attempts
5130 // to read from the socket and just drain from the buffer what's needed.
5131 // --------------------------------------------------------------------------
5132 if ((__buffer_head >= __buffer_tail // Does the buffer wrap around?
5133 ? __buffer_head - __buffer_tail // No, so calcluation is straight-forward
5134 : (__buffer_boundary - __buffer_tail) // Yes, so we need the sum of both parts
5135 + (__buffer_head - __buffer)) >= len) goto __recv_drain;
5136
5137 __recv_loop:
5138 // --------------------------------------------------------------------------
5139 // Tail is behind head, so do one extra read beforehand to fill it up, which
5140 // affects the first part of a wrap-around since this is a circular buffer.
5141 // --------------------------------------------------------------------------
5142 if (__buffer_head >= __buffer_tail) { // std::cout << "__buffer_head >= __buffer_tail" << std::endl;
5143 n = __tls ? __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, __buffer_head, std::min(len, (size_t)(__buffer_boundary - __buffer_head))))) // Not using SSL_peek() at all here because we need to consume this data as we stuff it into the internal buffer
5144 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, __buffer_head, std::min(len, (size_t)(__buffer_boundary - __buffer_head)), flags & (~MSG_PEEK)))); // Mask the MSG_PEEK flag because we need to consume this data as we stuff it into the internal buffer
5145//std::cout << "n(0)=" << n << std::endl;
5146 if ((__buffer_head += n) > __buffer_boundary) __buffer_head = __buffer; // Advance head (and wrap around if we're at the boundary)
5147// TODO: Test wrap-around, make sure we don't need >= instead of >
5148 } // -x- if __buffer_head -x-
5149
5150/*
5151std::cout << "-(1)- __buffer=" << (long)__buffer << std::endl
5152 << " __buffer_head=" << (long)__buffer_head << std::endl
5153 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5154 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5155 << " __buffer_size=" << __buffer_size << std::endl
5156 << " len=" << len << std::endl;
5157*/
5158//std::cout << "Buffer: [" << __buffer_tail[0] << "]" << std::endl;
5159
5160 // --------------------------------------------------------------------------
5161 // Fill in the remaining (unused) portion of the internal buffer.
5162 // --------------------------------------------------------------------------
5163 if (__buffer_head < __buffer_tail && ioctl(__socket_fd, FIONREAD, &n) == 0) {
5164 n = __tls ? __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, __buffer_head, __buffer_head - __buffer_tail))) // Not using SSL_peek() at all here because we need to consume this data as we stuff it into the internal buffer
5165 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, __buffer_head, __buffer_head - __buffer_tail, flags & (~MSG_PEEK)))); // Mask the MSG_PEEK flag because we need to consume this data as we stuff it into the internal buffer
5166//std::cout << "n(1)=" << n << std::endl;
5167 __buffer_head += n; // Advance the head
5168 } // -x- if __buffer_head -x-
5169
5170 // --------------------------------------------------------------------------
5171 // Check if more bytes are available for reading, then run the loop again so
5172 // that we're pulling in as many bytes as possible. This makes up for a
5173 // problem with socket I/O functions not always returning all the data that's
5174 // ready to be consumed.
5175 // --------------------------------------------------------------------------
5176// if (__buffer_head != __buffer_tail && ioctl(__socket_fd, FIONREAD, &n) == 0 && n > 0) goto __recv_loop;
5177//std::cout << "Buffer size = " << __buffer_size << std::endl; // Debug
5178 // --------------------------------------------------------------------------
5179 // Drain the internal buffer. If we can drain it completely, we'll either
5180 // reset head and tail (default), or free() the internal buffer (based on the
5181 // internal policy). TODO: Create policy control options
5182 //
5183 // MSG_PEEK mode is limited to the internal buffer size. Not using this flag
5184 // is not limited, ... TODO: Figure out how to deal with this
5185 // --------------------------------------------------------------------------
5186 __recv_drain:
5187 char* drain = __buffer_tail;
5188//std::cout << "Drain: [";
5189 for (n = 0; n < len; n++) {
5190//if (*drain < 32) std::cout << "^" << (char)(drain[0] + 64); else std::cout << drain[0];
5191 ((char*)data)[n] = *drain++; // Copy byte, and advance the drain
5192//std::cout << "{" << (long)drain << "}";
5193 if (drain > __buffer_boundary) drain = __buffer; // Wrap around // TODO: Test this
5194 if (drain == __buffer_head) break; // We've reached the limit; there's no more data // TODO: Move this to earlier to avoid buffer overrun
5195 } // -x- for n -x-
5196 n++;
5197//std::cout << "] n=" << ++n << std::endl;
5198
5199 // --------------------------------------------------------------------------
5200 // Consume data in __buffer if this isn't a MSG_PEEK operation.
5201 // --------------------------------------------------------------------------
5202/*
5203std::cout << "-(2)- __buffer=" << (long)__buffer << std::endl
5204 << " __buffer_head=" << (long)__buffer_head << std::endl
5205 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5206 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5207 << " MSG_PEEK=" << (flags && MSG_PEEK ? "true" : "false") << std::endl
5208 << " len=" << len << std::endl
5209 << " drain=" << (long)drain << std::endl;
5210*/
5211// if ((flags & MSG_PEEK) == 0) std::cout << "[Consuming buffer]" << std::endl;
5212 if ((flags & MSG_PEEK) == 0) __buffer_tail = drain; // Consume character(s) unless MSG_PEEK flag is set
5213/*
5214std::cout << "-(3)- __buffer=" << (long)__buffer << std::endl
5215 << " __buffer_head=" << (long)__buffer_head << std::endl
5216 << " __buffer_tail=" << (long)__buffer_tail << std::endl
5217 << "__buffer_boundary=" << (long)__buffer_boundary << std::endl
5218 << " MSG_PEEK=" << (flags && MSG_PEEK ? "true" : "false") << std::endl
5219 << " len=" << len << std::endl
5220 << " drain=" << (long)drain << std::endl;
5221*/
5222 return n;
5223
5224 }; // -x- int __recv -x-
5225
5226 public:
5227 /*======================================================================*//**
5228 @brief
5229 Receive a line of data from the endpoint, with the EoL character(s) removed.
5230 This is meant for binary data that isn't expected to always be plain text.
5231
5232 If nbytes is 0, then the internal buffer_size will be used as the default,
5233 which can also be changed from its compiled-in default of 1024 by using one
5234 of the buffer_size() methods.
5235
5236 Internally, the length of the @ref eol() sequence is added to the buffer size
5237 so that the maximum line length without EoL characters matches the maximum
5238 that was specified with @ref buffer_size() or with the @c nbytes parameter.
5239
5240 The @c timeout parameter can be used to override the total overall timeout
5241 for receiving a line, which is useful for specific situations where a
5242 specific timeout desired for particular prompts, during certain data entry
5243 stages, etc.
5244
5245 @par Notes
5246 If you're searching for a readline() method for socket I/O, then this is most
5247 likely what you're wanting/needing.
5248
5249 @pre
5250 When setting the recvline timeout (either with the @c timeout parameter with
5251 this method, or by using the @ref timeout_recvline(long) method), you may
5252 also need to set the socket timeout beforehand using the @ref timeout()
5253 method, because @c recvline doesn't do this...
5254 @n
5255 <blockquote>
5256 @ref timeout_recvline sets the overall total timeout for an entire line to
5257 be entered
5258 @n
5259 @ref timeout sets the timeout between individual characters received (such
5260 as keystrokes from a live end-user)
5261 </blockquote>
5262 @n
5263 If your socket timeout is longer than then recvline timeout, this will have
5264 the effect of rendering the recvling timeout as ineffective.
5265
5266 @throws randolf::rex::xEAGAIN if timeout occurs before EoL
5267 @throws randolf::rex::xEBADF The underlying socket is not open
5268 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5269 connections
5270 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5271 part of the user address space
5272 @throws randolf::rex::xEINTR Interrupted by a signal
5273 @throws randolf::rex::xEINVAL Invalid argument passed
5274 @throws randolf::rex::xENOMEM Insufficient memory
5275 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5276 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5277 doesn't refer to a socket
5278 @throws randolf::rex::xERANGE if the timeout parameter is below 0
5279 @throws randolf::rex::xEOVERFLOW if no EoL character sequence is detected
5280 after recvline's buffer became full (whatever data is waiting may
5281 still be received using any of the recv_ methods with or without the
5282 MSG_PEEK flag set, including recvline() with a larger buffer size
5283 specified with the @c nbytes parameter)
5284 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5285 there's no new data
5286
5287 @returns One line of text as a std::string object.
5288 @see eol()
5289 @see sendline(const std::string, const int)
5290 @see timeout
5291 @see timeout_recvline
5292 @see timeout_recvline(long)
5293 @qualifier TLS
5294 *///=========================================================================
5295 std::string recvline(
5296 /// Maximum number of bytes to receive
5297 const size_t nbytes = 0,
5298 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5299 const int flags = 0,
5300 /// Line timeout (in seconds)@n
5301 /// 0 = no timeout (default), unless it was configured by way of the
5302 /// @ref timeout_recvline(long) method
5303 long timeout = 0) {
5304 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size) // If 0, then set this to the internal __buffer_size default
5305 + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF
5306 if (__debug) debug("recvline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5307 + ", " + std::to_string(buf_size)
5308 + ", " + std::to_string(flags)
5309 + ");");
5310
5311 // --------------------------------------------------------------------------
5312 // Syntax checks as part of preparation of timeout variable for faster
5313 // comparisons within line-reading loop.
5314 // --------------------------------------------------------------------------
5315 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
5316 if (timeout == 0) timeout = __recvline_timeout;
5317 timeout = timeout == 0 ? LONG_MAX : timeout + time(0);
5318
5319 // --------------------------------------------------------------------------
5320 // Allocate line buffer.
5321 // --------------------------------------------------------------------------
5322 std::string v; v.resize(buf_size); // Add EoL size to buffer size; we'll probably be shrinking later anyway
5323
5324 // --------------------------------------------------------------------------
5325 // Create internal buffer for __recv (if it hasn't already been created).
5326 //
5327 // Buffering becomes necessary with TLS connection because SSL_peek fails to
5328 // return data that spans multiple packets. What triggers this behaviour is
5329 // the command "stty -icanon && openssl s_client host_name tcp_port" followed
5330 // by manual interactive typing some text then pressing the "[Enter]" key.
5331 // --------------------------------------------------------------------------
5332 if (__buffer == nullptr) {
5333 size_t bsize = std::max(__buffer_size, buf_size);
5334 __buffer = (char*)(::malloc(bsize)); //new char[bsize];
5335 __buffer_head = __buffer;
5336 __buffer_tail = __buffer;
5337 __buffer_boundary = __buffer + bsize;
5338 } // -x- if !__buffer -x-
5339
5340 // --------------------------------------------------------------------------
5341 // Line-reading loop.
5342 // --------------------------------------------------------------------------
5343 int len_with_eol = 0;
5344 recvline_check:
5345 int br = 0; // Number of Bytes Received
5346 try {
5347 br = __recv(v.data(), v.size(), MSG_PEEK | flags); // Look-ahead at socket stream (with buffer size that was inflated earlier to include EoL sequence)
5348 } catch (const randolf::rex::xEAGAIN e) { // Socket timeout occurred, but recvline timeout may not have yet so we need to catch it here to implement the recvline timeout
5349 //goto recvline_check;
5350std::cout << "EAGAIN caught" << std::endl;
5351 __rc_check(nanosleep(&__recvline_idle, nullptr)); // Prevent tight CPU utilization loop; EINVAL = bad parameters (e.g., tv_nsec > 999999999 is not uncommon)
5352 }
5353 if (time(0) > timeout) throw randolf::rex::xEAGAIN("recvline timed out");
5354 int len = eol_index(v, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
5355 if (len < 0) {
5356 if (br >= buf_size) throw randolf::rex::xEOVERFLOW("recvline buffer became full before EoL");
5357 goto recvline_check;
5358 } // -x- if !len -x-
5359
5360 // --------------------------------------------------------------------------
5361 // Final clean-up.
5362 // --------------------------------------------------------------------------
5363 if ((flags & MSG_PEEK) == 0) __recv(v.data(), len_with_eol, flags); // Drain if not in MSG_PEEK mode / __recv will throw exception if something goes wrong so there's no need for an additional __rc_check call from here
5364 v.resize(len); // Truncate string to exclude unused portion
5365 return v;
5366
5367 }; // -x- std::string recvline -x-
5368
5369 /*======================================================================*//**
5370 @brief
5371 Receive data in the form of a "msghdr" structure.
5372 @warning
5373 This method is not compatible with TLS.
5374
5375 @throws randolf::rex::xEBADF The underlying socket is not open
5376 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5377 connections
5378 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5379 part of the user address space
5380 @throws randolf::rex::xEINTR Interrupted by a signal
5381 @throws randolf::rex::xEINVAL Invalid argument passed
5382 @throws randolf::rex::xENOMEM Insufficient memory
5383 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5384 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5385 doesn't refer to a socket
5386 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5387 there's no new data
5388
5389 @returns pointer to "msghdr" structure
5390 @qualifier POSIX
5391 *///=========================================================================
5392 msghdr* recvmsg(
5393 /// Pointer to "msghdr" structure (or nullptr for automatic allocation)
5394 msghdr* msg,
5395 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5396 const int flags = 0) {
5397 if (__debug) debug("recvmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5398 + ", <msghdr>"
5399 + ", " + std::to_string(flags)
5400 + ");");
5401 if (msg == nullptr) msg = new msghdr{0};
5402 __track_bytes_rx(__rc_check(::recvmsg(__socket_fd, msg, flags)));
5403 return msg;
5404 }; // -x- msghdr* recvmsg -x-
5405
5406 /*======================================================================*//**
5407 @brief
5408 Receive data in the form of an "mmsghdr" structure.
5409 @warning
5410 This method is not compatible with TLS.
5411
5412 @throws randolf::rex::xEBADF The underlying socket is not open
5413 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5414 connections
5415 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5416 part of the user address space
5417 @throws randolf::rex::xEINTR Interrupted by a signal
5418 @throws randolf::rex::xEINVAL Invalid argument passed
5419 @throws randolf::rex::xENOMEM Insufficient memory
5420 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5421 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5422 doesn't refer to a socket
5423 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5424 there's no new data
5425
5426 @returns pointer to "mmsghdr" structure
5427 @qualifier POSIX
5428 *///=========================================================================
5429 mmsghdr* recvmmsg(
5430 /// Pointer to "mmsghdr" structure (or nullptr for automatic allocation)
5431 struct mmsghdr* mmsg,
5432 /// Size of target endpoint structure
5433 const unsigned int vlen = sizeof(mmsghdr),
5434 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
5435 const int flags = 0,
5436 /// Timeout
5437 struct timespec* timeout = {0}) {
5438 if (__debug) debug("recvmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5439 + ", <mmsghdr>"
5440 + ", " + std::to_string(vlen)
5441 + ", " + std::to_string(flags)
5442 + ", timeout{tv_sec=" + std::to_string(timeout->tv_sec)
5443 + ", tv_nsec=" + std::to_string(timeout->tv_nsec) + "}"
5444 + ");");
5445 __track_bytes_rx(__rc_check(::recvmmsg(__socket_fd, mmsg, vlen, flags, timeout)));
5446 return mmsg;
5447 }; // -x- mmsghdr* recvmmsg -x-
5448
5449 /*======================================================================*//**
5450 @brief
5451 Receive data from the endpoint, and add a 0 (null) onto the end. This is
5452 useful when using the resulting std::vector<char> as an ASCIIZ string.
5453
5454 If nbytes is 0, then the internal buffer_size will be used as the default,
5455 which can also be changed from its compiled-in default of 1024 by using one
5456 of the buffer_size() methods.
5457
5458 @post
5459 The resulting std::vector size is always inflated by 1. This means that
5460 relying on a comparison against 0 will result in an infinite loop:
5461 @code{.cpp}
5462 for (std::vector<char> v = r.recvz(); v.size > 0; v = recvz()) { ... }
5463 @endcode
5464 So, you'll need to compare against 1 instead of 0 to compensate fot the
5465 inflated size due to the addition of null character 0:
5466 @code{.cpp}
5467 for (std::vector<char> v = r.recvz(); v.size > 1; v = recvz()) { ... }
5468 @endcode
5469
5470 @warning
5471 The resulting size of std::vector<char> will be <tt>nbytes + 1</tt> which
5472 ensures that any string processing functions or presentation libraries - such
5473 as @c printf() - expecting an ASCIIZ string buffer will stop at null (0), and
5474 will reliably output no more than the maximum size specified.
5475 @n@n
5476 The way to think of this is that @c nbytes specifies the maximum number of
5477 bytes (a.k.a., characters) to recieve over the underlying socket, and the
5478 final 0 (null) being added is not included in the maximum specified by the
5479 @c nbytes parameter.
5480
5481 @par Notes
5482 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5483 reception, but it's important to note that it does implement temporary
5484 blocking while waiting for data.
5485
5486 @throws randolf::rex::xEBADF The underlying socket is not open
5487 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5488 connections
5489 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5490 part of the user address space
5491 @throws randolf::rex::xEINTR Interrupted by a signal
5492 @throws randolf::rex::xEINVAL Invalid argument passed
5493 @throws randolf::rex::xENOMEM Insufficient memory
5494 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5495 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5496 doesn't refer to a socket
5497 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5498 there's no new data
5499
5500 @returns appropriately-sized vector of characters + 1 (includes additional
5501 null terminator character 0)
5502 @see recv(const size_t, const int)
5503 @see recv(std::vector<char>, const int)
5504 @qualifier TLS
5505 *///=========================================================================
5506 std::vector<char> recvz(
5507 /// Maximum number of bytes to receive
5508 const size_t nbytes = 0,
5509 /// MSG_OOB@n MSG_PEEK@n MSG_WAITALL@n MSG_DONTWAIT@n MSG_CMSG_CLOEXEC
5510 const int flags = 0) {
5511 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5512 if (__debug) debug("recvz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5513 + ", <buf>"
5514 + ", " + std::to_string(buf_size)
5515 + ", " + std::to_string(flags)
5516 + ");");
5517 std::vector<char> v(buf_size);
5518 v.resize(__recv(v.data(), v.size(), flags) + 1); // Add 1 to include room for final null character 0
5519 v[v.size() - 1] = (char)0; // Set final element to 0 (null)
5520 return v;
5521 }; // -x- std::vector<char> recvz -x-
5522
5523 private:
5524 /*======================================================================*//**
5525 @brief
5526 This is an internal function that:
5527 1. sends data to the endpoint:
5528 - via underlying socket when TLS is not enabled
5529 - via the OpenSSL socket API when TLS is enabled
5530 2. checks for a socket I/O error (and throws an exception)
5531 3. tracks number of bytes transmitted (if there were no errors)
5532
5533 @par Threads
5534 This method is threadsafe because the underlying POSIX send() and OpenSSL's
5535 SSL_write() functions are threadsafe.
5536
5537 @throws randolf::rex::xEBADF The underlying socket is not open
5538 @throws randolf::rex::xECONNRESET Connect reset by peer
5539 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5540 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5541 part of the user address space
5542 @throws randolf::rex::xEINTR Interrupted by a signal
5543 @throws randolf::rex::xEINVAL Invalid argument passed
5544 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5545 occur, but the POSIX sockets documentation lists it as one of the
5546 errors that can be returned, perhaps because some incorrectly
5547 implemented TCP/IP stacks return this error?)
5548 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5549 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5550 and 65,527 bytes for IPv6)
5551 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5552 network congestion (or, less commonly, insufficient memory)
5553 @throws randolf::rex::xENOMEM Insufficient memory
5554 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5555 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5556 doesn't refer to a socket
5557 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5558 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5559 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5560 isn't set)
5561 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5562 there's no new data
5563 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5564 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5565 but it really isn't)
5566
5567 @returns Number of bytes that were successfully transmitted
5568 @qualifier TLS
5569 *///=========================================================================
5570 int __send(
5571 /// Pointer to data
5572 const void* data,
5573 /// Length of message data
5574 const size_t len,
5575 /// Flags (ignored with encrypted streams)
5576 const int flags = 0) {
5577 if (__tls) return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data, len)));
5578 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data, len, flags)));
5579 }; // -x- int __send -x-
5580
5581 /*======================================================================*//**
5582 @brief
5583 This is an internal function that:
5584 1. sends data to the endpoint:
5585 - via underlying socket when TLS is not enabled
5586 - via the OpenSSL socket API when TLS is enabled
5587 2. checks for a socket I/O error (and throws an exception)
5588 3. tracks number of bytes transmitted (if there were no errors)
5589
5590 @par Threads
5591 This method is threadsafe because the underlying POSIX send() and OpenSSL's
5592 SSL_write() functions are threadsafe.
5593
5594 @throws randolf::rex::xEBADF The underlying socket is not open
5595 @throws randolf::rex::xECONNRESET Connect reset by peer
5596 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5597 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5598 part of the user address space
5599 @throws randolf::rex::xEINTR Interrupted by a signal
5600 @throws randolf::rex::xEINVAL Invalid argument passed
5601 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5602 occur, but the POSIX sockets documentation lists it as one of the
5603 errors that can be returned, perhaps because some incorrectly
5604 implemented TCP/IP stacks return this error?)
5605 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5606 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5607 and 65,527 bytes for IPv6)
5608 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5609 network congestion (or, less commonly, insufficient memory)
5610 @throws randolf::rex::xENOMEM Insufficient memory
5611 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5612 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5613 doesn't refer to a socket
5614 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5615 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5616 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5617 isn't set)
5618 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5619 there's no new data
5620 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5621 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5622 but it really isn't)
5623
5624 @returns Number of bytes that were successfully transmitted
5625 @qualifier TLS
5626 *///=========================================================================
5627 int __sendline(
5628 /// Pointer to data
5629 const void* data,
5630 /// Number of bytes to send
5631 const size_t len,
5632 /// Flags (ignored with encrypted streams)
5633 const int flags = 0) {
5634 if (__tls) { // Encrypted
5635 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data , len )))
5636 + __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.c_str(), __eol_out.length())));
5637 } else { // Not encrypted
5638 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data , len , MSG_MORE | flags))) // MSG_MORE prevents extra packets from being sent
5639 + __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.c_str(), __eol_out.length(), flags)));
5640 } // -x- if __tls -x-
5641 }; // -x- int __sendline -x-
5642
5643 /*======================================================================*//**
5644 @brief
5645 This is an internal function that:
5646 1. sends data to the endpoint:
5647 - via underlying socket when TLS is not enabled
5648 - via the OpenSSL socket API when TLS is enabled
5649 2. checks for a socket I/O error (and throws an exception)
5650 3. tracks number of bytes transmitted (if there were no errors)
5651
5652 @par Threads
5653 This method is threadsafe because the underlying POSIX send() and OpenSSL's
5654 SSL_write() functions are threadsafe.
5655
5656 @throws randolf::rex::xEBADF The underlying socket is not open
5657 @throws randolf::rex::xECONNRESET Connect reset by peer
5658 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5659 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5660 part of the user address space
5661 @throws randolf::rex::xEINTR Interrupted by a signal
5662 @throws randolf::rex::xEINVAL Invalid argument passed
5663 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5664 occur, but the POSIX sockets documentation lists it as one of the
5665 errors that can be returned, perhaps because some incorrectly
5666 implemented TCP/IP stacks return this error?)
5667 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5668 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5669 and 65,527 bytes for IPv6)
5670 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5671 network congestion (or, less commonly, insufficient memory)
5672 @throws randolf::rex::xENOMEM Insufficient memory
5673 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5674 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5675 doesn't refer to a socket
5676 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5677 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5678 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5679 isn't set)
5680 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5681 there's no new data
5682 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5683 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5684 but it really isn't)
5685
5686 @returns Number of bytes that were successfully transmitted
5687 @qualifier TLS
5688 *///=========================================================================
5689 int __send_eol(
5690 /// Flags (ignored with encrypted streams)
5691 const int flags = 0) {
5692 if (__tls) { // Encrypted
5693 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.c_str(), __eol_out.length())));
5694 } else { // Not encrypted
5695 return __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.c_str(), __eol_out.length(), flags)));
5696 } // -x- if __tls -x-
5697 }; // -x- int __send_eol -x-
5698 // TODO: Create a recv_eol() method that some people will probably consider to be evil (ha ha!)
5699 // recv_eol() will need to be able to auto-detect the CRLF sequence on-the-fly (if necessary) just like recvline() does
5700
5701 public:
5702 /*======================================================================*//**
5703 @brief
5704 Send data in the form of a std::string to the endpoint.
5705 @par Threads
5706 This method is threadsafe.
5707
5708 @throws randolf::rex::xEBADF The underlying socket is not open
5709 @throws randolf::rex::xECONNRESET Connect reset by peer
5710 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5711 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5712 part of the user address space
5713 @throws randolf::rex::xEINTR Interrupted by a signal
5714 @throws randolf::rex::xEINVAL Invalid argument passed
5715 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5716 occur, but the POSIX sockets documentation lists it as one of the
5717 errors that can be returned, perhaps because some incorrectly
5718 implemented TCP/IP stacks return this error?)
5719 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5720 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5721 and 65,527 bytes for IPv6)
5722 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5723 network congestion (or, less commonly, insufficient memory)
5724 @throws randolf::rex::xENOMEM Insufficient memory
5725 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5726 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5727 doesn't refer to a socket
5728 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5729 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5730 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5731 isn't set)
5732 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5733 there's no new data
5734 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5735 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5736 but it really isn't)
5737
5738 @returns The same rsocket object so as to facilitate stacking
5739 @qualifier TLS
5740 *///=========================================================================
5741 rsocket* send(
5742 /// Data to send
5743 const std::string msg,
5744 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
5745 const int flags = 0) {
5746 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5747 + ", <std::string>"
5748 + ", " + std::to_string(msg.length())
5749 + ", " + std::to_string(flags)
5750 + ");");
5751 __send(msg.c_str(), msg.length(), flags);
5752 return this;
5753 }; // -x- rsocket* send -x-
5754
5755 /*======================================================================*//**
5756 @brief
5757 Send data in the form of a std::vector<char> to the endpoint.
5758 @par Threads
5759 This method is threadsafe.
5760
5761 @throws randolf::rex::xEBADF The underlying socket is not open
5762 @throws randolf::rex::xECONNRESET Connect reset by peer
5763 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5764 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5765 part of the user address space
5766 @throws randolf::rex::xEINTR Interrupted by a signal
5767 @throws randolf::rex::xEINVAL Invalid argument passed
5768 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5769 occur, but the POSIX sockets documentation lists it as one of the
5770 errors that can be returned, perhaps because some incorrectly
5771 implemented TCP/IP stacks return this error?)
5772 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5773 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5774 and 65,527 bytes for IPv6)
5775 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5776 network congestion (or, less commonly, insufficient memory)
5777 @throws randolf::rex::xENOMEM Insufficient memory
5778 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5779 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5780 doesn't refer to a socket
5781 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5782 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5783 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5784 isn't set)
5785 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5786 there's no new data
5787 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5788 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5789 but it really isn't)
5790
5791 @returns The same rsocket object so as to facilitate stacking
5792 @qualifier TLS
5793 *///=========================================================================
5794 rsocket* send(
5795 /// Data to send
5796 const std::vector<char> msg,
5797 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
5798 const int flags = 0) {
5799 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5800 + ", <std::vector>"
5801 + ", " + std::to_string(msg.size())
5802 + ", " + std::to_string(flags)
5803 + ");");
5804 __send(msg.data(), msg.size(), flags);
5805 return this;
5806 }; // -x- rsocket* send -x-
5807
5808 /*======================================================================*//**
5809 @brief
5810 Send data in the form of a C-string to the endpoint.
5811 @par Threads
5812 This method is threadsafe.
5813
5814 @throws randolf::rex::xEBADF The underlying socket is not open
5815 @throws randolf::rex::xECONNRESET Connect reset by peer
5816 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5817 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5818 part of the user address space
5819 @throws randolf::rex::xEINTR Interrupted by a signal
5820 @throws randolf::rex::xEINVAL Invalid argument passed
5821 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5822 occur, but the POSIX sockets documentation lists it as one of the
5823 errors that can be returned, perhaps because some incorrectly
5824 implemented TCP/IP stacks return this error?)
5825 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5826 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5827 and 65,527 bytes for IPv6)
5828 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5829 network congestion (or, less commonly, insufficient memory)
5830 @throws randolf::rex::xENOMEM Insufficient memory
5831 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5832 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5833 doesn't refer to a socket
5834 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5835 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5836 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5837 isn't set)
5838 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5839 there's no new data
5840 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5841 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5842 but it really isn't)
5843
5844 @returns The same rsocket object so as to facilitate stacking
5845 @qualifier POSIX
5846 @qualifier TLS
5847 *///=========================================================================
5848 rsocket* send(
5849 /// Pointer to data to send
5850 const char* msg,
5851 /// Number of bytes to send, or 0 to auto-detect length if message is an ASCIIZ string
5852 size_t len = 0,
5853 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
5854 const int flags = 0) {
5855 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5856 + ", <msg>"
5857 + ", " + std::to_string(len)
5858 + ", " + std::to_string(flags)
5859 + ");");
5860
5861 // --------------------------------------------------------------------------
5862 // Measure size of format string if an ASCIIZ string was indicated.
5863 // --------------------------------------------------------------------------
5864 if (len == 0) len = std::strlen(msg);
5865
5866 // --------------------------------------------------------------------------
5867 // Send string.
5868 // --------------------------------------------------------------------------
5869 __send(msg, len, flags);
5870 return this;
5871 }; // -x- rsocket* send -x-
5872
5873 /*======================================================================*//**
5874 @brief
5875 Send data in the form of an ASCIIZ string to the endpoint, including the
5876 terminating NULL character.
5877 @par Threads
5878 This method is threadsafe.
5879
5880 @throws randolf::rex::xEBADF The underlying socket is not open
5881 @throws randolf::rex::xECONNRESET Connect reset by peer
5882 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5883 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5884 part of the user address space
5885 @throws randolf::rex::xEINTR Interrupted by a signal
5886 @throws randolf::rex::xEINVAL Invalid argument passed
5887 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5888 occur, but the POSIX sockets documentation lists it as one of the
5889 errors that can be returned, perhaps because some incorrectly
5890 implemented TCP/IP stacks return this error?)
5891 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5892 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5893 and 65,527 bytes for IPv6)
5894 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5895 network congestion (or, less commonly, insufficient memory)
5896 @throws randolf::rex::xENOMEM Insufficient memory
5897 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5898 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5899 doesn't refer to a socket
5900 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5901 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5902 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5903 isn't set)
5904 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5905 there's no new data
5906 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5907 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5908 but it really isn't)
5909
5910 @returns The same rsocket object so as to facilitate stacking
5911 @see recv_asciiz(const size_t, const int)
5912 @see sendz(const char*, const int) which doesn't transmit the terminating
5913 NULL character
5914 @qualifier TLS
5915 *///=========================================================================
5916 rsocket* send_asciiz(
5917 /// Pointer to data to send
5918 const char* msg,
5919 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
5920 const int flags = 0) {
5921 if (__debug) debug("send_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5922 + ", <msg>"
5923 + ", " + std::to_string(std::strlen(msg) + 1)
5924 + ", " + std::to_string(flags)
5925 + ");");
5926 __send(msg, std::strlen(msg) + 1, flags);
5927 return this;
5928 }; // -x- rsocket* send_asciiz -x-
5929
5930 /*======================================================================*//**
5931 @brief
5932 Send one 8-bit byte (one unsigned character) of data to the endpoint.
5933 @par Threads
5934 This method is threadsafe.
5935
5936 @throws randolf::rex::xEBADF The underlying socket is not open
5937 @throws randolf::rex::xECONNRESET Connect reset by peer
5938 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
5939 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5940 part of the user address space
5941 @throws randolf::rex::xEINTR Interrupted by a signal
5942 @throws randolf::rex::xEINVAL Invalid argument passed
5943 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
5944 occur, but the POSIX sockets documentation lists it as one of the
5945 errors that can be returned, perhaps because some incorrectly
5946 implemented TCP/IP stacks return this error?)
5947 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
5948 sent atomically (the maximum size is typically 65,507 bytes for IPv4
5949 and 65,527 bytes for IPv6)
5950 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
5951 network congestion (or, less commonly, insufficient memory)
5952 @throws randolf::rex::xENOMEM Insufficient memory
5953 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5954 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5955 doesn't refer to a socket
5956 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5957 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
5958 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
5959 isn't set)
5960 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5961 there's no new data
5962 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
5963 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5964 but it really isn't)
5965
5966 @returns The same rsocket object so as to facilitate stacking
5967 @see recv_byte
5968 @see recv_char
5969 @see send_char
5970 @qualifier TLS
5971 *///=========================================================================
5972 rsocket* send_byte(
5973 /// Data to send
5974 const u_char value,
5975 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
5976 const int flags = 0) {
5977 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5978 + ", <value>"
5979 + ", " + std::to_string(sizeof(value))
5980 + ", " + std::to_string(flags)
5981 + ");");
5982 __send(&value, sizeof(value), flags);
5983 return this;
5984 }; // -x- rsocket* send_byte -x-
5985
5986 /*======================================================================*//**
5987 @brief
5988 Send one signed character (one 8-bit byte) of data to the endpoint.
5989 @copydoc send_byte(char, int)
5990 @returns The same rsocket object so as to facilitate stacking
5991 @see recv_byte
5992 @see recv_char
5993 @see send_byte
5994 @qualifier TLS
5995 *///=========================================================================
5996 rsocket* send_char(
5997 /// Data to send
5998 const char value,
5999 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6000 const int flags = 0) {
6001 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6002 + ", <value>"
6003 + ", " + std::to_string(sizeof(value))
6004 + ", " + std::to_string(flags)
6005 + ");");
6006 __send(&value, sizeof(value), flags);
6007 return this;
6008 }; // -x- rsocket* send_char -x-
6009
6010 /*======================================================================*//**
6011 @brief
6012 Send the EoL sequence to the endpoint.
6013 @par Threads
6014 This method is threadsafe.
6015
6016 @throws randolf::rex::xEBADF The underlying socket is not open
6017 @throws randolf::rex::xECONNRESET Connect reset by peer
6018 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6019 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6020 part of the user address space
6021 @throws randolf::rex::xEINTR Interrupted by a signal
6022 @throws randolf::rex::xEINVAL Invalid argument passed
6023 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6024 occur, but the POSIX sockets documentation lists it as one of the
6025 errors that can be returned, perhaps because some incorrectly
6026 implemented TCP/IP stacks return this error?)
6027 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6028 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6029 and 65,527 bytes for IPv6)
6030 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6031 network congestion (or, less commonly, insufficient memory)
6032 @throws randolf::rex::xENOMEM Insufficient memory
6033 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6034 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6035 doesn't refer to a socket
6036 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6037 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6038 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6039 isn't set)
6040 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6041 there's no new data
6042 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6043 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6044 but it really isn't)
6045
6046 @returns The same rsocket object so as to facilitate stacking
6047 @qualifier TLS
6048 *///=========================================================================
6049 rsocket* send_eol(
6050 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6051 const int flags = 0) {
6052 if (__debug) debug("send_eol(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6053 + ", " + std::to_string(flags)
6054 + ");");
6055 __send_eol(flags);
6056 return this;
6057 }; // -x- rsocket* send_eol -x-
6058
6059 /*======================================================================*//**
6060 @brief
6061 Send one byte of data to the endpoint.
6062 @pre
6063 MSB/LSB considerations are important for any integers within your structure,
6064 so be sure to sanitize them with htons(), ntohs(), and related methods prior
6065 to sending, and then after receiving. This way, your data will be protected
6066 against corruption resulting from byte order differences when communicating
6067 between hardware architectures that differ in MSB and LSB byte ordering.
6068 @par Threads
6069 This method is threadsafe.
6070
6071 @throws randolf::rex::xEBADF The underlying socket is not open
6072 @throws randolf::rex::xECONNRESET Connect reset by peer
6073 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6074 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6075 part of the user address space
6076 @throws randolf::rex::xEINTR Interrupted by a signal
6077 @throws randolf::rex::xEINVAL Invalid argument passed
6078 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6079 occur, but the POSIX sockets documentation lists it as one of the
6080 errors that can be returned, perhaps because some incorrectly
6081 implemented TCP/IP stacks return this error?)
6082 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6083 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6084 and 65,527 bytes for IPv6)
6085 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6086 network congestion (or, less commonly, insufficient memory)
6087 @throws randolf::rex::xENOMEM Insufficient memory
6088 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6089 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6090 doesn't refer to a socket
6091 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6092 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6093 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6094 isn't set)
6095 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6096 there's no new data
6097 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6098 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6099 but it really isn't)
6100
6101 @returns The same rsocket object so as to facilitate stacking
6102 @qualifier TLS
6103 *///=========================================================================
6104 template <typename T> rsocket* send_struct(
6105 /// Data to send
6106 const T value,
6107 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6108 const int flags = 0) {
6109 if (__debug) debug("send_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6110 + ", <value>"
6111 + ", " + std::to_string(sizeof(value))
6112 + ", " + std::to_string(flags)
6113 + ");");
6114 __send(&value, sizeof(value), flags);
6115 return this;
6116 }; // -x- rsocket* send_struct -x-
6117
6118 /*======================================================================*//**
6119 @brief
6120 Send one 16-bit unsigned integer of data in LSB (little endian) order to the
6121 endpoint.
6122 @par Threads
6123 This method is threadsafe.
6124
6125 @throws randolf::rex::xEBADF The underlying socket is not open
6126 @throws randolf::rex::xECONNRESET Connect reset by peer
6127 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6128 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6129 part of the user address space
6130 @throws randolf::rex::xEINTR Interrupted by a signal
6131 @throws randolf::rex::xEINVAL Invalid argument passed
6132 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6133 occur, but the POSIX sockets documentation lists it as one of the
6134 errors that can be returned, perhaps because some incorrectly
6135 implemented TCP/IP stacks return this error?)
6136 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6137 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6138 and 65,527 bytes for IPv6)
6139 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6140 network congestion (or, less commonly, insufficient memory)
6141 @throws randolf::rex::xENOMEM Insufficient memory
6142 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6143 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6144 doesn't refer to a socket
6145 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6146 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6147 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6148 isn't set)
6149 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6150 there's no new data
6151 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6152 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6153 but it really isn't)
6154
6155 @returns The same rsocket object so as to facilitate stacking
6156 @qualifier TLS
6157 *///=========================================================================
6158 rsocket* send_uint16_lsb(
6159 /// Data to send
6160 const uint16_t value,
6161 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6162 const int flags = 0) {
6163 uint16_t buf = !__endian_is_msb ? value : ntohs(value);
6164 if (__debug) debug("send_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6165 + ", " + std::to_string(value)
6166 + ", " + std::to_string(sizeof(buf))
6167 + ", " + std::to_string(flags)
6168 + ");");
6169 __send(&buf, sizeof(buf), flags);
6170 return this;
6171 }; // -x- rsocket* send_uint16_lsb -x-
6172
6173 /*======================================================================*//**
6174 @brief
6175 Send one 16-bit integer of data in MSB (big endian) order to the endpoint.
6176 @par Threads
6177 This method is threadsafe.
6178
6179 @throws randolf::rex::xEBADF The underlying socket is not open
6180 @throws randolf::rex::xECONNRESET Connect reset by peer
6181 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6182 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6183 part of the user address space
6184 @throws randolf::rex::xEINTR Interrupted by a signal
6185 @throws randolf::rex::xEINVAL Invalid argument passed
6186 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6187 occur, but the POSIX sockets documentation lists it as one of the
6188 errors that can be returned, perhaps because some incorrectly
6189 implemented TCP/IP stacks return this error?)
6190 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6191 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6192 and 65,527 bytes for IPv6)
6193 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6194 network congestion (or, less commonly, insufficient memory)
6195 @throws randolf::rex::xENOMEM Insufficient memory
6196 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6197 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6198 doesn't refer to a socket
6199 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6200 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6201 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6202 isn't set)
6203 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6204 there's no new data
6205 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6206 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6207 but it really isn't)
6208
6209 @returns The same rsocket object so as to facilitate stacking
6210 @qualifier TLS
6211 *///=========================================================================
6212 rsocket* send_uint16_msb(
6213 /// Data to send
6214 const uint16_t value,
6215 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6216 const int flags = 0) {
6217 int16_t buf = __endian_is_msb ? value : htons(value);
6218 if (__debug) debug("send_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6219 + ", " + std::to_string(value)
6220 + ", " + std::to_string(sizeof(buf))
6221 + ", " + std::to_string(flags)
6222 + ");");
6223 __send(&buf, sizeof(buf), flags);
6224 return this;
6225 }; // -x- rsocket* send_int16_msb -x-
6226
6227 /*======================================================================*//**
6228 @brief
6229 Send one 32-bit unsigned integer of data in LSB (little endian) order to the
6230 endpoint.
6231 @par Threads
6232 This method is threadsafe.
6233
6234 @throws randolf::rex::xEBADF The underlying socket is not open
6235 @throws randolf::rex::xECONNRESET Connect reset by peer
6236 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6237 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6238 part of the user address space
6239 @throws randolf::rex::xEINTR Interrupted by a signal
6240 @throws randolf::rex::xEINVAL Invalid argument passed
6241 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6242 occur, but the POSIX sockets documentation lists it as one of the
6243 errors that can be returned, perhaps because some incorrectly
6244 implemented TCP/IP stacks return this error?)
6245 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6246 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6247 and 65,527 bytes for IPv6)
6248 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6249 network congestion (or, less commonly, insufficient memory)
6250 @throws randolf::rex::xENOMEM Insufficient memory
6251 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6252 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6253 doesn't refer to a socket
6254 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6255 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6256 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6257 isn't set)
6258 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6259 there's no new data
6260 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6261 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6262 but it really isn't)
6263
6264 @returns The same rsocket object so as to facilitate stacking
6265 @qualifier TLS
6266 *///=========================================================================
6267 rsocket* send_uint32_lsb(
6268 /// Data to send
6269 const uint32_t value,
6270 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6271 const int flags = 0) {
6272 uint32_t buf = !__endian_is_msb ? value : ntohl(value);
6273 if (__debug) debug("send_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6274 + ", " + std::to_string(value)
6275 + ", " + std::to_string(sizeof(buf))
6276 + ", " + std::to_string(flags)
6277 + ");");
6278 __send(&buf, sizeof(buf), flags);
6279 return this;
6280 }; // -x- rsocket* send_uint32_lsb -x-
6281
6282 /*======================================================================*//**
6283 @brief
6284 Send one 32-bit unsigned integer of data in MSB (big endian) order to the
6285 endpoint.
6286 @par Threads
6287 This method is threadsafe.
6288
6289 @throws randolf::rex::xEBADF The underlying socket is not open
6290 @throws randolf::rex::xECONNRESET Connect reset by peer
6291 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6292 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6293 part of the user address space
6294 @throws randolf::rex::xEINTR Interrupted by a signal
6295 @throws randolf::rex::xEINVAL Invalid argument passed
6296 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6297 occur, but the POSIX sockets documentation lists it as one of the
6298 errors that can be returned, perhaps because some incorrectly
6299 implemented TCP/IP stacks return this error?)
6300 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6301 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6302 and 65,527 bytes for IPv6)
6303 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6304 network congestion (or, less commonly, insufficient memory)
6305 @throws randolf::rex::xENOMEM Insufficient memory
6306 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6307 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6308 doesn't refer to a socket
6309 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6310 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6311 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6312 isn't set)
6313 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6314 there's no new data
6315 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6316 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6317 but it really isn't)
6318
6319 @returns The same rsocket object so as to facilitate stacking
6320 @qualifier TLS
6321 *///=========================================================================
6322 rsocket* send_uint32_msb(
6323 /// Data to send
6324 const uint32_t value,
6325 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6326 const int flags = 0) {
6327 uint32_t buf = __endian_is_msb ? value : htonl(value);
6328 if (__debug) debug("send_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6329 + ", " + std::to_string(value)
6330 + ", " + std::to_string(sizeof(buf))
6331 + ", " + std::to_string(flags)
6332 + ");");
6333 __send(&buf, sizeof(buf), flags);
6334 return this;
6335 }; // -x- rsocket* send_uint32_msb -x-
6336
6337 /*======================================================================*//**
6338 @brief
6339 Send one 64-bit unsigned integer of data in LSB (little endian) order to the
6340 endpoint.
6341 @par Threads
6342 This method is threadsafe.
6343
6344 @throws randolf::rex::xEBADF The underlying socket is not open
6345 @throws randolf::rex::xECONNRESET Connect reset by peer
6346 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6347 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6348 part of the user address space
6349 @throws randolf::rex::xEINTR Interrupted by a signal
6350 @throws randolf::rex::xEINVAL Invalid argument passed
6351 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6352 occur, but the POSIX sockets documentation lists it as one of the
6353 errors that can be returned, perhaps because some incorrectly
6354 implemented TCP/IP stacks return this error?)
6355 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6356 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6357 and 65,527 bytes for IPv6)
6358 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6359 network congestion (or, less commonly, insufficient memory)
6360 @throws randolf::rex::xENOMEM Insufficient memory
6361 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6362 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6363 doesn't refer to a socket
6364 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6365 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6366 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6367 isn't set)
6368 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6369 there's no new data
6370 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6371 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6372 but it really isn't)
6373
6374 @returns The same rsocket object so as to facilitate stacking
6375 @qualifier TLS
6376 *///=========================================================================
6377 rsocket* send_uint64_lsb(
6378 /// Data to send
6379 const uint64_t value,
6380 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6381 const int flags = 0) {
6382 uint64_t buf = !__endian_is_msb ? value : ((((uint64_t)ntohl((value) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((value) >> 32)));
6383 if (__debug) debug("send_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6384 + ", " + std::to_string(value)
6385 + ", " + std::to_string(sizeof(buf))
6386 + ", " + std::to_string(flags)
6387 + ");");
6388 __send(&buf, sizeof(buf), flags);
6389 return this;
6390 }; // -x- rsocket* send_uint64_lsb -x-
6391
6392 /*======================================================================*//**
6393 @brief
6394 Send one 64-bit unsigned integer of data in MSB (big endian) order to the
6395 endpoint.
6396 @par Threads
6397 This method is threadsafe.
6398
6399 @throws randolf::rex::xEBADF The underlying socket is not open
6400 @throws randolf::rex::xECONNRESET Connect reset by peer
6401 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6402 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6403 part of the user address space
6404 @throws randolf::rex::xEINTR Interrupted by a signal
6405 @throws randolf::rex::xEINVAL Invalid argument passed
6406 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6407 occur, but the POSIX sockets documentation lists it as one of the
6408 errors that can be returned, perhaps because some incorrectly
6409 implemented TCP/IP stacks return this error?)
6410 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6411 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6412 and 65,527 bytes for IPv6)
6413 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6414 network congestion (or, less commonly, insufficient memory)
6415 @throws randolf::rex::xENOMEM Insufficient memory
6416 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6417 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6418 doesn't refer to a socket
6419 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6420 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6421 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6422 isn't set)
6423 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6424 there's no new data
6425 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6426 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6427 but it really isn't)
6428
6429 @returns The same rsocket object so as to facilitate stacking
6430 @qualifier TLS
6431 *///=========================================================================
6432 rsocket* send_uint64_msb(
6433 /// Data to send
6434 const uint64_t value,
6435 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6436 const int flags = 0) {
6437 uint64_t buf = __endian_is_msb ? value : ((((uint64_t)htonl((value) & 0xffffffffUL)) << 32) | htonl((uint32_t)((value) >> 32)));
6438 if (__debug) debug("send_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6439 + ", " + std::to_string(value)
6440 + ", " + std::to_string(sizeof(buf))
6441 + ", " + std::to_string(flags)
6442 + ");");
6443 __send(&buf, sizeof(buf), flags);
6444 return this;
6445 }; // -x- rsocket* send_uint64_msb -x-
6446
6447 /*======================================================================*//**
6448 @brief
6449 Send data in the form of a std::string to the endpoint, with an EoL sequence
6450 appended.
6451 @par Threads
6452 This method is threadsafe.
6453
6454 @throws randolf::rex::xEBADF The underlying socket is not open
6455 @throws randolf::rex::xECONNRESET Connect reset by peer
6456 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6457 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6458 part of the user address space
6459 @throws randolf::rex::xEINTR Interrupted by a signal
6460 @throws randolf::rex::xEINVAL Invalid argument passed
6461 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6462 occur, but the POSIX sockets documentation lists it as one of the
6463 errors that can be returned, perhaps because some incorrectly
6464 implemented TCP/IP stacks return this error?)
6465 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6466 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6467 and 65,527 bytes for IPv6)
6468 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6469 network congestion (or, less commonly, insufficient memory)
6470 @throws randolf::rex::xENOMEM Insufficient memory
6471 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6472 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6473 doesn't refer to a socket
6474 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6475 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6476 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6477 isn't set)
6478 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6479 there's no new data
6480 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6481 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6482 but it really isn't)
6483
6484 @returns The same rsocket object so as to facilitate stacking
6485 @see eol
6486 @see printfline
6487 @see recvline(const size_t, const int)
6488 @see vprintfline
6489 @qualifier TLS
6490 *///=========================================================================
6491 rsocket* sendline(
6492 /// Data to send
6493 const std::string msg = std::string(),
6494 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6495 const int flags = 0) {
6496 if (__debug) debug("sendline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6497 + ", <std::string>"
6498 + ", " + std::to_string(msg.length())
6499 + "+" + std::to_string(__eol_out.length())
6500 + ", " + std::to_string(flags)
6501 + ");");
6502 __sendline(msg.c_str(), msg.length(), flags);
6503 return this;
6504 }; // -x- rsocket* sendline -x-
6505
6506 /*======================================================================*//**
6507 @brief
6508 Send data in the form of a "msghdr" structure to a specific endpoint.
6509 @warning
6510 This method is not compatible with TLS.
6511 @par Threads
6512 This method is threadsafe.
6513
6514 @throws randolf::rex::xEBADF The underlying socket is not open
6515 @throws randolf::rex::xECONNRESET Connect reset by peer
6516 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6517 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6518 part of the user address space
6519 @throws randolf::rex::xEINTR Interrupted by a signal
6520 @throws randolf::rex::xEINVAL Invalid argument passed
6521 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6522 occur, but the POSIX sockets documentation lists it as one of the
6523 errors that can be returned, perhaps because some incorrectly
6524 implemented TCP/IP stacks return this error?)
6525 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6526 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6527 and 65,527 bytes for IPv6)
6528 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6529 network congestion (or, less commonly, insufficient memory)
6530 @throws randolf::rex::xENOMEM Insufficient memory
6531 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6532 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6533 doesn't refer to a socket
6534 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6535 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6536 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6537 isn't set)
6538 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6539 there's no new data
6540 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6541 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6542 but it really isn't)
6543
6544 @returns The same rsocket object so as to facilitate stacking
6545 @qualifier POSIX
6546 *///=========================================================================
6547 rsocket* sendmsg(
6548 /// Pointer to data to send
6549 const struct msghdr* msg,
6550 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6551 const int flags = 0) {
6552 if (__debug) debug("sendmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6553 + ", <msghdr>"
6554 + ", " + std::to_string(flags)
6555 + ");");
6556 __track_bytes_tx(__rc_check(::sendmsg(__socket_fd, msg, flags)));
6557 return this;
6558 }; // -x- rsocket* sendmsg -x-
6559
6560 /*======================================================================*//**
6561 @brief
6562 Send data in the form of a "mmsghdr" structure to a specific endpoint.
6563 @warning
6564 This method is not compatible with TLS.
6565 @par Threads
6566 This method is threadsafe.
6567
6568 @throws randolf::rex::xEBADF The underlying socket is not open
6569 @throws randolf::rex::xECONNRESET Connect reset by peer
6570 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6571 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6572 part of the user address space
6573 @throws randolf::rex::xEINTR Interrupted by a signal
6574 @throws randolf::rex::xEINVAL Invalid argument passed
6575 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6576 occur, but the POSIX sockets documentation lists it as one of the
6577 errors that can be returned, perhaps because some incorrectly
6578 implemented TCP/IP stacks return this error?)
6579 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6580 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6581 and 65,527 bytes for IPv6)
6582 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6583 network congestion (or, less commonly, insufficient memory)
6584 @throws randolf::rex::xENOMEM Insufficient memory
6585 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6586 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6587 doesn't refer to a socket
6588 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6589 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6590 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6591 isn't set)
6592 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6593 there's no new data
6594 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6595 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6596 but it really isn't)
6597
6598 @returns The same rsocket object so as to facilitate stacking
6599 @qualifier POSIX
6600 *///=========================================================================
6601 rsocket* sendmmsg(
6602 /// Pointer to data to send
6603 struct mmsghdr* mmsg,
6604 /// Size of target endpoint structure
6605 const unsigned int vlen = sizeof(mmsghdr),
6606 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6607 const int flags = 0) {
6608 if (__debug) debug("sendmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6609 + ", <mmsghdr>"
6610 + ", " + std::to_string(vlen)
6611 + ", " + std::to_string(flags)
6612 + ");");
6613 __track_bytes_tx(__rc_check(::sendmmsg(__socket_fd, mmsg, vlen, flags)));
6614 return this;
6615 }; // -x- rsocket* sendmsg -x-
6616
6617 /*======================================================================*//**
6618 @brief
6619 Send data in the form of a std::string to a specific endpoint.
6620 @warning
6621 This method is not compatible with TLS.
6622 @par Threads
6623 This method is threadsafe.
6624
6625 @throws randolf::rex::xEBADF The underlying socket is not open
6626 @throws randolf::rex::xECONNRESET Connect reset by peer
6627 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6628 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6629 part of the user address space
6630 @throws randolf::rex::xEINTR Interrupted by a signal
6631 @throws randolf::rex::xEINVAL Invalid argument passed
6632 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6633 occur, but the POSIX sockets documentation lists it as one of the
6634 errors that can be returned, perhaps because some incorrectly
6635 implemented TCP/IP stacks return this error?)
6636 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6637 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6638 and 65,527 bytes for IPv6)
6639 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6640 network congestion (or, less commonly, insufficient memory)
6641 @throws randolf::rex::xENOMEM Insufficient memory
6642 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6643 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6644 doesn't refer to a socket
6645 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6646 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6647 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6648 isn't set)
6649 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6650 there's no new data
6651 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6652 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6653 but it really isn't)
6654
6655 @returns The same rsocket object so as to facilitate stacking
6656 @qualifier POSIX
6657 *///=========================================================================
6658 rsocket* sendto(
6659 /// Data to send
6660 const std::string msg,
6661 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6662 const int flags,
6663 /// Target endpoint address structure
6664 const struct sockaddr *to,
6665 /// Size of target endpoint structure
6666 socklen_t tolen = sizeof(sockaddr)) {
6667 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6668 + ", <std::string>"
6669 + ", " + std::to_string(flags)
6670 + ", <sockaddr>"
6671 + ", " + std::to_string(tolen)
6672 + ");");
6673 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg.c_str(), msg.length(), flags, to, tolen)));
6674 return this;
6675 }; // -x- rsocket* sendto -x- // TODO: Create easier-to-use variants
6676
6677 /*======================================================================*//**
6678 @brief
6679 Send data in the form of a C-string to a specific endpoint.
6680 @warning
6681 This method is not compatible with TLS.
6682 @par Threads
6683 This method is threadsafe.
6684
6685 @throws randolf::rex::xEBADF The underlying socket is not open
6686 @throws randolf::rex::xECONNRESET Connect reset by peer
6687 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6688 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6689 part of the user address space
6690 @throws randolf::rex::xEINTR Interrupted by a signal
6691 @throws randolf::rex::xEINVAL Invalid argument passed
6692 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6693 occur, but the POSIX sockets documentation lists it as one of the
6694 errors that can be returned, perhaps because some incorrectly
6695 implemented TCP/IP stacks return this error?)
6696 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6697 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6698 and 65,527 bytes for IPv6)
6699 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6700 network congestion (or, less commonly, insufficient memory)
6701 @throws randolf::rex::xENOMEM Insufficient memory
6702 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6703 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6704 doesn't refer to a socket
6705 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6706 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6707 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6708 isn't set)
6709 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6710 there's no new data
6711 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6712 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6713 but it really isn't)
6714
6715 @returns The same rsocket object so as to facilitate stacking
6716 @qualifier POSIX
6717 *///=========================================================================
6718 rsocket* sendto(
6719 /// Pointer to data to send
6720 const char* msg,
6721 /// Number of bytes to send
6722 const size_t len,
6723 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6724 const int flags,
6725 /// Target endpoint address structure
6726 const struct sockaddr *to,
6727 /// Size of target endpoint structure
6728 socklen_t tolen = sizeof(sockaddr)) {
6729 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6730 + ", <msg>"
6731 + ", " + std::to_string(std::strlen(msg))
6732 + ", " + std::to_string(flags)
6733 + ", <sockaddr>"
6734 + ", " + std::to_string(tolen)
6735 + ");");
6736 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, len, flags, to, tolen)));
6737 return this;
6738 }; // -x- rsocket* sendto -x-
6739
6740 /*======================================================================*//**
6741 @brief
6742 Send data in the form of an ASCIIZ string to the endpoint. The terminating
6743 NULL character won't be transmitted.
6744 @par Threads
6745 This method is threadsafe.
6746
6747 @throws randolf::rex::xEBADF The underlying socket is not open
6748 @throws randolf::rex::xECONNRESET Connect reset by peer
6749 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6750 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6751 part of the user address space
6752 @throws randolf::rex::xEINTR Interrupted by a signal
6753 @throws randolf::rex::xEINVAL Invalid argument passed
6754 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6755 occur, but the POSIX sockets documentation lists it as one of the
6756 errors that can be returned, perhaps because some incorrectly
6757 implemented TCP/IP stacks return this error?)
6758 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6759 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6760 and 65,527 bytes for IPv6)
6761 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6762 network congestion (or, less commonly, insufficient memory)
6763 @throws randolf::rex::xENOMEM Insufficient memory
6764 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6765 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6766 doesn't refer to a socket
6767 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6768 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6769 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6770 isn't set)
6771 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6772 there's no new data
6773 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6774 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6775 but it really isn't)
6776
6777 @returns The same rsocket object so as to facilitate stacking
6778 @see recvz(const size_t, const int)
6779 @see send_asciiz(const char*, const int) which also transmits the terminating
6780 NULL character
6781 @qualifier TLS
6782 *///=========================================================================
6783 rsocket* sendz(
6784 /// Pointer to data to send
6785 const char* msg,
6786 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6787 const int flags = 0) {
6788 if (__debug) debug("sendz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6789 + ", <msg>"
6790 + ", " + std::to_string(std::strlen(msg))
6791 + ", " + std::to_string(flags)
6792 + ");");
6793 __send(msg, std::strlen(msg), flags);
6794 return this;
6795 }; // -x- rsocket* sendz -x-
6796
6797 /*======================================================================*//**
6798 @brief
6799 Send data in the form of an ASCIIZ string to a specific endpoint. The
6800 terminating NULL character won't be transmitted.
6801 @warning
6802 This method is not compatible with TLS.
6803 @par Threads
6804 This method is threadsafe.
6805
6806 @throws randolf::rex::xEBADF The underlying socket is not open
6807 @throws randolf::rex::xECONNRESET Connect reset by peer
6808 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6809 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6810 part of the user address space
6811 @throws randolf::rex::xEINTR Interrupted by a signal
6812 @throws randolf::rex::xEINVAL Invalid argument passed
6813 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6814 occur, but the POSIX sockets documentation lists it as one of the
6815 errors that can be returned, perhaps because some incorrectly
6816 implemented TCP/IP stacks return this error?)
6817 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6818 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6819 and 65,527 bytes for IPv6)
6820 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6821 network congestion (or, less commonly, insufficient memory)
6822 @throws randolf::rex::xENOMEM Insufficient memory
6823 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6824 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6825 doesn't refer to a socket
6826 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6827 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6828 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6829 isn't set)
6830 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6831 there's no new data
6832 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6833 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6834 but it really isn't)
6835
6836 @returns The same rsocket object so as to facilitate stacking
6837 *///=========================================================================
6838 rsocket* sendzto(
6839 /// Pointer to data to send
6840 const char* msg,
6841 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
6842 const int flags,
6843 /// Target endpoint address structure
6844 const struct sockaddr *to,
6845 /// Size of target endpoint structure
6846 socklen_t tolen = sizeof(sockaddr)) {
6847 if (__debug) debug("sendzto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6848 + ", <msg>"
6849 + ", " + std::to_string(std::strlen(msg))
6850 + ", " + std::to_string(flags)
6851 + ", <sockaddr>"
6852 + ", " + std::to_string(tolen)
6853 + ");");
6854 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, std::strlen(msg), flags, to, tolen)));
6855 return this;
6856 }; // -x- rsocket* sendzto -x-
6857
6858 /*======================================================================*//**
6859 @brief
6860 Set socket option to the specific integer.
6861
6862 @par Notes
6863 These setsockopt() methods take an integer or character value directly, or a
6864 pointer to a structure, and then rsocket handles the remaining tedious
6865 technical details behind-the-scenes for you when calling the underlying
6866 socket's setsockopt() function.
6867
6868 @throws randolf::rex::xEBADF The underlying socket is not open
6869 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6870 part of the user address space
6871 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
6872 valid for this socket's family (a.k.a., communication domain)
6873 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
6874 is not supported
6875 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6876 doesn't refer to a socket
6877
6878 @returns The same rsocket object so as to facilitate stacking
6879 @qualifier POSIX
6880 @qualifier TLS
6881 *///=========================================================================
6882 rsocket* setsockopt(
6883 /// The level at which the option resides; typically @c SOL_SOCKET
6884 const int level,
6885 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
6886 const int option,
6887 /// The value that this socket option will be set to
6888 const int value) {
6889 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
6890 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
6891 return this;
6892 }; // -x- rsocket* setsockopt -x-
6893
6894 /*======================================================================*//**
6895 @brief
6896 Set socket option to the specific unsigned integer.
6897 @copydetails setsockopt(const int, const int, const int)
6898
6899 @pre
6900 For any values that require a u_int, you'll need to explicitly cast this type
6901 when specifying the value directly; for example: (u_int)32768
6902
6903 @throws randolf::rex::xEBADF The underlying socket is not open
6904 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6905 part of the user address space
6906 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
6907 valid for this socket's family (a.k.a., communication domain)
6908 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
6909 is not supported
6910 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6911 doesn't refer to a socket
6912
6913 @returns The same rsocket object so as to facilitate stacking
6914 @qualifier TLS
6915 *///=========================================================================
6916 rsocket* setsockopt(
6917 /// The level at which the option resides; typically @c SOL_SOCKET
6918 const int level,
6919 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
6920 const int option,
6921 /// The value that this socket option will be set to
6922 const u_int value) {
6923 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
6924 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
6925 return this;
6926 }; // -x- rsocket* setsockopt -x-
6927
6928 /*======================================================================*//**
6929 @brief
6930 Set socket option to the specific unsigned character.
6931 @copydetails setsockopt(const int, const int, const int)
6932 @qualifier TLS
6933 *///=========================================================================
6934 rsocket* setsockopt(
6935 /// The level at which the option resides; typically @c SOL_SOCKET
6936 const int level,
6937 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
6938 const int option,
6939 /// The value that this socket option will be set to
6940 const u_char value) {
6941 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
6942 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
6943 return this;
6944 }; // -x- rsocket* setsockopt -x-
6945
6946 /*======================================================================*//**
6947 @brief
6948 Set socket option to the specific structure.
6949 @copydetails setsockopt(const int, const int, const int)
6950 @qualifier TLS
6951 *///=========================================================================
6952 rsocket* setsockopt(
6953 /// The level at which the option resides; typically @c SOL_SOCKET
6954 const int level,
6955 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
6956 const int option,
6957 /// The structure that this socket option will be set to
6958 const linger& value) {
6959 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
6960 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
6961 return this;
6962 }; // -x- rsocket* setsockopt -x-
6963
6964 /*======================================================================*//**
6965 @copydoc setsockopt(const int, const int, const linger&)
6966 @qualifier TLS
6967 *///=========================================================================
6968 rsocket* setsockopt(
6969 /// The level at which the option resides; typically @c SOL_SOCKET
6970 const int level,
6971 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
6972 const int option,
6973 /// The structure that this socket option will be set to
6974 const timeval& value) {
6975 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
6976 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
6977 return this;
6978 }; // -x- rsocket* setsockopt -x-
6979
6980 /*======================================================================*//**
6981 @copydoc setsockopt(const int, const int, const linger&)
6982 @qualifier TLS
6983 *///=========================================================================
6984 rsocket* setsockopt(
6985 /// The level at which the option resides; typically @c SOL_SOCKET
6986 const int level,
6987 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
6988 const int option,
6989 /// The structure that this socket option will be set to
6990 const in_addr& value) {
6991 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
6992 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
6993 return this;
6994 }; // -x- rsocket* setsockopt -x-
6995
6996 /*======================================================================*//**
6997 @copydoc setsockopt(const int, const int, const linger&)
6998 @qualifier TLS
6999 *///=========================================================================
7000 rsocket* setsockopt(
7001 /// The level at which the option resides; typically @c SOL_SOCKET
7002 const int level,
7003 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7004 const int option,
7005 /// The structure that this socket option will be set to
7006 const ip_mreq& value) {
7007 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7008 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7009 return this;
7010 }; // -x- rsocket* setsockopt -x-
7011
7012 /*======================================================================*//**
7013 @copydoc setsockopt(const int, const int, const linger&)
7014 @qualifier TLS
7015 *///=========================================================================
7016 rsocket* setsockopt(
7017 /// The level at which the option resides; typically @c SOL_SOCKET
7018 const int level,
7019 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7020 const int option,
7021 /// The structure that this socket option will be set to
7022 const ip_mreq_source& value) {
7023 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7024 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7025 return this;
7026 }; // -x- rsocket* setsockopt -x-
7027
7028 /*======================================================================*//**
7029 @copydoc setsockopt(const int, const int, const linger&)
7030 @qualifier TLS
7031 *///=========================================================================
7032 rsocket* setsockopt(
7033 /// The level at which the option resides; typically @c SOL_SOCKET
7034 const int level,
7035 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7036 const int option,
7037 /// The structure that this socket option will be set to
7038 const icmp6_filter& value) {
7039 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7040 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7041 return this;
7042 }; // -x- rsocket* setsockopt -x-
7043
7044 /*======================================================================*//**
7045 @copydoc setsockopt(const int, const int, const linger&)
7046 @qualifier TLS
7047 *///=========================================================================
7048 rsocket* setsockopt(
7049 /// The level at which the option resides; typically @c SOL_SOCKET
7050 const int level,
7051 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7052 const int option,
7053 /// The structure that this socket option will be set to
7054 const sockaddr_in6& value) {
7055 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7056 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7057 return this;
7058 }; // -x- rsocket* setsockopt -x-
7059
7060 /*======================================================================*//**
7061 @copydoc setsockopt(const int, const int, const linger&)
7062 @qualifier TLS
7063 *///=========================================================================
7064 rsocket* setsockopt(
7065 /// The level at which the option resides; typically @c SOL_SOCKET
7066 const int level,
7067 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7068 const int option,
7069 /// The structure that this socket option will be set to
7070 const ip6_mtuinfo& value) {
7071 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7072 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7073 return this;
7074 }; // -x- rsocket* setsockopt -x-
7075
7076 /*======================================================================*//**
7077 @copydoc setsockopt(const int, const int, const linger&)
7078 @qualifier TLS
7079 *///=========================================================================
7080 rsocket* setsockopt(
7081 /// The level at which the option resides; typically @c SOL_SOCKET
7082 const int level,
7083 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7084 const int option,
7085 /// The structure that this socket option will be set to
7086 const ipv6_mreq& value) {
7087 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7088 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7089 return this;
7090 }; // -x- rsocket* setsockopt -x-
7091
7092 /*======================================================================*//**
7093 @copydoc setsockopt(const int, const int, const linger&)
7094 @qualifier TLS
7095 *///=========================================================================
7096 rsocket* setsockopt(
7097 /// The level at which the option resides; typically @c SOL_SOCKET
7098 const int level,
7099 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7100 const int option,
7101 /// The structure that this socket option will be set to
7102 const group_req& value) {
7103 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7104 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7105 return this;
7106 }; // -x- rsocket* setsockopt -x-
7107
7108 /*======================================================================*//**
7109 @copydoc setsockopt(const int, const int, const linger&)
7110 @qualifier TLS
7111 *///=========================================================================
7112 rsocket* setsockopt(
7113 /// The level at which the option resides; typically @c SOL_SOCKET
7114 const int level,
7115 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7116 const int option,
7117 /// The structure that this socket option will be set to
7118 const group_source_req& value) {
7119 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7120 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7121 return this;
7122 }; // -x- rsocket* setsockopt -x-
7123
7124 /*======================================================================*//**
7125 @brief
7126 Shut down the underlying socket, partially or fully.
7127
7128 <div style=padding-left:32px;>
7129 <table>
7130 <tr>
7131 <td valign=top>SHUT_RD:</td>
7132 <td>Further receives will be disallowed.</td>
7133 </tr>
7134 <tr>
7135 <td valign=top>SHUT_WR:</td>
7136 <td>Further sends will be disallowed (this may cause actions specific
7137 to the protocol family of the socket to occur).</td>
7138 </tr>
7139 <tr>
7140 <td valign=top>SHUT_RDWR:</td>
7141 <td>Further sends and receives will be disallowed (default).</td>
7142 </tr>
7143 </table>
7144 </div>
7145
7146 @throws randolf::rex::xEBADF The underlying socket is not open
7147 @throws randolf::rex::xEINVAL Invalid argument passed
7148 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7149 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7150 doesn't refer to a socket
7151
7152 @returns The same rsocket object so as to facilitate stacking
7153 @qualifier POSIX
7154 @qualifier TLS
7155 *///=========================================================================
7156 rsocket* shutdown(
7157 /// SHUT_RD@n
7158 /// SHUT_RW@n
7159 /// SHUT_RDWR (default)
7160 const int how = SHUT_RDWR) {
7161 if (__debug) debug("shutdown(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7162 + ", " + std::to_string(how)
7163 + ");");
7164 if (__tls) SSL_shutdown(__tls_fd); // We have to shut down TLS connections first, if they're active
7165 __rc_check(::shutdown(__socket_fd, how));
7166 //__socket_connected = false; // TODO: Figure out when to change this to false
7167 return this;
7168 }; // -x- rsocket* shutdown -x-
7169
7170 /*======================================================================*//**
7171 @brief
7172 Complete the configuration of an rsocket that was previously initialized
7173 without any parameters (a.k.a., an "empty rsocket").
7174 @copydetails rsocket()
7175 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
7176 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
7177 @throws randolf::rex::xEALREADY If this socket() method was already used, or
7178 it was used after rsocket() initialized with at least one parameter
7179 @throws randolf::rex::xEINVAL Protocal family invalid or not available
7180 @throws randolf::rex::xEINVAL Invalid flags in type
7181 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
7182 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
7183 @throws randolf::rex::xENOBUFS Insufficient memory
7184 @throws randolf::rex::xENOMEM Insufficient memory
7185 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
7186 supported within the specified family (a.k.a., communication domain)
7187 @returns The same rsocket object so as to facilitate stacking
7188 @see rsocket()
7189 @see socket_family()
7190 @see socket_fd()
7191 @see socket_protocol()
7192 @see socket_type()
7193 @qualifier POSIX
7194 @qualifier TLS
7195 *///=========================================================================
7196 rsocket* socket(
7197 /// Communication domain; usually one of:@n
7198 /// AF_INET (IPv4)@n
7199 /// AF_INET6 (IPv6)@n
7200 /// AF_UNIX (UNIX domain sockets)
7201 const int family,
7202 /// Communication semantics; usually one of:@n
7203 /// SOCK_STREAM (common for TCP)@n
7204 /// SOCK_DGRAM (common for UDP)
7205 const int type = SOCK_STREAM,
7206 /// Network protocol; usually one of:@n
7207 /// IPPROTO_TCP@n
7208 /// IPPROTO_UDP@n
7209 /// IPPROTO_IP@n
7210 /// PF_UNSPEC (auto-detect)
7211 const int protocol = PF_UNSPEC) {
7212 __socket(family, type, protocol);
7213 return this;
7214 }; // -x- rsocket* socket -x-
7215
7216 /*======================================================================*//**
7217 @brief
7218 Get underlying socket family/domain constant (SO_DOMAIN).
7219 @returns socket family/domain constant
7220 @see port()
7221 @see socket()
7222 @see socket_fd()
7223 @see socket_protocol()
7224 @see socket_type()
7225 @qualifier TLS
7226 *///=========================================================================
7227 const int socket_family() noexcept { return __socket_addr.ss_family; }; // -x- int socket_family -x-
7228
7229 /*======================================================================*//**
7230 @brief
7231 Get underlying socket descriptor/handle.
7232 @returns socket descriptor/handle
7233 @returns 0 = socket not yet allocated
7234 @see port()
7235 @see socket()
7236 @see socket_family()
7237 @see socket_protocol()
7238 @see socket_type()
7239 @qualifier TLS
7240 *///=========================================================================
7241 const int socket_fd() noexcept { return __socket_fd; }; // -x- int socket_fd -x-
7242
7243 /*======================================================================*//**
7244 @brief
7245 Set underlying socket descriptor/handle (to one that is presumed to be open).
7246 @note
7247 This method is only available while an underlying socket has not been created
7248 or previously assigned, such as after an empty @ref rsocket instantiation.
7249 @throws randolf::rex::xEALREADY If this socket_fd() method was already used,
7250 or it was used after socket() initialized it, or if rsocket() had
7251 initialized with at least one parameter that resulted in the creation
7252 of an underlying socket
7253 @returns The same rsocket object so as to facilitate stacking
7254 @see socket()
7255 @see socket_family()
7256 @see socket_protocol()
7257 @see socket_type()
7258 @qualifier TLS
7259 *///=========================================================================
7260 rsocket* socket_fd(
7261 /// New socket descriptor/handle
7262 const int new_socket_fd) {
7263 if (__debug) debug("socket_fd(socket{0x" + randolf::rtools::to_hex(new_socket_fd) + "}"
7264 + ");");
7265 if (__socket_fd != 0) throw randolf::rex::xEALREADY("EALREADY");
7266 __socket_fd = new_socket_fd;
7267 __socket_addr.ss_family = getsockopt_int(SOL_SOCKET, SO_DOMAIN);
7268 __socket_type = getsockopt_int(SOL_SOCKET, SO_TYPE);
7269 __socket_protocol = getsockopt_int(SOL_SOCKET, SO_PROTOCOL);
7270 __socket_open = true;
7271 return this;
7272 }; // -x- rsocket* socket_fd -x-
7273
7274 /*======================================================================*//**
7275 @brief
7276 Get underlying socket protocol constant (SO_PROTOCOL).
7277 @returns socket protocol constant
7278 @see port()
7279 @see socket()
7280 @see socket_family()
7281 @see socket_fd()
7282 @see socket_type()
7283 @qualifier TLS
7284 *///=========================================================================
7285 const int socket_protocol() noexcept { return __socket_protocol; }; // -x- int socket_protocol -x-
7286
7287 /*======================================================================*//**
7288 @brief
7289 Get underlying socket type constant (SO_TYPE).
7290 @returns socket type constant
7291 @see port()
7292 @see socket()
7293 @see socket_family()
7294 @see socket_fd()
7295 @see socket_protocol()
7296 @qualifier TLS
7297 *///=========================================================================
7298 const int socket_type() noexcept { return __socket_type; }; // -x- int socket_type -x-
7299
7300 /*======================================================================*//**
7301 @brief
7302 Find out whether the underlying socket is at the out-of-band (OOB) mark.
7303
7304 @throws randolf::rex::xEBADF The underlying socket is not open
7305 @throws randolf::rex::xEINVAL The underlying socket file descriptor is not a
7306 type to which @ref sockatmark() can be applied
7307
7308 @returns TRUE = at OOB mark
7309 @returns FALSE = not at OOB mark
7310 @qualifier POSIX
7311 @qualifier TLS
7312 *///=========================================================================
7313 const bool sockatmark() {
7314 return __rc_check(::sockatmark(__socket_fd)) ? true : false;
7315 }; // -x- bool sockatmark -x-
7316
7317 /*======================================================================*//**
7318 @brief
7319 Find out what the read timeout is set to on the current socket.
7320
7321 Since getting the read timeout is such a common operation, this specialized
7322 method was created to ease software development efforts; internally we're
7323 just calling getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO).
7324
7325 @throws randolf::rex::xEBADF The underlying socket is not open
7326 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7327 part of the user address space
7328 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7329 valid for this socket's family (a.k.a., communication domain)
7330 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7331 is not supported
7332 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7333 doesn't refer to a socket
7334
7335 @returns @c timeval socket option structure wrapped in std::shared_ptr
7336 @qualifier TLS
7337 *///=========================================================================
7338 std::shared_ptr<timeval> timeout() {
7339 return getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO);
7340 }; // -x- std::shared_ptr<timeval> timeout -x-
7341
7342 /*======================================================================*//**
7343 @brief
7344 Set the recv timeout on the current socket.
7345
7346 Since setting the read timeout is such a common operation, this specialized
7347 method was created to ease software development efforts; internally we're
7348 just calling setsockopt(SOL_SOCKET, SO_RCVTIMEO, timeval).
7349 @attention
7350 Although a timeout of 100,000 microseconds (1/10 of a one second) may suffice
7351 in healthy and efficient networks, a more conservative setting of 1 second
7352 tends to minimally yield more reliable results. Many end-user applications
7353 use defaults of 3 to 5 seconds to cover a wider variety of unpredictable
7354 network connections (such as over shared wireless connections that are slow),
7355 and this setting should ultimately be configurable by users/administrators.
7356
7357 @note
7358 The default timeout for new sockets is normally 0 (no timeout).
7359
7360 @throws randolf::rex::xEBADF The underlying socket is not open
7361 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7362 part of the user address space
7363 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7364 valid for this socket's family (a.k.a., communication domain)
7365 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7366 is not supported
7367 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7368 doesn't refer to a socket
7369
7370 @returns The same rsocket object so as to facilitate stacking
7371 @qualifier TLS
7372 *///=========================================================================
7373 rsocket* timeout(
7374 /// timeval structure
7375 const struct timeval tv) {
7376 return setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
7377 }; // -x- rsocket* timeout -x-
7378
7379 /*======================================================================*//**
7380 @copydoc timeout(struct timeval)
7381 @qualifier TLS
7382 *///=========================================================================
7383 rsocket* timeout(
7384 /// Timeout in seconds
7385 const int tv_sec = 0,
7386 /// Timeout in microseconds
7387 const long tv_usec = 0) {
7388 struct timeval tv{tv_sec, tv_usec};
7389 return setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
7390 }; // -x- rsocket* timeout -x-
7391
7392 /*======================================================================*//**
7393 @brief
7394 Find out what the read timeout is set to when using the @ref recvline()
7395 method.
7396
7397 @returns @c long value (0 = no timeout)
7398 @see recvline
7399 @see timeout
7400 @see timeout_recvline(long)
7401 @qualifier TLS
7402 *///=========================================================================
7403 long timeout_recvline() {
7404 return __recvline_timeout;
7405 }; // -x- long timeout_recvline -x-
7406
7407 /*======================================================================*//**
7408 @brief
7409 Set the read timeout for the @ref recvline() method (the @ref recvline()
7410 method's @c timeout parameter can override this setting).
7411
7412 @note
7413 The default timeout for this recvline_timeout setting is 0 (no timeout).
7414
7415 @throws randolf::rex::xERANGE if the timeout parameter is below 0
7416
7417 @returns The same rsocket object so as to facilitate stacking
7418 @see recvline
7419 @see timeout
7420 @see timeout_recvline
7421 @qualifier TLS
7422 *///=========================================================================
7423 rsocket* timeout_recvline(
7424 /// timeval structure
7425 const long timeout) {
7426 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
7427 __recvline_timeout = timeout;
7428 return this;
7429 }; // -x- rsocket* timeout_recvline -x-
7430
7431 /*======================================================================*//**
7432 @brief
7433 Enable or disable encrypted communications (from the OpenSSL library).
7434
7435 @warning
7436 TLS cannot be enabled before a non-encrypted rsocket is open (an rsocket is
7437 typically opened with the @ref socket() method, the @ref connect() method, or
7438 one of the @ref accept() methods). If calling @c tls(true) on an rsocket
7439 that isn't open, an exception will be thrown.
7440
7441 If needed, a new TLS context will be instantiated and TLS will be initialized
7442 (if this hasn't already been done). TLS instantiation can be done first by
7443 calling the @ref tls_ctx() method (regardless of whether encryption is being
7444 enabled or disabled). If the default @ref TLS_FLAGS aren't sufficient for
7445 the needs of your application, then the @ref tls_ctx() method facilitates
7446 this regardless of wehther rsocket is open.
7447
7448 @note
7449 The reason a TLS context is instantiated and TLS is initialized even when the
7450 status is being set to FALSE is to facilitate TLS ingress from an unencrypted
7451 connection later in the session (see the @ref TLS_NO_INGRESS flag for more
7452 information).
7453
7454 @post
7455 If a client attempts to upgrade a TLS connection to TLS (e.g., by using a
7456 command such as @c STARTTLS, which is commonly transmitted in unencrypted
7457 form, like @c telnet-ssl does -- using `telnet-ssl -z ssl` prevents this
7458 condition), the following error that's difficult to track down may be
7459 triggered when calling any of the @c recv methods (I hope that including this
7460 information here in this documentation will be helpful):
7461 @verbatim
7462 SSL_ERROR_UNKNOWN: error:0A00010B:SSL routines::wrong version number
7463 @endverbatim
7464 This is most likely not a programming error, but rather a problem with how
7465 users may attempt to mis-use a connection based on a misunderstanding of the
7466 communications requirements (e.g., connecting unencrypted and attempting to
7467 upgrade to TLS over a connection that's expecting TLS encrypted data from the
7468 very beginning, without involving any ingress).
7469
7470 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7471 OpenSSL library doesn't document which errors may be returned)
7472
7473 @returns The same rsocket object so as to facilitate stacking
7474 @see is_tls
7475 @see tls_ctx
7476 @qualifier TLS
7477 *///=========================================================================
7478 rsocket* tls(
7479 /// TRUE = Enable encrypted communications@n FALSE = Disable encrypted communications
7480 const bool status = true,
7481 /// Configuration parameters
7482 const int flags = TLS_FLAGS::TLS_DEFAULT) { // | TLS_FLAGS::TLS_CLIENT
7483 if (__debug) debug("tls(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7484 + ", " + (status ? "true" : "false")
7485 + ");");
7486
7487 // --------------------------------------------------------------------------
7488 // Create default context (with "flags" passthrough), unless it was already
7489 // created (usually by one of the tls_ctx() methods).
7490 // --------------------------------------------------------------------------
7491 if (__tls_ctx == nullptr) tls_ctx((SSL_CTX*)nullptr, flags);
7492
7493 // --------------------------------------------------------------------------
7494 // Make sure tls_fd (ssl_fd) is allocated. If not, then it needs to be
7495 // allocated and configured.
7496 // --------------------------------------------------------------------------
7497 if (status == true && __tls_fd == nullptr) {
7498 __tls_fd = SSL_new(__tls_ctx);
7499 if (__debug) debug("SSL_set_fd(<tls_fd>, socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7500 + ");");
7501
7502 // --------------------------------------------------------------------------
7503 // Associate OpenSSL file descriptor with underlying socket file descriptor.
7504 // --------------------------------------------------------------------------
7505 __rc_check_tls(SSL_set_fd(__tls_fd, __socket_fd));
7506
7507 // --------------------------------------------------------------------------
7508 // Enable read-ahead so that SSL_peek will work properly.
7509 // --------------------------------------------------------------------------
7510 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
7511 SSL_set_read_ahead(__tls_fd, 1); // 0=disable / 1=enable
7512
7513// SSL_CTX_set_max_pipelines(__tls_ctx, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
7514// SSL_set_max_pipelines(__tls_fd, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
7515
7516// SSL_CTX_set_mode(__tls_ctx, SSL_MODE_AUTO_RETRY);
7517// SSL_set_mode(__tls_fd, SSL_MODE_AUTO_RETRY);
7518
7519 // --------------------------------------------------------------------------
7520 // We're probably not going to use BIO because we don't need it, and it adds
7521 // unnecessary overhead for what we're doing. Also, BIO doesn't provide an
7522 // alternative to the SSL_peek function, and also doesn't resolve the need
7523 // for the MSG_WAITALL flag that the ::recv() function supports.
7524 //
7525 // TODO: Remove this completely, unless is can solve the problem of reading
7526 // all incoming data (needed for readline, primarily)
7527 // --------------------------------------------------------------------------
7528 __tls_rbio = SSL_get_rbio(__tls_fd);
7529 __tls_wbio = SSL_get_wbio(__tls_fd);
7530
7531 } // -x- if !__tls_fd -x-
7532 __tls = status;
7533 return this;
7534 }; // -x- rsocket* tls -x-
7535
7536 /*======================================================================*//**
7537 @brief
7538 Return the current TLS context (multiple TLS contexts are supported, although
7539 typically needed to support SNI with inbound connections).
7540 @returns Pointer to OpenSSL's context (normally labelled @c SSL_CTX* in the
7541 documentation for OpenSSL), or nullptr if this context was never
7542 assigned to (or created by) this rsocket
7543 @see tls_ctx
7544 @qualifier TLS
7545 *///=========================================================================
7546 SSL_CTX* tls_ctx() noexcept {
7547 return __tls_ctx;
7548 }; // -x- SSL_CTX* tls_ctx -x-
7549
7550 /*======================================================================*//**
7551 @brief
7552 Copy the source rsocket's TLS context map and add it to this rsocket's
7553 collection; or, if the source doesn't have any TLS contexts and this rsocket
7554 doesn't have any TLS contexts in its collection, then initialize TLS and
7555 instantiate a new TLS context. In either scenario, the source rsocket will
7556 be treated as a template as all TLS flags duplicated to enable encrypted
7557 socket I/O for use in this rsocket().
7558
7559 @note
7560 At least one TLS context is needed to enable encrypted socket I/O for use in
7561 this rsocket().
7562
7563 @post
7564 Encrypted socket I/O is only possible after a TLS context has been
7565 initialized (this is not a global setting as it has per-rsocket specificity).
7566
7567 @note
7568 The only @ref TLS_FLAGS flag that doesn't get transferred is @ref TLS_SERVER
7569 when no flags are specified. Specifying any flag(s) will cause this method
7570 to ignore the source rsocket's TLS flags so as to defer to this override.
7571
7572 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7573 OpenSSL library doesn't document which errors may be returned)
7574
7575 @returns The same rsocket object so as to facilitate stacking
7576 @see tls_sni
7577 @qualifier TLS
7578 *///=========================================================================
7579 rsocket* tls_ctx(
7580 /// OpenSSL's TLS context to use (if not provided, a new context will be
7581 /// created automatically using OpenSSL's defaults)
7582 rsocket* rtemplate,
7583 /// Configuration parameters
7584 const int flags = TLS_FLAGS::TLS_DEFAULT) {
7585 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7586 + ", <rsocket>"
7587 + ", " + std::to_string(flags)
7588 + ");");
7589
7590 // --------------------------------------------------------------------------
7591 // TLS-related variables (OpenSSL).
7592 //
7593 // Note: The __tls_fd variable cannot be allocated until after __socket_fd
7594 // has been, hence the "post" note in the documentation.
7595 // --------------------------------------------------------------------------
7596 __tls = (bool)rtemplate->__tls; // Preserve TLS mode setting
7597 if (rtemplate->__tls_ctx != nullptr) { // If TLS context is defined, then do these important things:
7598 __tls_ctx = rtemplate->__tls_ctx; // 1. copy the pointer to SSL_CTX
7599 SSL_CTX_up_ref(__tls_ctx); // 2. increment SSL_CTX's internal reference count
7600 } // -x- if __tlx_ctx -x-
7601
7602 // --------------------------------------------------------------------------
7603 // Copy or override TLS flags.
7604 // --------------------------------------------------------------------------
7605 if (flags == TLS_FLAGS::TLS_DEFAULT) {
7606 __tls_exclusive = rtemplate->__tls_exclusive; // TLS policy
7607 __tls_egress = rtemplate->__tls_egress; // TLS policy
7608 __tls_ingress = rtemplate->__tls_ingress; // TLS policy
7609 } else { // Save flags
7610 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
7611 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
7612 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
7613 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
7614 } // -x- if flags -x-
7615
7616 return this;
7617 }; // -x- rsocket* tls_ctx -x-
7618
7619 /*======================================================================*//**
7620 @brief
7621 Initialize TLS and instantiate a TLS context, and add it to this rsocket's
7622 current collection of TLS contexts, and set it as the currently active TLS
7623 context (so that a certificate chain and private key may be added to it).
7624 @note
7625 At least one TLS context is needed to enable encrypted socket I/O for use in
7626 this rsocket().
7627 @post
7628 Encrypted socket I/O is only possible after a TLS context has been
7629 initialized (this is not a global setting as it has per-rsocket specificity).
7630 @note
7631 This is the default TLS context for this @c rsocket, which will also be used
7632 for non-SNI handshakes.
7633
7634 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7635 OpenSSL library doesn't document which errors may be returned)
7636
7637 @returns The same rsocket object so as to facilitate stacking
7638 @see tls_sni
7639 @qualifier TLS
7640 *///=========================================================================
7641 rsocket* tls_ctx(
7642 /// OpenSSL's TLS context to use (if not provided, a new context will be
7643 /// created using OpenSSL's defaults)
7644 SSL_CTX* ctx,
7645 /// Configuration parameters
7646 const int flags = TLS_FLAGS::TLS_DEFAULT) {
7647 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7648 + ", <ctx>"
7649 + ", " + std::to_string(flags)
7650 + ");");
7651
7652 // --------------------------------------------------------------------------
7653 // Ignore repeated calls to this method.
7654 // --------------------------------------------------------------------------
7655 if (__tls_ctx != nullptr) return this;
7656
7657 // --------------------------------------------------------------------------
7658 // Fire up OpenSSL's algorithms and pre-load its error strings.
7659 //
7660 // These two functions have been deprecated since OpenSSL v1.1.0, so we don't
7661 // need to call them. If someone needs them, then they can always call them
7662 // in their own code anyway.
7663 // --------------------------------------------------------------------------
7664 //OpenSSL_add_all_algorithms(); // Load and register cryptography algorithms
7665 //SSL_load_error_strings(); // Load all error messages into memory
7666
7667 // --------------------------------------------------------------------------
7668 // Save (ctx != nullptr) or create (ctx == nullptr) OpenSSL context.
7669 // --------------------------------------------------------------------------
7670 __tls_ctx = ctx == nullptr ? SSL_CTX_new(flags & TLS_FLAGS::TLS_SERVER ? TLS_server_method() : TLS_client_method()) // Create anew (default)
7671 : ctx; // Use OpenSSL context that was provided
7672 if (__tls_ctx == nullptr) randolf::rex::mk_exception("Cannot create TLS context", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
7673
7674 // --------------------------------------------------------------------------
7675 // Enable read-ahead so that SSL_peek will work properly.
7676 // --------------------------------------------------------------------------
7677 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
7678
7679 // --------------------------------------------------------------------------
7680 // Save flags.
7681 // --------------------------------------------------------------------------
7682 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
7683 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
7684 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
7685 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
7686
7687 return this;
7688 }; // -x- rsocket* tls_ctx -x-
7689
7690 /*======================================================================*//**
7691 @brief
7692 Check the private key it to ensure it's consistent with the corresponding TLS
7693 certificate chain.
7694
7695 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7696 OpenSSL library doesn't document which errors may be returned)
7697
7698 @returns The same rsocket object so as to facilitate stacking
7699 @see tls_ctx_use_privatekey_file
7700 @see tls_ctx_use_privatekey_pem
7701 @qualifier TLS
7702 *///=========================================================================
7703 rsocket* tls_ctx_check_privatekey() {
7704 if (__debug) debug("tls_ctx_check_privatekey();");
7705 if (!SSL_CTX_check_private_key(__tls_ctx)) randolf::rex::mk_exception("Cannot validate consistency between certificate chain and private key", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
7706 return this;
7707 }; // -x- rsocket* tls_ctx_check_privatekey -x-
7708
7709 /*======================================================================*//**
7710 @brief
7711 Load a TLS certificate chain and private key in PEM format from text files
7712 and use them in the TLS context.
7713
7714 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7715 OpenSSL library doesn't document which errors may be returned)
7716
7717 @returns The same rsocket object so as to facilitate stacking
7718 @see tls_ctx_use_certificate_chain_and_privatekey_pems
7719 @see tls_ctx_use_certificate_chain_file
7720 @see tls_ctx_use_certificate_chain_pem
7721 @see tls_ctx_use_privatekey_file
7722 @see tls_ctx_use_privatekey_pem
7723 @see tls_ctx_check_privatekey
7724 @qualifier TLS
7725 *///=========================================================================
7726 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
7727 /// Pointer to ASCIIZ path and filename to certificate chain file (@c nullptr
7728 /// will simply be ignored)
7729 const char* chain_file,
7730 /// Pointer to ASCIIZ path and filename to private key file (@c nullptr will
7731 /// simply be ignored)
7732 const char* key_file) {
7733 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
7734 + std::string(chain_file)
7735 + ", " + std::string( key_file)
7736 + ");");
7737 if (chain_file != nullptr) tls_ctx_use_certificate_chain_file(chain_file);
7738 if ( key_file != nullptr) tls_ctx_use_privatekey_file( key_file);
7739 return this;
7740 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
7741
7742 /*======================================================================*//**
7743 @brief
7744 Load a TLS certificate chain and private key in PEM format from text files
7745 and use them in the TLS context.
7746
7747 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7748 OpenSSL library doesn't document which errors may be returned)
7749
7750 @returns The same rsocket object so as to facilitate stacking
7751 @see tls_ctx_use_certificate_chain_and_privatekey_pems
7752 @see tls_ctx_use_certificate_chain_file
7753 @see tls_ctx_use_certificate_chain_pem
7754 @see tls_ctx_use_privatekey_file
7755 @see tls_ctx_use_privatekey_pem
7756 @see tls_ctx_check_privatekey
7757 @qualifier TLS
7758 *///=========================================================================
7759 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
7760 /// Pointer to ASCIIZ path and filename to certificate chain file (an empty
7761 /// string will simply be ignored)
7762 const std::string chain_file,
7763 /// Pointer to ASCIIZ path and filename to private key file (an empty string
7764 /// will simply be ignored)
7765 const std::string key_file) {
7766 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
7767 + chain_file
7768 + ", " + key_file
7769 + ");");
7770 if (!chain_file.empty()) tls_ctx_use_certificate_chain_file(chain_file);
7771 if ( !key_file.empty()) tls_ctx_use_privatekey_file( key_file);
7772 return this;
7773 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
7774
7775 /*======================================================================*//**
7776 @brief
7777 Load a TLS certificate chain and a TLS private key in PEM format from memory
7778 and use them in the TLS context.
7779
7780 Although this functionality doesn't exist in OpenSSL (at the time of writing
7781 this method), it's provided here in a manner that has exactly the same effect
7782 as the @ref tls_ctx_use_certificate_chain_and_privatekey_files() methods, but
7783 without needing the PEM-formatted certificate chain stored in files
7784 beforehand.
7785
7786 @note
7787 The @c cert_pem_data and key_pem_data parameters are pointers to the memory
7788 locations that holds the PEM formatted certificate chain data and private key
7789 data, respectively. If the corresponding lengths of each of these data aren't
7790 specified or are set to zero (default), then they will be treated as multiline
7791 ASCIIZ strings.
7792
7793 Behind the scenes, we're just writing the cert_pem_data and key_pem_data
7794 memory to temporary files with severely-limited permissions (), then
7795 optionally overwriting those temporary files with random data prior to
7796 deleting them (this is the default, since better security practices should be
7797 the default, but on a secured system it may not be necessary and so this
7798 option can also be disabled to save CPU cycles and reduce overall disk-write
7799 I/O operations).
7800
7801 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7802 OpenSSL library doesn't document which errors may be returned)
7803
7804 @returns The same rsocket object so as to facilitate stacking
7805 @see tls_ctx_use_certificate_chain_and_privatekey_files
7806 @see tls_ctx_use_certificate_chain_file
7807 @see tls_ctx_use_certificate_chain_pem
7808 @see tls_ctx_use_privatekey_file
7809 @see tls_ctx_use_privatekey_pem
7810 @see tls_ctx_check_privatekey
7811 @qualifier TLS
7812 *///=========================================================================
7813 rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems(
7814 /// Pointer to certificate chain data in PEM format
7815 const char* cert_pem_data,
7816 /// Pointer to private key data in PEM format
7817 const char* key_pem_data,
7818 /// Length of cert_pem_data (in bytes), or 0 to auto-detect length if cert_pem_data is an ASCIIZ string
7819 size_t cert_len = 0,
7820 /// Length of key_pem_data (in bytes), or 0 to auto-detect length if key_pem_data is an ASCIIZ string
7821 size_t key_len = 0,
7822 /// Whether to overwrite the temporary files with random data before deleting them
7823 const bool random_fill = true) {
7824 tls_ctx_use_certificate_chain_pem(cert_pem_data, cert_len, random_fill);
7825 tls_ctx_use_privatekey_pem( key_pem_data, key_len, random_fill);
7826 return this;
7827 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems -x-
7828
7829 /*======================================================================*//**
7830 @brief
7831 Load a TLS certificate chain in PEM format from a text file and use it in the
7832 TLS context.
7833
7834 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7835 OpenSSL library doesn't document which errors may be returned)
7836
7837 @returns The same rsocket object so as to facilitate stacking
7838 @see tls_ctx_use_certificate_chain_file
7839 @see tls_ctx_use_certificate_chain_pem
7840 @see tls_ctx_check_privatekey
7841 @qualifier TLS
7842 *///=========================================================================
7843 rsocket* tls_ctx_use_certificate_chain_file(
7844 /// Pointer to ASCIIZ path and filename to certificate chain file
7845 const char* file) {
7846 if (__debug) debug("tls_ctx_use_certificate_chain_file("
7847 + std::string(file)
7848 + ");");
7849 if (SSL_CTX_use_certificate_chain_file(__tls_ctx, file) != 1) randolf::rex::mk_exception(std::string("Cannot use certificate chain file ") + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
7850 return this;
7851 }; // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
7852
7853 /*======================================================================*//**
7854 @brief
7855 Load a TLS certificate chain in PEM format from a text file and use it in the
7856 TLS context.
7857
7858 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7859 OpenSSL library doesn't document which errors may be returned)
7860
7861 @returns The same rsocket object so as to facilitate stacking
7862 @see tls_ctx_use_certificate_chain_file
7863 @see tls_ctx_use_certificate_chain_pem
7864 @see tls_ctx_check_privatekey
7865 @qualifier TLS
7866 *///=========================================================================
7867 rsocket* tls_ctx_use_certificate_chain_file(
7868 /// Path and filename to certificate chain file
7869 const std::string file) {
7870 if (__debug) debug("tls_ctx_use_certificate_chain_file("
7871 + file
7872 + ");");
7873 if (SSL_CTX_use_certificate_chain_file(__tls_ctx, file.c_str()) != 1) randolf::rex::mk_exception("Cannot use certificate chain file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
7874 return this;
7875 }; // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
7876
7877 /*======================================================================*//**
7878 @brief
7879 Load a TLS certificate chain in PEM format from memory and use it in the TLS
7880 context.
7881
7882 Although this functionality doesn't exist in OpenSSL (at the time of writing
7883 this method), it's provided here in a manner that has exactly the same effect
7884 as the @ref tls_ctx_use_certificate_chain_file() methods, but without needing
7885 the PEM-formatted certificate chain stored in a file beforehand.
7886
7887 @note
7888 The @c pem_data parameter is a pointer to the memory location that holds
7889 the PEM formatted certificate chain data. If the length of this data isn't
7890 specified or is set to zero (default), then it will be treated as a multiline
7891 ASCIIZ string.
7892
7893 Behind the scenes, we're just writing the pem_data memory to a temporary
7894 file with severely-limited permissions (), then optionally overwriting that
7895 temporary file with random data prior to deleting it (this is the default,
7896 since better security practices should be the default, but on a secured
7897 system it may not be necessary and so this option can also be disabled to
7898 save CPU cycles and reduce overall disk-write I/O operations).
7899
7900 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7901 OpenSSL library doesn't document which errors may be returned)
7902
7903 @returns The same rsocket object so as to facilitate stacking
7904 @see tls_ctx_use_certificate_chain_file
7905 @see tls_ctx_check_privatekey
7906 @qualifier TLS
7907 *///=========================================================================
7908 rsocket* tls_ctx_use_certificate_chain_pem(
7909 /// Pointer to certificate chain data in PEM format
7910 const char* pem_data,
7911 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
7912 size_t len = 0,
7913 /// Whether to overwrite the temporary file with random data before deleting it
7914 const bool random_fill = true) {
7915 if (__debug) debug("tls_ctx_use_certificate_chain_pem(<buf>, " + std::to_string(len)
7916 + ", " + (random_fill ? "true" : "false")
7917 + ");");
7918
7919 // --------------------------------------------------------------------------
7920 // Measure size of certificate chain if an ASCIIZ string was indicated.
7921 // --------------------------------------------------------------------------
7922 if (len == 0) len = std::strlen(pem_data);
7923
7924 // --------------------------------------------------------------------------
7925 // Generate filename for temporary use.
7926 // --------------------------------------------------------------------------
7927 std::string file = std::filesystem::temp_directory_path();
7928 file.append("/rsocket.")
7929 .append(std::to_string(::getpid()))
7930 .append(".")
7931 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
7932
7933 // --------------------------------------------------------------------------
7934 // Open temporary file.
7935 // --------------------------------------------------------------------------
7936 FILE* fp = fopen(file.c_str(), "w+");
7937 if (fp == nullptr) randolf::rex::mk_exception("Cannot open temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
7938 { int attr;
7939 ioctl(fileno(fp), FS_IOC_GETFLAGS, &attr);
7940 attr |= FS_NOATIME_FL // Don't update access time attribute
7941 | FS_NODUMP_FL // Don't include in filesystem backup dumps
7942 | FS_SECRM_FL; // Mark file for secure deletion (where supported)
7943 ioctl(fileno(fp), FS_IOC_SETFLAGS, &attr);
7944 }
7945 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
7946 if (fputs(pem_data, fp) == EOF) {
7947 fclose(fp);
7948 randolf::rex::mk_exception("Cannot write to temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
7949 } // -x- if !fputs -x-
7950 fflush(fp);
7951
7952 // --------------------------------------------------------------------------
7953 // Attempt to load certificate chain file, but save the error code for later
7954 // because we need to clean up the temporary file before possibly throwing an
7955 // exception.
7956 // --------------------------------------------------------------------------
7957 int rc = SSL_CTX_use_certificate_chain_file(__tls_ctx, file.c_str());
7958
7959 // --------------------------------------------------------------------------
7960 // Overwrite the contenst of the temporary file before deleting it so as to
7961 // sabotage a simple attempt to undelete the file and access the certificate.
7962 //
7963 // We're also re-using the "len" local variable because it's not needed once
7964 // we get the loop started, and it's more optimal to not allocate yet another
7965 // local variable while "len" goes to waste. :D
7966 // --------------------------------------------------------------------------
7967 if (random_fill) { // This option is configurable
7968 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
7969 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
7970 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
7971 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
7972 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
7973 } // -x- for i -x-
7974 } // -x- if randfill -x-
7975 fchmod(fileno(fp), 0); // Remove all permissions
7976 fclose(fp); // Close file handle
7977 unlink(file.c_str()); // Delete temporary file
7978
7979 // --------------------------------------------------------------------------
7980 // Error check ... was delayed here until after temporary file cleanup.
7981 // --------------------------------------------------------------------------
7982 if (rc != 1) randolf::rex::mk_exception("Cannot use certificate chain file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
7983
7984 return this;
7985 }; // -x- rsocket* tls_ctx_use_certificate_chain_pem -x-
7986
7987 /*======================================================================*//**
7988 @brief
7989 Load a TLS private key in PEM format from a text file and use it in the TLS
7990 context.
7991
7992 @throws randolf::rex::xALL Catch this exception for now (at this time, the
7993 OpenSSL library doesn't document which errors may be returned)
7994
7995 @returns The same rsocket object so as to facilitate stacking
7996 @see tls_ctx_use_privatekey_file
7997 @see tls_ctx_use_privatekey_pem
7998 @see tls_ctx_check_privatekey
7999 @qualifier TLS
8000 *///=========================================================================
8001 rsocket* tls_ctx_use_privatekey_file(
8002 /// Pointer to ASCIIZ path-and-filename of private key file
8003 const char* file) {
8004 if (__debug) debug("tls_ctx_use_privatekey_file("
8005 + std::string(file)
8006 + ");");
8007 if (SSL_CTX_use_PrivateKey_file(__tls_ctx, file, SSL_FILETYPE_PEM) != 1) randolf::rex::mk_exception(std::string("Cannot use private key file ") + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8008
8009 return this;
8010 }; // -x- rsocket* tls_ctx_use_privatekey_file -x-
8011
8012 /*======================================================================*//**
8013 @brief
8014 Load a TLS private key in PEM format from a text file and use it in the TLS
8015 context.
8016
8017 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8018 OpenSSL library doesn't document which errors may be returned)
8019
8020 @returns The same rsocket object so as to facilitate stacking
8021 @see tls_ctx_use_privatekey_file
8022 @see tls_ctx_use_privatekey_pem
8023 @see tls_ctx_check_privatekey
8024 @qualifier TLS
8025 *///=========================================================================
8026 rsocket* tls_ctx_use_privatekey_file(
8027 /// Path and filename to private key file
8028 const std::string file) {
8029 if (__debug) debug("tls_ctx_use_privatekey_file("
8030 + file
8031 + ");");
8032 if (SSL_CTX_use_PrivateKey_file(__tls_ctx, file.c_str(), SSL_FILETYPE_PEM) != 1) randolf::rex::mk_exception("Cannot use private key file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8033
8034 return this;
8035 }; // -x- rsocket* tls_ctx_use_privatekey_file -x-
8036
8037 /*======================================================================*//**
8038 @brief
8039 Load a TLS private key in PEM format from memory and use it in the TLS
8040 context.
8041
8042 Although this functionality doesn't exist in OpenSSL (at the time of writing
8043 this method), it's provided here in a manner that has exactly the same effect
8044 as the @ref tls_ctx_use_privatekey_file() methods, but without needing the
8045 PEM-formatted private key stored in a file beforehand.
8046
8047 @note
8048 The @c pem_data parameter is a pointer to the memory location that holds the
8049 PEM formatted private key data. If the length of this data isn't specified
8050 or is set to zero (default), then it will be treated as a multiline ASCIIZ
8051 string.
8052
8053 Behind the scenes, we're just writing the pem_data memory to a temporary
8054 file (with severely-limited permissions), then optionally overwriting that
8055 temporary file with random data prior to deleting it (this is the default,
8056 since better security practices should be the default, but on a secured
8057 system it may not be necessary and so this option can also be disabled to
8058 save CPU cycles and reduce overall disk-write I/O operations).
8059
8060 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8061 OpenSSL library doesn't document which errors may be returned)
8062
8063 @returns The same rsocket object so as to facilitate stacking
8064 @see tls_ctx_use_privatekey_file
8065 @see tls_ctx_check_privatekey
8066 @qualifier TLS
8067 *///=========================================================================
8068 rsocket* tls_ctx_use_privatekey_pem(
8069 /// Pointer to private key data in PEM format
8070 const char* pem_data,
8071 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
8072 size_t len = 0,
8073 /// Whether to overwrite the temporary file with random data before deleting it
8074 const bool random_fill = true) {
8075 if (__debug) debug("tls_ctx_use_privatekey_pem(<buf>, " + std::to_string(len)
8076 + ", " + (random_fill ? "true" : "false")
8077 + ");");
8078
8079 // --------------------------------------------------------------------------
8080 // Measure size of private key if an ASCIIZ string was indicated.
8081 // --------------------------------------------------------------------------
8082 if (len == 0) len = std::strlen(pem_data);
8083
8084 // --------------------------------------------------------------------------
8085 // Generate filename for temporary use.
8086 // --------------------------------------------------------------------------
8087 std::string file = std::filesystem::temp_directory_path();
8088 file.append("/rsocket.")
8089 .append(std::to_string(::getpid()))
8090 .append(".")
8091 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
8092
8093 // --------------------------------------------------------------------------
8094 // Open temporary file.
8095 // --------------------------------------------------------------------------
8096 FILE* fp = fopen(file.c_str(), "w+");
8097 if (fp == nullptr) randolf::rex::mk_exception("Cannot cannot open temporary file (to use private key) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8098 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
8099 if (fputs(pem_data, fp) == EOF) {
8100 fclose(fp);
8101 randolf::rex::mk_exception("Cannot cannot write to temporary file (to use private key) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8102 } // -x- if !fputs -x-
8103 fflush(fp);
8104
8105 // --------------------------------------------------------------------------
8106 // Attempt to load private key file, but save the error code for later
8107 // because we need to clean up the temporary file before possibly throwing an
8108 // exception.
8109 // --------------------------------------------------------------------------
8110 int rc = SSL_CTX_use_PrivateKey_file(__tls_ctx, file.c_str(), SSL_FILETYPE_PEM);
8111
8112 // --------------------------------------------------------------------------
8113 // Overwrite the contenst of the temporary file before deleting it so as to
8114 // sabotage a simple attempt to undelete the file and access the certificate.
8115 //
8116 // We're also re-using the "len" local variable because it's not needed once
8117 // we get the loop started, and it's more optimal to not allocate yet another
8118 // local variable while "len" goes to waste. :D
8119 // --------------------------------------------------------------------------
8120 if (random_fill) { // This option is configurable
8121 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
8122 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
8123 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
8124 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
8125 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
8126 } // -x- for i -x-
8127 } // -x- if randfill -x-
8128 fchmod(fileno(fp), 0); // Remove all permissions
8129 fclose(fp); // Close file handle
8130 unlink(file.c_str()); // Delete temporary file
8131
8132 // --------------------------------------------------------------------------
8133 // Error check ... was delayed here until after temporary file cleanup.
8134 // --------------------------------------------------------------------------
8135 if (rc != 1) randolf::rex::mk_exception("Cannot use private key file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8136
8137 return this;
8138 }; // -x- rsocket* tls_ctx_use_privatekey_pem -x-
8139
8140 /*======================================================================*//**
8141 @brief
8142 Initiate the TLS handshake with the endpoint (which is presumed to be a
8143 server).
8144 This method makes it easier to support application-level commands such as @c
8145 STARTTLS (which are implemented in protocols like SMTP, POP3, and IMAP4).
8146
8147 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8148 OpenSSL library doesn't document which errors may be returned)
8149
8150 @returns The same rsocket object so as to facilitate stacking
8151 @see connect()
8152 @see connect(std::string, int)
8153 @see TLS_NO_INGRESS
8154 @qualifier TLS
8155 *///=========================================================================
8156 rsocket* tls_do_handshake() {
8157 if (__debug) debug("tls_handshake(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8158 + ");");
8159 __rc_check_tls(SSL_do_handshake(__tls_fd));
8160 return this;
8161 }; // -x- rsocket* tls_do_handshake -x-
8162
8163 /*======================================================================*//**
8164 @brief
8165 Get OpenSSL's TLS structure.
8166 @returns TLS structure
8167 @returns nullptr = TLS structure not yet allocated
8168 @qualifier TLS
8169 *///=========================================================================
8170 const SSL* tls_fd() noexcept {
8171 return __tls_fd;
8172 }; // -x- int tls_fd -x-
8173
8174 /*======================================================================*//**
8175 @brief
8176 Return the current @ref rsocket_sni object that this @c rsocket will use when
8177 accepting incoming encrypted connections.
8178 @returns Pointer to @c rsocket_sni object
8179 @returns nullptr = SNI is not assigned
8180 @see name_sni
8181 @see tls_sni(rsocket_sni*)
8182 @qualifier TLS
8183 *///=========================================================================
8184 rsocket_sni* tls_sni() noexcept {
8185 return __tls_sni;
8186 }; // -x- rsocket_sni* tls_sni -x-
8187
8188 /*======================================================================*//**
8189 @brief
8190 Set the current @ref rsocket_sni object that this @c rsocket will use when
8191 accepting incoming encrypted connections.
8192
8193 Use the @ref name() method to find out which server name was supplied by the
8194 endpoint that triggered the SNI callback, regardless of whether it matches
8195 any of the TLS certificates used with this rsocket object or the rsocket_sni
8196 object that's associated with this rsocket object. If an SNI callback wasn't
8197 triggered, or if the endpoint didn't provide a server name, then it will
8198 remain unaffected (and the default {empty string} will remain unchanged).
8199 @returns The same rsocket object so as to facilitate stacking
8200 @see name_sni
8201 @see tls_sni
8202 @see is_tls_sni_match
8203 @qualifier TLS
8204 *///=========================================================================
8205 rsocket* tls_sni(
8206 /// Pointer to the @ref rsocket_sni object to use, or specify @c nullptr to
8207 /// remove SNI support from this rsocket object
8208 rsocket_sni* sni) noexcept {
8209
8210 // --------------------------------------------------------------------------
8211 // Remove SNI support.
8212 // --------------------------------------------------------------------------
8213 if (sni == nullptr) {
8214 if (__tls_sni != nullptr) SSL_CTX_set_client_hello_cb(__tls_ctx, nullptr, this);
8215
8216 // --------------------------------------------------------------------------
8217 // Add or set SNI support.
8218 // --------------------------------------------------------------------------
8219 } else {
8220 SSL_CTX_set_client_hello_cb(__tls_ctx, tls_sni_callback, this); // Configure SNI callbacks for TLS.
8221 //SSL_CTX_set_tlsext_servername_callback(__tls_ctx, tls_sni_callback); // Don't use this anymore; it's outdated
8222 //SSL_CTX_set_tlsext_servername_arg(__tls_ctx, this); // Don't use this anymore; it's outdated
8223
8224 } // -x- if !sni -x-
8225
8226 // --------------------------------------------------------------------------
8227 // Update internal pointer to the SNI map.
8228 // --------------------------------------------------------------------------
8229 __tls_sni = sni;
8230
8231 return this;
8232 }; // -x- rsocket_sni* tls_sni -x-
8233
8234private:
8235 /*======================================================================*//**
8236 @brief
8237 Get OpenSSL's TLS structure.
8238 @returns TLS structure
8239 @returns nullptr = TLS structure not yet allocated
8240 @qualifier TLS
8241 *///=========================================================================
8242 int static tls_sni_callback(
8243 /// OpenSSL's socket descriptor/handle
8244 SSL* tls_fd,
8245 /// Where to store the @c alert value
8246 int* al,
8247 /// Context-specific argument
8248 void* arg) noexcept {
8249
8250 // --------------------------------------------------------------------------
8251 // Internal variables.
8252 // --------------------------------------------------------------------------
8253 rsocket* r = (rsocket*)arg; // We may be updating the TLS context (this is normally the new client's rsocket object)
8254 const unsigned char* out = nullptr;
8255 size_t out_size = 0;
8256
8257 // --------------------------------------------------------------------------
8258 // Obtain a pointer to newly-allocated ClientHello fields data. If *out is
8259 // nullptr, it means that no TLSEXT_TYPE_server_name was received from the
8260 // client, and so the default TLS context will suffice. If out_size is less
8261 // than or equal to 2, then it also won't have what we need.
8262 // --------------------------------------------------------------------------
8263 if (!SSL_client_hello_get0_ext(tls_fd, TLSEXT_TYPE_server_name, &out, &out_size)
8264 || out == nullptr || out_size <= 2) return SSL_CLIENT_HELLO_SUCCESS; // 1
8265
8266 // --------------------------------------------------------------------------
8267 // Scan for TLSEXT_NAMETYPE_host_name, but if we can't find it then we'll
8268 // just leave the default TLS context as is.
8269 // --------------------------------------------------------------------------
8270 unsigned char* p = (unsigned char*)out;
8271 size_t len = (*(p++) << 8);
8272 len += *(p++);
8273 if (len + 2 != out_size) goto finish;
8274
8275 // --------------------------------------------------------------------------
8276 // We're taking a shortcut by examining only the first element in the list,
8277 // but in the future we need to make this more robust in case other types of
8278 // elements precede what we're looking for.
8279 //
8280 // Unfortunately, there's no documentation that properly-explains the format
8281 // of the list, so some deeper research into OpenSSL's source code will be
8282 // needed (a cursory look so far has not yielded the necessary insight).
8283 //
8284 // TODO: Turn this into a loop that supports future clients that provide
8285 // multiple SNI server names in their requests. (Although this isn't
8286 // occuring at present with common end-user tools such as web browsers
8287 // and eMail software, it may happen in the future as client/server
8288 // software becomes more savvy.)
8289 // --------------------------------------------------------------------------
8290 if (out_size == 0 || *p++ != TLSEXT_NAMETYPE_host_name) goto finish;
8291
8292 // --------------------------------------------------------------------------
8293 // Avoid buffer overrun caused by corrupt or prematurely-truncated data.
8294 // --------------------------------------------------------------------------
8295 if (--out_size <= 2) goto finish;
8296
8297 // --------------------------------------------------------------------------
8298 // Extract and use the hostname (SNI server name) that was supplied by the
8299 // endpoint so that the correct TLS certificate can be selected and assigned.
8300 // --------------------------------------------------------------------------
8301 len = (*(p++) << 8);
8302 len += *(p++);
8303 if (!(len + 2 > out_size)) { // Only process SNI server name string if it's at least 2 bytes long
8304 // Debug: std::cout << "Server name: " << (const char*)p << std::endl; // Debug
8305
8306 // --------------------------------------------------------------------------
8307 // Obtain the correct TLS context (wildcards supported) that is associated
8308 // with the hostname (SNI server name) that was supplied by the endpoint.
8309 // --------------------------------------------------------------------------
8310 const char* sni_name = (const char*)p;
8311 SSL_CTX* new_ctx = r->__tls_sni->get_ctx(sni_name, true, r->tls_ctx());
8312
8313 // --------------------------------------------------------------------------
8314 // Change TLS context so that encryption with the endpoint uses the correct
8315 // TLS certificate (otherwise the endpoint will indicate security risk errors
8316 // to end users, log files, etc., and may {should} reject the connection).
8317 // --------------------------------------------------------------------------
8318//std::cout << "SNI setup... " << sni_name << std::endl;
8319 SSL_set_SSL_CTX(tls_fd, new_ctx);
8320//std::cout << "---1--- " << r->__tls_new_endpoint << std::endl;
8321 r->__tls_new_endpoint->tls_ctx(new_ctx);
8322//std::cout << "---2---" << std::endl;
8323 r->__tls_new_endpoint->__name_sni.assign(sni_name);
8324//std::cout << "SNI name() = " << r->__tls_new_endpoint->name() << std::endl;
8325 r->__tls_new_endpoint->__tls_sni_match = true;
8326
8327 } // -x- if len+2 -x-
8328
8329 finish:
8330 // --------------------------------------------------------------------------
8331 // Free the ClientHello fields data, then return SSL_CLIENT_HELLO_SUCCESS.
8332 // Even if we encountered a problem with the ClientHello fields data, we
8333 // still return SSL_CLIENT_HELLO_SUCCESS so that the TLS context will be
8334 // accepted as valid.
8335 //
8336 // If we return SSL_CLIENT_HELLO_ERROR the connection will fail unnecessarily
8337 // for valid TLS certificates. If the ClientHello fields data is malformed
8338 // to a degree that doesn't satisfy OpenSSL, then OpenSSL will reject it, so
8339 // there's really no point in duplicating what OpenSSL already does properly,
8340 // which OpenSSL will pass through the standard error channels with normal
8341 // error details-and-diagnostics anyway.
8342 // --------------------------------------------------------------------------
8343 //OPENSSL_free((char*)out); // OpenSSL documentation says to do this, but it fails and crashes the program
8344
8345 return SSL_CLIENT_HELLO_SUCCESS; // 1
8346 }; // -x- int tls_sni_callback -x-
8347
8348public:
8349 /*======================================================================*//**
8350 @brief
8351 Convert a 48-bit integer to a machine address in the form of @c
8352 xx:xx:xx:xx:xx:xx where every instance of @c xx is a hexadecimal
8353 representation of each respective 8-bit byte portion.
8354
8355 This method is needed because we don't want to bring in the heavy fmt::format
8356 class as a dependency.
8357 @returns Mac address as 17-character in the typical format expected by system
8358 administrators
8359 @qualifier TLS
8360 *///=========================================================================
8361 static std::string to_mac(
8362 /// Pointer to 48-bit integer
8363 const void* addr) noexcept {
8364 std::string h;
8365 h.resize(18); // 48-bit mac address needs 6 hexadecimal pairs of nybbles delimited by colons plus a NULL terminator
8366 h.resize(snprintf(h.data(),
8367 h.size(),
8368 "%02x:%02x:%02x:%02x:%02x:%02x",
8369 ((u_char*)addr)[0],
8370 ((u_char*)addr)[1],
8371 ((u_char*)addr)[2],
8372 ((u_char*)addr)[3],
8373 ((u_char*)addr)[4],
8374 ((u_char*)addr)[5])); // Convert, and truncate NULL terminator
8375 return h;
8376 }; // -x- std::string to_mac -x-
8377
8378 /*======================================================================*//**
8379 @brief
8380 Convert a 48-bit integer to a node address in the form of @c xxxx:xxxx:xxxx
8381 where every instance of @c xxxx is a hexadecimal representation of each
8382 respective 16-bit word portion.
8383
8384 This method is needed because we don't want to bring in the heavy fmt::format
8385 class as a dependency.
8386 @returns Node address as 14-character in the typical format expected by
8387 network administrators
8388 @qualifier TLS
8389 *///=========================================================================
8390 static std::string to_node(
8391 /// Pointer to 48-bit integer
8392 const void* addr) noexcept {
8393 std::string h;
8394 h.resize(15); // 48-bit node address needs 3 hexadecimal sets of words delimited by colons plus a NULL terminator
8395 h.resize(snprintf(h.data(),
8396 h.size(),
8397 "%04x:%04x:%04x",
8398 ((u_int16_t*)addr)[0],
8399 ((u_int16_t*)addr)[1],
8400 ((u_int16_t*)addr)[2])); // Convert, and truncate NULL terminator
8401 return h;
8402 }; // -x- std::string to_node -x-
8403
8404 /*======================================================================*//**
8405 @brief
8406 Send a formatted string to the @ref rsocket endpoint.
8407
8408 The @c format is described in the documentation for the POSIX or Standard C
8409 Library @c printf() function.
8410 @throws randolf::rex::xEBADF The underlying socket is not open
8411 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
8412 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
8413 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
8414 @throws randolf::rex::xENOMEM Insufficient memory
8415 @returns The same rsocket object so as to facilitate stacking
8416 @see eol_fix_printf
8417 @see is_eol_fix_printf
8418 @see net_io
8419 @see printf
8420 @see printfline
8421 @see vprintfline
8422 @qualifier POSIX
8423 @qualifier TLS
8424 *///=========================================================================
8425 rsocket* vprintf(
8426 /// Format string to use
8427 const char* format,
8428 /// Variadic arguments in @c va_list format
8429 va_list args) {
8430 char* buf;
8431 int rc = ::vasprintf(&buf, format, args);
8432 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
8433 if (__eol_fix_printf && !__eol.empty()) {
8434 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
8435 ::free(buf);
8436 __send(str.c_str(), str.length());
8437 } else {
8438 try {
8439 __send(buf, rc);
8440 } catch (std::exception& e) { // Free buf then re-throw the exception
8441 ::free(buf); // Prevent memory leak when an exception is thrown
8442 throw;
8443 }
8444 } // -x- if __eol_fix_printf -x-
8445 return this;
8446 }; // -x- rsocket* vprintf -x-
8447
8448 /*======================================================================*//**
8449 @brief
8450 Send a formatted string to the @ref rsocket endpoint, and append an EoL
8451 sequence.
8452
8453 The @c format is described in the documentation for the POSIX or Standard C
8454 Library @c printf() function.
8455 @throws randolf::rex::xEBADF The underlying socket is not open
8456 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
8457 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
8458 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
8459 @throws randolf::rex::xENOMEM Insufficient memory
8460 @returns The same rsocket object so as to facilitate stacking
8461 @see eol
8462 @see eol_fix_printf
8463 @see is_eol_fix_printf
8464 @see net_io
8465 @see printf
8466 @see printfline
8467 @see sendline
8468 @see vprintf
8469 @qualifier TLS
8470 *///=========================================================================
8471 rsocket* vprintfline(
8472 /// Format string to use
8473 const char* format,
8474 /// Variadic arguments in @c va_list format
8475 va_list args) {
8476 char* buf;
8477 int rc = ::vasprintf(&buf, format, args);
8478 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
8479 if (__eol_fix_printf && !__eol.empty()) {
8480 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
8481 .append(__eol);
8482 ::free(buf);
8483 __send(str.c_str(), str.length());
8484 } else {
8485 try {
8486 __sendline(buf, rc);
8487 } catch (std::exception& e) { // Free buf then re-throw the exception
8488 ::free(buf); // Prevent memory leak when an exception is thrown
8489 throw;
8490 }
8491 } // -x- if __eol_fix_printf -x-
8492 return this;
8493 }; // -x- rsocket* vprintfline -x-
8494
8495 private:
8496 /*======================================================================*//**
8497 Track unencrypted bytes received. When the number of bytes is negative or
8498 zero, it isn't recorded.
8499 This is an internal function.
8500 @returns same value provided in @ref n
8501 @qualifier TLS
8502 *///=========================================================================
8503 int __track_bytes_rx(
8504 /// Number of bytes transferred
8505 const int n) {
8506 if (n > 0) __bytes_rx.fetch_add(n, std::memory_order_relaxed);
8507 return n;
8508 }; // -x- __track_bytes_rx -x-
8509
8510 /*======================================================================*//**
8511 Track unencrypted bytes transmitted. When the number of bytes is negative or
8512 zero, it isn't recorded.
8513 This is an internal function.
8514 @returns same value provided in @ref n
8515 @qualifier TLS
8516 *///=========================================================================
8517 int __track_bytes_tx(
8518 /// Number of bytes transferred
8519 const int n) {
8520 if (n > 0) __bytes_tx.fetch_add(n, std::memory_order_relaxed);
8521 return n;
8522 }; // -x- __track_bytes_tx -x-
8523
8524 /*======================================================================*//**
8525 Track encrypted bytes received. When the number of bytes is negative or
8526 zero, it isn't recorded.
8527 This is an internal function.
8528 @returns same value provided in @ref n
8529 @qualifier TLS
8530 *///=========================================================================
8531 int __track_crypt_rx(
8532 /// Number of bytes transferred
8533 const int n) {
8534 if (n > 0) __crypt_rx.fetch_add(n, std::memory_order_relaxed);
8535 return n;
8536 }; // -x- __track_crypt_rx -x-
8537
8538 /*======================================================================*//**
8539 Track encrypted bytes transmitted. When the number of bytes is negative or
8540 zero, it isn't recorded.
8541 This is an internal function.
8542 @returns same value provided in @ref n
8543 @qualifier TLS
8544 *///=========================================================================
8545 int __track_crypt_tx(
8546 /// Number of bytes transferred
8547 const int n) {
8548 if (n > 0) __crypt_tx.fetch_add(n, std::memory_order_relaxed);
8549 return n;
8550 }; // -x- __track_crypt_tx -x-
8551
8552 }; // -x- class rsocket -x-
8553
8554}; // -x- namespace randolf -x-
8555
8556// Save this for a future sendlines() methods.
8557// const void* msg_ptr = msg.c_str(); // Prevent repeated calls to c_str() method
8558// const int len = msg.length(); // Prevent repeated calls to length() method