randolf.ca  1.00
Randolf Richardson's C++ classes
Loading...
Searching...
No Matches
rsocket
1#pragma once
2
3#include <randolf/rex>
4#include <randolf/rline>
5#include <randolf/rring>
6#include <randolf/rring_bam>
7#include <randolf/rsocket_io>
8#include <randolf/rsocket_sni>
9#include <randolf/rtools>
10#include <randolf/sockaddr_dl.h>
11
12#include <atomic>
13#include <bit> // std::endian
14#include <cstdarg> // std::va_list
15#include <cstring> // std::strlen
16#include <ctime> // std::strftime
17#include <exception> // std::exception
18#include <filesystem> // std::filesystem::temp_directory_path
19#include <initializer_list>
20#include <iostream> // std::put_date
21#include <map> // std::map
22#include <memory> // std::shared_ptr
23#include <mutex> // std::mutex
24#include <regex>
25#include <set>
26//#include <sstream> // std::ostringstream
27#include <unordered_map> // std::unordered_map
28#include <vector>
29
30#include <ifaddrs.h>
31#include <netdb.h>
32#include <poll.h>
33#include <string.h> // strerror()
34#include <unistd.h>
35
36#include <arpa/inet.h>
37
38#include <linux/fs.h> // Flags for ioctl()
39
40#include <net/if.h> // ifreq structure used by bind()
41
42#include <netinet/icmp6.h>
43#include <netinet/in.h>
44
45#include <netpacket/packet.h> // struct sockaddr_ll
46
47#include <openssl/err.h>
48#include <openssl/ossl_typ.h>
49#include <openssl/ssl.h>
50
51#include <sys/ioctl.h>
52#include <sys/socket.h>
53#include <sys/stat.h> // fchmod()
54#include <sys/time.h> // TIMEVAL_TO_TIMESPEC macro
55#include <sys/types.h>
56#include <sys/un.h>
57
58static_assert((sizeof(__time_t) * CHAR_BIT) >= 64, "__time_t of at least 64-bit is required");
59
60namespace randolf {
61
62 /*======================================================================*//**
63 @brief
64 This @ref rsocket class provides an easy-to-use and thoroughy-implemented
65 object-oriented socket I/O interface for C++, intended to make socket I/O
66 programming (with or without TLS encryption) easier and more enjoyable.
67
68 Here's a short list of benefits that are helpful in developing high quality
69 code that's consistent-and-reliable, and improves overall productivity:
70
71 - eliminating the need to repeatedly write blocks of code that check for
72 errors, by throwing exceptions instead (see @ref randolf::rex::rex class
73 for details and the long list of exceptions that are supported)
74 - eliminating the need to track socket descriptors
75 - eliminating the need to handle encrypted I/O separately (most functions)
76 - eliminating the need to manage memory for many common structures used to
77 interface with socket options, etc.
78 - eliminating the need to record socket I/O statistics with every call to
79 underlying socket I/O functions (see @ref randolf::rsocket_io for
80 details)
81 - text-line reading/writing with an adapative approach (invented by
82 Randolf Richardson in the 1980s for a custom BBS software project) to
83 automatically detect EoL (End-of-Line) character sequences (unless the
84 developer provides a specific sequence via an @ref eol method) that can
85 determine whether an endpoint is sending Linux/UNIX (standard), MacOS,
86 DOS CR/LF, or even broken LF/CR (reverse of DOS) EoL sequences
87 - transparent support for encryption with many additional features,
88 including STARTTLS, ingress/egress policy enforcement, and SNI
89 - eliminating the complexity of handling events with poll(), select(), and
90 related functions (see the @ref randolf::rsocket_mux class for details)
91 - providing a variety of other useful features that make it easier to
92 communicate with socket endpoints, such as receiving/sending an entire
93 structure via a single call to the new-and-specialized @ref recv_struct
94 or @ref send_struct methods, respectively
95
96 An rsocket is either the endpoint that our underlying socket will connect to,
97 or it's the server daemon that our underying socket will listen() to and
98 accept() [inbound] connections from.
99
100 @par Use case
101
102 Using the C interface, the programming must check for errors by testing the
103 response codes (most of which are consistent, with a few subtle outliers),
104 which leads to a lot of additional error-checking code with the potential for
105 unintended errors (a.k.a., bugs). This style is necessary in C, but with C++
106 the way to handle errors is with exceptions, so I created this rsocket class
107 to handle all these tedious details behind-the-scenes and, for any socket
108 errors, to generate exceptions so that source code can be greatly simplified
109 (and, as a result, also easier to read and review).
110
111 Pre-allocating buffers is also handled internally, which is particularly
112 useful when making repeated calls to recv() and related methods. These
113 methods return std::shared_ptr<@c structure > (a C++ smart pointer that aids
114 in the prevention of resource leaks) or std::vector<char> (resized to the
115 actual number of bytes received) as appropriate which eliminates the need to
116 track @c size_t separately.
117
118 @par Conventions
119 Lower-case letter "r" is regularly used in partial example code to represent
120 an instantiated rsocket object.
121
122 An ASCIIZ string is a C-string (char* array) that includes a terminating null
123 (0) character at the end.
124
125 The following custom qualifiers are incorporated into headings by Doxygen
126 alongside method titles throughout the documentation:
127 - @c POSIX denotes a method that is based on POSIX functions by the same
128 name and don't deviate significantly from the POSIX function arguments
129 (intended to be helpful to developers transitioning to/from rsocket or
130 working on source code that utilizes @ref rsocket and POSIX functions)
131 - @c TLS denotes that a method works properly with TLS-encrypted sockets
132 (most of the POSIX functions have been made to work properly with TLS,
133 but for the few rare cases of functions that can't be made to work with
134 TLS an effort has also been made to mention this using Doxygen's
135 "warning" sections in addition to omitting the TLS qualifier)
136
137 @par Getting started with a few simple examples
138
139 This is an example of connecting to an HTTP server, using the "GET" command
140 to request the home page (using HTTP/1.0), then receiving-and-displaying the
141 resulting web page's contents via STDOUT (or sending an error message to
142 STDERR). Finally, we exit with an EXIT_SUCCESS (or EXIT_FAILURE) code.
143
144 @code{.cpp}
145 #include <iostream> // std::cout, std::cerr, std::endl, etc.
146 #include <randolf/rex>
147 #include <randolf/rsocket>
148
149 int main(int argc, char *argv[]) {
150 try {
151 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
152 r.connect("www.example.com", 80);
153 r.sendline("GET / HTTP/1.0");
154 r.sendline("Host: www.example.com");
155 r.sendline("Connection: close");
156 r.send_eol();
157 while (r.is_open()) {
158 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
159 } // -x- while data -x-
160 r.close();
161 } catch (const randolf::rex::xALL e) {
162 std::cerr << "Socket exception: " << e.what() << std::endl;
163 return EXIT_FAILURE;
164 } catch (const std::exception e) {
165 std::cerr << "Other exception: " << e.what() << std::endl;
166 return EXIT_FAILURE;
167 }
168 return EXIT_SUCCESS;
169 } // -x- int main -x-
170 @endcode
171
172 Parameter stacking is supported (with methods that return @c rsocket*); in
173 this example, notice that semicolons (";") and "r." references are omittted
174 (when compared with the above):
175
176 @code{.cpp}
177 #include <iostream> // std::cout, std::cerr, std::endl, etc.
178 #include <randolf/rex>
179 #include <randolf/rsocket>
180
181 int main(int argc, char *argv[]) {
182 try {
183 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
184 r.connect("www.example.com", 80)
185 ->sendline("GET / HTTP/1.0")
186 ->sendline("Host: www.example.com")
187 ->sendline("Connection: close")
188 ->send_eol();
189 while (r.is_open()) {
190 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
191 } // -x- while data -x-
192 r.close();
193 } catch (const randolf::rex::xALL e) {
194 std::cerr << "Socket exception: " << e.what() << std::endl;
195 return EXIT_FAILURE;
196 } catch (const std::exception e) {
197 std::cerr << "Other exception: " << e.what() << std::endl;
198 return EXIT_FAILURE;
199 }
200 return EXIT_SUCCESS;
201 } // -x- int main -x-
202 @endcode
203
204 @par Features
205
206 This is meant to be a comprehensive socket class for C++, which is intended
207 to make socket I/O coding easier for developers with some key features:
208
209 - easy conversion from C-style POSIX sockets due to API consistency
210 - transparent TLS support (OpenSSL dependency)
211 - keys and certificate chains can also be loaded from memory
212 - SNI support (see the @ref rsocket_sni class for more information)
213 - underlying socket handle is accessible (via the @ref socket_fd() method)
214 - socket options are easier to get and to set
215 - sensible support for future or unknown socket options
216 - errors are presented as ~100 separate exception classes: @ref rex::rex
217 - one parent exception class makes it easier to catch all socket errors
218 - a few exceptions groups are also provided to catch groups of errors
219 - new buffers are returned as std::shared_ptr's to eliminate memory leaks
220 - constructors with sensible defaults help to simplify coding
221 - documentation includes code samples (with @c \#include lines as needed)
222 - debug output with helpful output (and option to set output file handle)
223 - low-overhead is considered (this is why there's a bit more overloading)
224 - thread-safety is noted where it is absolutely available (if any caution
225 is warranted, it will also be noted)
226 - can send ASCIIZ (C-strings) without needing to specify string length
227 - can send @c std::string (which tracks its own string length)
228 - each socket can optionally have a @ref name arbitrarily assigned (e.g.,
229 by an algorithm, or even populated with the connecting user's name, or
230 whatever purpose the development goals find it useful for)
231
232 Additional features that are not part of the typical POSIX standard, but
233 deserve special mention because they are needed so often:
234
235 - easy access to internal I/O counters (see @ref rsocket_io for details)
236 - your rsocket_io structure can be updated automatically by rsocket's
237 destructor after underlying socket is closed (see @c net_io_final()
238 method for details)
239 - printf(), printfline(), vprintf(), vprintfline() // Formatted strings
240 - with automatic EoL sequence substitution (and/or the addition of)
241 - recv_asciiz(), send_asciiz() // ASCIIZ string I/O operations
242 - recv_byte() , send_byte() // 8-bit byte operations (LSB/MSB is N/A)
243 - recv_struct(), send_struct() // Multi-byte operations
244 - recv_uint16(), send_uint16() // 16-bit operations with LSB/MSB variants
245 - recv_uint32(), send_uint32() // 32-bit operations with LSB/MSB variants
246 - recv_uint64(), send_uint64() // 64-bit operations with LSB/MSB variants
247 - recvline(), recv_rline(), sendline() // ASCII text line I/O operations
248 - class-wide configurable newline sequence (defaults to @e autodetect)
249 - @ref rsocket_group class for grouping rsockets (and automatic inclusion
250 by accept() and accept4() methods; this may be a different group from
251 whichever group the parent rsocket is in, if it's even in one)
252 - option to send data to all rsocket objects in the rsocket_group
253 - with support from the rsocket_mux class (for multiplexing operations)
254 - automatic naming policies (possibly like net_io() formatting style)
255
256 Some advanced features are planned that exceed what the basic socket I/O
257 functions provide, but are also needed:
258
259 - recv_uint128()/send_uint128() // 128-bit operations w/ LSB/MSB variants
260 - recv_uint(n)/send_uint(n) where "n" specifies the number of bits (which
261 must be a multiple of 8), with LSB/MSB variants
262 - auto-detection of inbound TLS connection (this is turned off by default)
263 - This is not the same as STARTTLS (an application-level command, for
264 which the @ref tls_do_handshake() method will likely be used)
265 - simple timing tracking options using timing_start() and timing_stop()
266 methods, the results of which can be retrieved with timing_get() or a
267 similarly-named group of methods
268
269 Other features that are not a high priority:
270
271 - internal support for portability to Microsoft Windows, which is a major
272 undertaking that I know will be time-consuming since Windows Sockets
273 exhibit some nuanced behaviours and are not consistent with POSIX
274 sockets APIs that are used by Linux, UNIX, MacOS, and pretty much all
275 other Operating Systems. Because of this, MS-Windows portability just
276 isn't a high priority for me (without sufficient demand and sufficient
277 funding so I can commit my time without missing mortgage payments,
278 student loan payments {for my kids}, various living expenses, etc.).
279
280 @par Requirements
281 The @c __time_t structure must be 64-bit (or larger). This becomes espcially
282 important for the @ref recvline method, in addition to Year 2038 compliance.
283
284 @par Notes
285
286 I use the term "ASCIIZ string" to indicate an array of characters that's
287 terminated by a 0 (a.k.a., null). Although this is very much the same as a
288 C-string, the difference is that in many API functions a C-string must often
289 be accompanied by its length value. When referring to an ASCIIZ string, I'm
290 intentionally indicating that the length of the string is not needed because
291 the string is null-terminated. (This term was also commonly used in assembly
292 language programming in the 1970s, 1980s, and 1990s, and as far as I know is
293 still used by machine language programmers today.)
294
295 UTF-8 works without any problems as it is backward-compatible to 8-bit ASCII,
296 and because @c std::string uses 8-bit bytes to store strings internally. Do
297 keep in mind that the manipulation of UTF-8 substrings will require working
298 with UTF-8 codepoints that may consume one or more bytes, which is beyond the
299 scope of the impartial (to UTF-8 and ASCII) functionality that this rsocket
300 class provides.
301
302 UTF-8 newline codepoints NEL (c2 85), LS (e2 80 a8), and PS (e2 80 a9) garner
303 no special handling at this time - unlike CR/LF (0d/0a) - and are merely
304 treated as non-newline codepoints. There is a possibility of adding support
305 for this in the future, but additional research and planning is required to
306 make sure this works properly. What is most likely is that some UTF-8 flags
307 will be added to support each of these (which will probably be disabled by
308 default) that will be integrated into the readline() methods. This also
309 depends on how widely used these particular codepoints are, and pending
310 further research to determine whether these really are supposed to be used
311 functionally as newlines...
312
313 So far, there are two UTF-8 codepoints that absolutely are not functional,
314 yet which a small number of people have mistakenly assumed are:
315 - <sup>C</sup><sub>R</sub> = superscript "C" with subscript "R" (e2 90 9d)
316 - <sup>N</sup><sub>L</sub> = superscript "N" with subscript "L" (e2 90 a4)
317
318 The special characters above are intended to represent the Carriage-Return
319 and New-Line respectively in documentation such as ASCII character reference
320 charts, which were used mostly by IBM in the 1970s and 1980s for instruction
321 manuals (and in other documentation), and also on a few keyboard overlays.
322
323 @par Background
324
325 I created this class to make it easier to write internet server daemons. I
326 started out using C-style socket functions (because C++ doesn't come with a
327 socket class), but I found that I didn't enjoy mixing something as important
328 and detailed as socket I/O in a procedural way into the object-oriented
329 paradigm that C++ provides.
330
331 After looking for existing solutions (none of which served as comprehensive
332 replacements for socket I/O), I embarked on creating the rsocket class, and
333 then I began to understand why this probably hadn't been done -- it's a
334 massive undertaking, primarily because there are a lot of functions that are
335 needed to handle socket I/O. Further, [at the time of this writing] the @c
336 sockaddr_storage structure wasn't as widely used as it should be, and so
337 information about it tended to be scarce, incomplete, or incorrect (further
338 research, and diving down into some pretty deep "rabbit holes," was required
339 to understand this properly, which was worthwhile because it resulted in
340 having transparent support for IPv4 and IPv6 without breaking backward
341 compatibility for code expecting specific structures).
342
343 Moving error codes into exceptions is also a major effort because they are
344 diverse and plentiful, and there are so many errors that can occur at various
345 stages for many different reasons. There are also a few outlier functions
346 that require slightly different approaches to error handling due to subtly
347 different rules for handling their errors, and so the exception-generation
348 wasn't as straight-forward as one might optimistically expect, but this is
349 one of the many benefits of the object-oriented programming pardigm because
350 handling edge cases internally results in a consistent error-handling
351 interface using exceptions that also simplifies the source code. (Need to
352 handle a specific set of conditions? Catch the relevant exceptions for those
353 cases in an inner set of exceptions, and just catch all the others in a more
354 general way without the added complexity of repeatedly checking for errors
355 every step along the way.)
356
357 So, I dedicated time to make this work, and with the intention of making it
358 an open source project once I got it into a state that's ready for the
359 general public. This required putting my other C++ projects on hold, which
360 was fine because they didn't have strict deadlines and using this socket
361 class in them will speed up development in the long-term anyway, so it's
362 clearly worth the effort to me ... and I sincerely hope that my efforts will
363 be helpful to others too.
364
365 My background in programming began when I was a young child, teaching myself
366 BASIC and then machine language (when I found BASIC to be too limited) before
367 moving on to other languages like Perl and Java many years later. Eventually
368 I circled around to C (which I chose to learn the hard way by writing some
369 PostgreSQL extensions) and then C++ a few years after that. I have a lot of
370 experience with socket communications, including fully-featured DNS resolver
371 library code in machine language for Novell's NetWare that used C calling
372 conventions and supported varargs, which worked well for the few developers
373 who needed or wanted it.
374
375 @par History
376 - 2022-Nov-09 v1.00 Initial version
377 - 2022-Nov-26 v1.00 Moved exception handling to a separate class
378 - 2022-Nov-28 v1.00 Completed readline/send functionality
379 - 2022-Dec-03 v1.00 Added endianness transparency
380 - 2022-Dec-04 v1.00 Added printf() support
381 - 2022-Dec-24 v1.00 Added socket MUXing
382 - 2023-Feb-22 v1.00 Added TLS/SSL support
383 - 2023-Mar-10 v1.00 Added TLS-SNI support
384 - 2023-Apr-19 v1.00 Added TLS certificate loading from memory
385 - 2023-May-24 v1.00 Added support for clang++ compilation
386 - 2023-Jun-09 v1.00 Improvements to dynamic internal read buffering
387 - 2023-Oct-31 v1.00 Improvements to various classes
388 - 2024-Feb-21 v1.00 Added is_buffered() method
389 - 2024-Mar-31 v1.00 Completed @ref recvline (implemented a work-around for
390 a subtle SSL_peek failure to extract additional data
391 when a user at an end-point is communicating with
392 "icanon" mode enabled)
393 - 2024-May-24 v1.00 Changed source code to accomodate @c clang++ compiler
394 - 2024-Jun-04 v1.00 Added @c POSIX and @c TLS qualifiers to all applicable
395 methods (Doxygen incorporates into the documentation)
396 - 2024-Oct-23 v1.00 Various minor improvements to the documentation since
397 the previous update
398 - 2024-Nov-05 v1.00 Added eol_consumed_seq() method
399 - 2024-Nov-10 v1.00 Added discard() method
400 - 2024-Nov-17 v1.00 Added recv_rline() method
401 - 2024-Nov-19 v1.00 Added recv_as_string() method
402 - 2024-Nov-22 v1.00 Added discard_line() method
403 - 2024-Nov-28 v1.00 Switched internal ring buffer to a proper ring buffer
404 that's now available in the rring class (which was made
405 initially for this purpose)
406 - 2024-Dec-08 v1.00 Improved shutdown() method to handle SSL shutdown fully
407 and added a parameter to prevent calling SSL_shutdown()
408 - 2024-Dec-10 v1.00 Changed eos() method to use faster ioctl POSIX function
409 and OpenSSL's SSL_has_pending function (with TLS)
410 - 2024-Dec-10 v1.00 Added RECVLINE_FLAGS enum to expand the functionality
411 of how the recvline() and recv_rline methods deal with
412 data in specific scenarios, which helps to satisify the
413 needs of specific advanced data processing scenarios
414 @version 1.00
415 @author Randolf Richardson
416 *///=========================================================================
417 class rsocket {
418
419 // --------------------------------------------------------------------------
420 // The rsocket_group class needs access to some of our internal variables.
421 // --------------------------------------------------------------------------
422 friend class rsocket_group;
423
424 // --------------------------------------------------------------------------
425 // Socket variables.
426 // --------------------------------------------------------------------------
427 int __socket_fd = 0;
428 int __socket_type = 0;
429 int __socket_protocol = 0;
430 struct sockaddr_storage __socket_addr = {}; // Initialize to all elements to their default values
431 socklen_t __socket_addr_size = sizeof(__socket_addr); // We need to point to this (and it might be modified)
432 int __socket_backlog = SOMAXCONN; // Default backlog is 4096 on Linux, and 128 on older systems
433
434 // --------------------------------------------------------------------------
435 // Socket flags (internal, but with get-methods to provide read-only access).
436 // --------------------------------------------------------------------------
437 std::atomic_bool __socket_open = false; // Socket is open by way of socket() function
438 std::atomic_bool __socket_connected = false; // Socket is connected
439
440 // --------------------------------------------------------------------------
441 // TLS flags and variables.
442 // --------------------------------------------------------------------------
443 std::atomic_bool __tls = false; // Indicates whether socket I/O is encrypted with OpenSSL
444 SSL_CTX* __tls_ctx = nullptr; // TLS context (nullptr == no encryption initialized)
445 rsocket_sni* __tls_sni = nullptr; // SNI maps
446 bool __tls_sni_match = false; // Set to TRUE only if SNI matched in the SNI callback
447 SSL* __tls_fd = nullptr; // TLS connection structure (nullptr == no connection)
448 BIO* __tls_rbio = nullptr; // TLS BIO input stream
449 BIO* __tls_wbio = nullptr; // TLS BIO output stream
450 bool __tls_exclusive = false; // See enum TLS_FLAGS::TLS_EXCLUSIVE
451 bool __tls_egress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_EGRESS
452 bool __tls_ingress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_INGRESS
453 bool __tls_server_mode = true; // See enum TLS_FLAGS::TLS_SERVER
454 inline static std::atomic_int __fn_counter = 0; // Used in generating unique-and-threadsafe temporary filenames
455 rsocket* __tls_new_endpoint = nullptr; // Weak pointer to newly-spawned endpoint's rsocket object
456
457 // --------------------------------------------------------------------------
458 // Keep track of test for whether this host stores integers using big endian.
459 // --------------------------------------------------------------------------
460 inline static const bool __endian_is_msb = htons(42) == 42; // TRUE==MSB / FALSE==LSB
461
462 // --------------------------------------------------------------------------
463 // General variables. Atomic variables are used for variables as needed to
464 // support operations in a thread-safe manner.
465 //
466 // Dynamic buffer expansion for extra-long lines resolves problems with the
467 // default of 4,096 bytes for the read-ahead policy in Linux sockets. These
468 // are handled by the randolf::rring class, and echoed here.
469 // --------------------------------------------------------------------------
470 std::atomic_bool __debug = false;
471 std::atomic<std::FILE*> __debug_fd = stderr;
472 std::string __debug_prefix = "rsocket-debug";
473 #define RSOCKET_BUFFER_SIZE 8192 // Hard-coded default buffer size
474 size_t __buffer_size = RSOCKET_BUFFER_SIZE; // Buffer size used by recv() when a buffer size isn't provided
475 rring* __buffer = nullptr;
476 rring_bam* __buffer_bam = nullptr; // Map of rring's memory blocks
477 std::string __name; // Arbitrary name of this rsocket (used by some applications)
478 std::string __name_sni; // SNI hostname (only used when tls_sni is configured)
479
480 // --------------------------------------------------------------------------
481 // EoL sequence variables.
482 // --------------------------------------------------------------------------
483 static const char __CR = (char)13; // CTRL-M / ASCII 13 (0Dh) / "\r" / Used by eol() methods
484 static const char __LF = (char)10; // CTRL-J / ASCII 10 (0Ah) / "\n" / Used by eol() methods
485 static constexpr const char* __CRLF = "\x0d\x0a"; // Used by eol() methods
486 std::string __eol; // Used by recvline() method; maintained by eol() methods
487 std::string __eol_consumed_seq; // The EoL sequence that was successfully received by the most recent use of the recvline() method
488 bool __eol_adoption = true; // Whether to adopt the dynamically detected EoL sequence when __eol is empty (the default)
489 std::string __eol_out = std::string(__CRLF); // Used by sendline() method; maintained by eol() methods; also used by sendline() method
490 bool __eol_fix_printf = true; // Whether to replace "\n" sequence in printf() methods to EoL sequence
491
492 // --------------------------------------------------------------------------
493 // Fine-tuning for __recvline(). This is used with nanosleep().
494 //
495 // To test, also use commands in canonical mode, like this:
496 // stty -icanon && openssl s_client host:port
497 // stty -icanon && netcat host port
498 // stty -icanon && telnet host port
499 // --------------------------------------------------------------------------
500 long __recvline_timeout = 0; // Number of seconds (0 = unlimited; maximum for "long" equates to approximately 292 billion years since the UNIX/Linux epoch)
501
502 // --------------------------------------------------------------------------
503 // Statistical variables.
504 // --------------------------------------------------------------------------
505 rsocket_io* __io_final_addr = nullptr; // Used by ~rsocket() destructor, net_io_final(), and net_io_update()
506 std::atomic_ulong __bytes_rx = 0; // Total number of bytes received
507 std::atomic_ulong __bytes_tx = 0; // Total number of bytes transmitted
508 std::atomic_ulong __crypt_rx = 0; // Total number of encrypted bytes recevied
509 std::atomic_ulong __crypt_tx = 0; // Total number of encrypted bytes transmitted
510
511 public:
512 /*======================================================================*//**
513 @brief
514 Optional flags used with rsocket's @ref recvline() and @ref recv_rline()
515 methods to specify relevant text-line reading policies/semantics.
516 @see recvline
517 @see recv_rline
518 *///=========================================================================
519 enum RECVLINE_FLAGS: int {
520
521 /*----------------------------------------------------------------------*//**
522 The RECVLINE_DEFAULT flag isn't necessary, but it's included here for
523 completeness as it accomodates programming styles that prefer to emphasize
524 when defaults are being relied upon.
525 *///-------------------------------------------------------------------------
526 RECVLINE_DEFAULT = 0,
527
528 /*----------------------------------------------------------------------*//**
529 The RECVLINE_NO_DISCARD_ON_OVERFLOW flag prevents data from being discarded
530 when an @ref randolf::rex::xEOVERFLOW exception is thrown (which is caused
531 when a line of text exceeds the specified maximum line length).
532
533 @note
534 The @c MSG_PEEK flag also prevents the automatic discarding of data,
535 regardless of whether the @ref randolf::rex::xEOVERFLOW is thrown, thus it
536 differs from this flag in multiple ways that vary with how and why it's used.
537
538 @warning
539 Data will need to be consumed in a different way, such as by attempting to
540 read into a larger buffer than what had been allocated, or by receiving
541 portions or discarding some or all that remains.
542
543 By default, when the @ref randolf::rex::xEOVERFLOW exception is thrown, all
544 pending data (up to and including the upcoming EoL sequence, or the EoS,
545 whichever comes first) is discarded.
546 @see discard_line()
547 *///-------------------------------------------------------------------------
548 RECVLINE_KEEP_ON_OVERFLOW = 1,
549
550 /*----------------------------------------------------------------------*//**
551 The RECVLINE_PARTIAL flag receives an incomplete line of text and returns it
552 instead of throwing the @ref randolf::rex::xEOVERFLOW exception, and causes
553 the @ref eol_consumed_seq() method to return an empty string (which indicates
554 that no EoL sequence was consumed).
555 @see eol_consumed_seq()
556 *///-------------------------------------------------------------------------
557 RECVLINE_PARTIAL = 2,
558
559 }; // -x- enum RECVLINE_FLAGS -x-
560
561 /*======================================================================*//**
562 @brief
563 Optional flags used with various methods to determine whether they will throw
564 an @ref randolf::rex::xETIMEDOUT exception or merely return a @c nullptr, an
565 empty set, or a 0 (zero) when a timeout duration elapses.
566
567 If you're not sure of whether to use @ref TIMEOUT_EMPTY, @ref TIMEOUT_NULL,
568 or @ref TIMEOUT_ZERO in your code, then I suggest using @ref TIMEOUT_NULL
569 since @c NULL will likely be the most recognizable in most code reviews.
570
571 @note
572 You'll know when this is an option because the method will support this.
573 *///=========================================================================
574 enum TIMEOUT_BEHAVIOUR: bool {
575
576 /*----------------------------------------------------------------------*//**
577 Indicate that an @ref randolf::rex::xETIMEDOUT exception should be thrown
578 when the timeout duration elapses.
579 *///-------------------------------------------------------------------------
580 TIMEOUT_EXCEPTION = true,
581
582 /*----------------------------------------------------------------------*//**
583 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
584 when the timeout duration elapses.
585 *///-------------------------------------------------------------------------
586 TIMEOUT_EMPTY = false,
587
588 /*----------------------------------------------------------------------*//**
589 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
590 when the timeout duration elapses.
591 *///-------------------------------------------------------------------------
592 TIMEOUT_NULL = false,
593
594 /*----------------------------------------------------------------------*//**
595 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
596 when the timeout duration elapses.
597 *///-------------------------------------------------------------------------
598 TIMEOUT_ZERO = false,
599
600 }; // -x- enum TIMEOUT_BEHAVIOUR -x-
601
602 /*======================================================================*//**
603 @brief
604 Optional flags used with rsocket's @ref tls() and @ref tls_ctx() methods to
605 specify relevant policies/semantics.
606 *///=========================================================================
607 enum TLS_FLAGS: int {
608
609 /*----------------------------------------------------------------------*//**
610 The TLS_DEFAULT flag isn't necessary, but it's included here for completeness
611 as it accomodates programming styles that prefer to emphasize when defaults
612 are being relied upon.
613 *///-------------------------------------------------------------------------
614 TLS_DEFAULT = 0,
615
616 /*----------------------------------------------------------------------*//**
617 Only encrypted connections are permitted, initially, and all attempts to
618 begin with unencrypted connections will consistently fail.
619
620 Encrypted connections must begin with a cryptographic handshake packet, or
621 else the connection will be rejected as due to being reasonably assumed to be
622 "not encrypted."
623 @post
624 If this flag isn't set, an application-level mechanism can be used to upgrade
625 to TLS encryption (this is common with connections that begin unencrypted,
626 and issue a proprietary command {such as the @c STARTTLS command in SMTP} to
627 upgrade to TLS later on {of which the @ref tls() method is used to affect an
628 ingress; see the @ref TLS_NO_INGRESS flag for additional information}).
629 @note
630 Creating an exclusively @c unencrypted connection is accomplished by not
631 initializing TLS.
632 @see tls()
633 @see TLS_NO_EGRESS to prevent support for application-level downgrades to
634 unencrypted connections
635 *///-------------------------------------------------------------------------
636 TLS_EXCLUSIVE = 1,
637
638 /*----------------------------------------------------------------------*//**
639 Mid-stream upgrades to encrypted connections are not permitted (e.g., via
640 application-level initiations like the @c STARTTLS command as seen in SMTP),
641 which will also cause calls to the @ref tls() method to fail fast after plain
642 non-encrypted data has already been sent or received.
643 @pre
644 This flag is not necessary when the @ref TLS_EXCLUSIVE flag is set.
645 @see is_tls_ingress_okay()
646 @see tls_do_handshake()
647 @see TLS_NO_EGRESS
648 *///-------------------------------------------------------------------------
649 TLS_NO_INGRESS = 2,
650
651 /*----------------------------------------------------------------------*//**
652 Mid-stream downgrades to unencrypted connections are not permitted (e.g., via
653 application-level initiations like a hypothetical @c STOPTLS command as seen
654 in FTPS), which will also cause calls to the @ref tls() method to fail fast
655 after encrypted data has already been sent or received.
656 @pre
657 This flag may be combined with the @ref TLS_EXCLUSIVE flag to prevent a
658 connection from being downgraded programatically.
659 @note
660 Although egress to an unencrypted connection doesn't occur automatically
661 (since egress can only be affected programatically to support commands at the
662 application level), this flag is useful to prevent third-party code from
663 downgrading an encrypted @ref rsocket to unencrypted.
664 @warning
665 Supporting unencrypted communications is strongly discouraged over public
666 networks (e.g., the internet) because unencrypted streams are trivially
667 susceptible to man-in-the-middle attacks that can alter the contents of the
668 data in both directions (which is a particularly dangerous prospect for
669 sending/receiving sensitive information).
670 @see is_tls_egress_okay()
671 @see TLS_NO_INGRESS
672 *///-------------------------------------------------------------------------
673 TLS_NO_EGRESS = 4,
674
675 /*----------------------------------------------------------------------*//**
676 This is a convenience flag that provides an option for developers to be more
677 clear in their use of the @ref tls() and @ref tls_ctx() methods to indicate
678 intent to rely on what is already the default.
679 @see tls_do_handshake()
680 @see TLS_SERVER
681 *///-------------------------------------------------------------------------
682 TLS_CLIENT = 0,
683
684 /*----------------------------------------------------------------------*//**
685 Indicates that this rsocket will be for a server daemon, and to initialize a
686 new TLS context (when one isn't being provided) using OpenSSL's
687 @c TLS_server_method() function instead of OpenSSL's @c TLS_client_method()
688 function (the latter is the default because most code is anticipated to be
689 client-oriented).
690
691 @attention
692 Setting the CLIENT/SERVER flag incorrectly will typically cause the following
693 error that's difficult to track down, which is usually triggered by calling
694 @ref accept, @ref accept4(), or @ref connect(), and so I hope that including
695 this information here in this documentation will be helpful:
696 @verbatim
697 error:140C5042:SSL routines:ssl_undefined_function:called a function you should not call
698 @endverbatim
699
700 The absence of this flag has the same effect as specifying the @ref
701 TLS_CLIENT flag.
702 @see TLS_CLIENT
703 *///-------------------------------------------------------------------------
704 TLS_SERVER = 8,
705
706 }; // -x- enum TLS_FLAGS -x-
707
708 private:
709 /*======================================================================*//**
710 Return Code check, and throws an rsocket-specific exception if an error
711 occurred, which is indicated by @c -1 (a lot of example code shows @c < @c 1
712 comparisons, but we specifically test for @c -1 because the documentation
713 clearly states @c -1. This is important because a system that can support an
714 extremely high number of socket handles might, in theory, assign handles with
715 values that get interpreted as negative integers, and so less-than-zero tests
716 would result in dropped packets or dropped sockets (any such socket code that
717 allocates such handles obviously must not ever allocate @c -1 since this
718 would definitely be misinterpreted as an error).
719
720 If rc is not @c -1, then it is simply returned as is.
721
722 If n is nonzero and rc is 0, then n will override errno. (This option is
723 provided to accomodate the few socket library functions that return values
724 that are never errors, and expect the developer to rely on other means of
725 detecting whether an error occurred. This is an example of where Object
726 Oriented Programming is helpful in making things better.)
727 @returns Original value of @c rc (if no exceptions were thrown)
728 *///=========================================================================
729 const int __rc_check(
730 /// Return code
731 const int rc,
732 /// Override @c errno (if not 0)
733 const int n = 0,
734 /// Exception handling flags (see @ref rex::REX_FLAGS for details)
735 const int flags = rex::rex::REX_FLAGS::REX_DEFAULT) {
736 if (rc == -1 || (rc == 0 && n != 0)) {
737 const int socket_errno = n == 0 ? errno : n; // If "n" is not zero, use it instead
738 if (__debug) debug("__rc_check(" + std::to_string(rc) + ", " + std::to_string(n) + ", " + std::to_string(flags) + ") throwing exception (errno=" + std::to_string(socket_errno) + ")"); // TODO: Remove this
739 randolf::rex::mk_exception("", socket_errno, flags); // This function doesn't return (this code ends here)
740 } // -x- if rc -x-
741 return rc;
742 }; // -x- int __rc_check -x-
743
744 /*======================================================================*//**
745 Similar to @ref __rc_check(), but specialized for handling OpenSSL return
746 codes.
747 *///=========================================================================
748 const int __rc_check_tls(
749 /// Return code (from OpenSSL's API functions)
750 const int rc) {
751 if (rc <= 0) {
752 if (__debug) debug("__rc_check_tls(" + std::to_string(rc) + ") throwing exception"); // TODO: Remove this
753 randolf::rex::mk_exception("", SSL_get_error(__tls_fd, rc), rex::rex::REX_FLAGS::REX_TLS); // This function doesn't return (this code ends here)
754 } // -x- if rc -x-
755 return rc;
756 }; // -x- int __rc_check -x-
757
758 /*======================================================================*//**
759 Internal function that opens the socket. This is used by the constructors
760 and their accompanying socket() methods.
761
762 Throws randolf::rex::xEALREADY exception if the socket is already open to
763 prevent multiple calls to @c open, which can be particularly problematic if
764 any of the additional function calls specify different parameters -- this
765 helps developer(s) to avoid unexpected results if they're inadvertently using
766 the same rsocket object when they intend to use different rsocket objects.
767 *///=========================================================================
768 void __socket(
769 /// Communication domain; usually one of:@n
770 /// AF_INET (IPv4)@n
771 /// AF_INET6 (IPv6)@n
772 /// AF_UNIX (UNIX domain sockets)
773 const int family,
774 /// Communication semantics; usually one of:@n
775 /// SOCK_STREAM (common for TCP)@n
776 /// SOCK_DGRAM (common for UDP)
777 const int type,
778 /// Network protocol; usually one of:@n
779 /// IPPROTO_TCP@n
780 /// IPPROTO_UDP@n
781 /// IPPROTO_IP
782 const int protocol) {
783 if (__debug) debug("socket(" + std::to_string(family)
784 + ", " + std::to_string(type)
785 + ", " + std::to_string(protocol)
786 + ");");
787
788 // --------------------------------------------------------------------------
789 // Syntax checks.
790 // --------------------------------------------------------------------------
791 if (__socket_open) throw randolf::rex::xEALREADY("EALREADY: Socket is already open"); // Socket has already been opened
792 if (family == AF_UNSPEC) throw randolf::rex::xEAFNOSUPPORT("EAFNOSUPPORT: AF_UNSPEC family (SO_DOMAIN) is not supported by socket()");
793
794 // --------------------------------------------------------------------------
795 // Build minimum parts of __socket_addr structure.
796 // --------------------------------------------------------------------------
797 __socket_addr.ss_family = family;
798 __socket_type = type;
799 __socket_protocol = protocol;
800
801 // --------------------------------------------------------------------------
802 // Create new socket handle, and save it, then set internal variable to
803 // indicate that the socket is open.
804 // --------------------------------------------------------------------------
805 __socket_fd = __rc_check(::socket(__socket_addr.ss_family, __socket_type, __socket_protocol));
806 __socket_open = true;
807
808 }; // -x- void __socket -x-
809
810 public:
811 /*======================================================================*//**
812 @brief
813 Instantiate an empty rsocket without actually opening a socket, and therefore
814 also without throwing any exceptions (useful in header-file definitions).
815
816 @details
817 Instantiating an empty rsocket is particularly useful for header-file
818 definitions since exceptions can't be handled outside of subroutines, and
819 it's also useful for enabling debug() mode @em before setting the socket's
820 configuration with one of the socket() methods; for example:
821 @code{.cpp}
822 randolf::rsocket r; // Empty rsocket (empty / incomplete initialization)
823 r.debug(true); // Enable debug mode
824 r.socket(...); // Required to complete rsocket initialization
825 @endcode
826
827 @par Notes
828
829 The built-in defaults, when not provided, are as follows ("family" is also
830 known as the "communication domain"):
831 - @c family = AF_INET
832 - @c type = SOCK_STREAM
833 - @c protocol = PF_UNSPEC
834
835 You will need to use one of the socket(...) methods to specify socket details
836 after defining rsocket objects with empty constructors so that you can catch
837 runtime exceptions. (This also provides you with an option to enable debug
838 mode during runtime prior to attempting to open an rsocket.)
839
840 @par Examples
841
842 @code{.cpp}
843 #include <iostream> // std::cout, std::cerr, std::endl, etc.
844 #include <randolf/rex>
845 #include <randolf/rsocket>
846
847 randolf::rsocket r; // <-- Empty rsocket initialization (no exceptions)
848
849 int main(int argc, char *argv[]) {
850 try {
851 r.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Required to complete rsocket initialization
852 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
853 // ... other socket I/O operations
854 r.close();
855 } catch (const randolf::rex::xALL e) {
856 std::cerr << "Socket exception: " << e.what() << std::endl;
857 return EXIT_FAILURE;
858 } catch (const std::exception e) {
859 std::cerr << "Other exception: " << e.what() << std::endl;
860 return EXIT_FAILURE;
861 }
862 return EXIT_SUCCESS;
863 } // -x- int main -x-
864 @endcode
865 @see rsocket()
866 @see socket()
867 @see socket_family()
868 @see socket_fd()
869 @see socket_protocol()
870 @see socket_type()
871 @qualifier TLS
872 *///=========================================================================
873 rsocket() noexcept {}; // -x- constructor rsocket -x-
874
875 /*======================================================================*//**
876 @brief
877 Instantiate an rsocket based on a minimal subset of the settings in the
878 specified rsocket (using it as a template), without actually opening a
879 socket, and therefore also without throwing any exceptions.
880
881 @note
882 This constructor does not suffice as a full clone()-like operation, and is
883 minimal because it's used internally by the @ref accept() and @ref accept4()
884 methods.
885
886 Details that are absorbed from the template/source rsocket (which eliminates
887 the need to assign, set, and configure various parameters (TLS and TLS SNI
888 parameters will be copied in a passive way by default):
889 - Socket family (SO_DOMAIN)
890 - Socket type (SO_TYPE)
891 - Socket protocol (SO_PROTOCOL)
892 - TLS details (status, context {which includes loaded certificates},
893 policies specified with @ref TLS_FLAGS, etc.)
894 - EoL details
895 - TLS context (you'll still need to call `tls_ctx(r->tls_ctx())`)
896 - TLS SNI map (you'll still need to call `tls_sni(r->tls_sni())`)
897
898 @post
899 The TLS Context will not be initialized because it needs a real socket to
900 draw from. If using TLS, you'll need to use the @ref tls() method after
901 the underlying socket has been initiated.
902
903 When @c flag_create_socket is set to FALSE, no exceptions will be thrown.
904
905 @see rsocket()
906 @see socket()
907 @see socket_family()
908 @see socket_fd()
909 @see socket_protocol()
910 @see socket_type()
911 @qualifier TLS
912 *///=========================================================================
913 rsocket(
914 /// Source rsocket object to use as a template to absorb settings from
915 const rsocket* rtemplate,
916 /// TRUE = create a new socket handle (default)@n
917 /// FALSE = don't create a new socket because a new one will be assigned or
918 /// created later (all variants of the @ref accept() methods do this)
919 const bool flag_create_socket = true) {
920
921 // --------------------------------------------------------------------------
922 // General socket variables.
923 // --------------------------------------------------------------------------
924 if (flag_create_socket) {
925 __socket(rtemplate->__socket_addr.ss_family,
926 rtemplate->__socket_type,
927 rtemplate->__socket_protocol);
928 } else { // !flag_create_socket
929 __socket_addr.ss_family = rtemplate->__socket_addr.ss_family;
930 __socket_type = rtemplate->__socket_type;
931 __socket_protocol = rtemplate->__socket_protocol;
932 } // -x- if flag_create_socket -x-
933 __name = rtemplate->__name;
934
935 // --------------------------------------------------------------------------
936 // TLS and SNI settings, but not whether TLS is enabled.
937 // --------------------------------------------------------------------------
938 __tls_ctx = rtemplate->__tls_ctx;
939 __tls_sni = rtemplate->__tls_sni;
940
941 // --------------------------------------------------------------------------
942 // EoL variables.
943 // --------------------------------------------------------------------------
944 __eol.assign( rtemplate->__eol); // Copy source std::string's contents (not a reference)
945 __eol_fix_printf = rtemplate->__eol_fix_printf;
946
947 }; // -x- constructor rsocket -x-
948
949 /*======================================================================*//**
950 @brief
951 Instantiate an rsocket with IP/host address and [optional] port number.
952
953 This is either the endpoint that our underlying socket will be connecting to,
954 or it's the local address of the server daemon that our socket will listen()
955 to and accept() inbound connections from.
956
957 @par Notes
958
959 The built-in defaults, when not provided, are as follows ("family" is also
960 known as the "communication domain"):
961 - @c family = AF_INET
962 - @c type = SOCK_STREAM
963 - @c protocol = PF_UNSPEC
964
965 The socket() methods do the same work as the constructors with matching
966 arguments, and are provided as convenience methods intended to augment
967 empty rsocket constructors used in header files, but do require an address to
968 be specified (for protocols that need port numbers, such as TCP or UDP, a
969 "port" number also needs to be specified since the default port 0 will result
970 in the dynamic allocation of a port number by the system).
971
972 For UNIX domain sockets use family AF_UNIX, type SOCK_STREAM, and protocol
973 IPPROTO_IP when instantiating or opening an rsocket.
974
975 @par Examples
976
977 @code{.cpp}
978 #include <iostream> // std::cout, std::cerr, std::endl, etc.
979 #include <randolf/rex>
980 #include <randolf/rsocket>
981
982 int main(int argc, char *argv[]) {
983 try {
984 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Same as socket() method
985 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
986 r.bind("127.0.0.1", 32768);
987 // ... other socket I/O operations
988 r.close();
989 } catch (const randolf::rex::xALL e) {
990 std::cerr << "Socket exception: " << e.what() << std::endl;
991 return EXIT_FAILURE;
992 } catch (const std::exception e) {
993 std::cerr << "Other exception: " << e.what() << std::endl;
994 return EXIT_FAILURE;
995 }
996 return EXIT_SUCCESS;
997 } // -x- int main -x-
998 @endcode
999
1000 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
1001 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
1002 @throws randolf::rex::xEINVAL Protocal family invalid or not available
1003 @throws randolf::rex::xEINVAL Invalid flags in type
1004 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1005 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1006 @throws randolf::rex::xENOBUFS Insufficient memory
1007 @throws randolf::rex::xENOMEM Insufficient memory
1008 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1009 supported within the specified family (a.k.a., communication domain)
1010 @see rsocket()
1011 @see socket()
1012 @qualifier TLS
1013 *///=========================================================================
1014 rsocket(
1015 /// Communication domain; usually one of:@n
1016 /// AF_INET (IPv4)@n
1017 /// AF_INET6 (IPv6)@n
1018 /// AF_UNIX (UNIX domain sockets)
1019 const int family,
1020 /// Communication semantics; usually one of:@n
1021 /// SOCK_STREAM (common for TCP)@n
1022 /// SOCK_DGRAM (common for UDP)
1023 const int type = SOCK_STREAM,
1024 /// Network protocol; usually one of:@n
1025 /// IPPROTO_TCP@n
1026 /// IPPROTO_UDP@n
1027 /// IPPROTO_IP@n
1028 /// PF_UNSPEC (auto-detect)
1029 const int protocol = PF_UNSPEC) {
1030 __socket(family, type, protocol);
1031 }; // -x- constructor rsocket -x-
1032
1033 /*======================================================================*//**
1034 @brief
1035 Destructor, which closes any underlying sockets, frees any TLS structures
1036 that were allocated by OpenSSL, and performs any other necessary clean-up
1037 before finally copying the I/O statistics to a designated structure (if one
1038 was specified with the @ref net_io_final() method).
1039 @attention
1040 Developers should take care to check that the @ref rsocket_io::is_final flag
1041 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
1042 since there's no guarantee that the destructor will necessarily be executed
1043 in a timely manner (this flag is set last, after all other statistics are
1044 copied into the structure while in a mutex-locked state).
1045 @see net_io_final()
1046 @qualifier TLS
1047 *///=========================================================================
1048 ~rsocket() noexcept {
1049
1050 // --------------------------------------------------------------------------
1051 // Debug.
1052 // --------------------------------------------------------------------------
1053 if (__debug) {
1054 debug("destructor(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1055 + ");");
1056 debug(false); // Disable debug mode (might trigger extra clean-up in future)
1057 } // -x- if _debug -x-
1058
1059 // --------------------------------------------------------------------------
1060 // Free memory and resources and close handles that need to be freed/closed.
1061 // --------------------------------------------------------------------------
1062 if (__tls_fd != nullptr) SSL_free(__tls_fd);
1063// if (__tls_sni != nullptr) tls_sni(nullptr); // Remove any SNI callbacks // WE DON'T NEED THIS
1064// if (__tls_ctx != nullptr) SSL_CTX_free(__tls_ctx); // TODO: Research using SSL_up_ref(__tls_ctx)
1065 if (__socket_fd != 0) ::close(__socket_fd); // POSIX close() is ensured with ::close()
1066 if (__buffer_bam != nullptr) free(__buffer_bam); // Release the rring_bam structure
1067 if (__buffer != nullptr) delete __buffer; // Release the rring buffer
1068
1069 // --------------------------------------------------------------------------
1070 // Copy statistics to final location. (We do this last in case there's an
1071 // issue with locking; there shouldn't be, but if there is then at least we
1072 // already we're not inadvertently hanging on to resources at this point that
1073 // need to be freed/closed anyway.)
1074 // --------------------------------------------------------------------------
1075 if (__io_final_addr != nullptr) {
1076 __io_final_addr->lock();
1077 __io_final_addr->bytes_rx = __bytes_rx;
1078 __io_final_addr->bytes_tx = __bytes_tx;
1079 // ->bytes_xx = <bytes in buffer; these are lost bytes that should be subtracted from __bytes_rx>
1080 __io_final_addr->crypt_rx = __crypt_rx;
1081 __io_final_addr->crypt_tx = __crypt_tx;
1082 // ->crypt_xx = <bytes in buffer; these are lost bytes that should be subtracted from __crypt_rx>
1083 __io_final_addr->is_final = true;
1084 __io_final_addr->unlock();
1085 } // -x- if __io_final_addr -x-
1086
1087 }; // -x- destructor ~rsocket -x-
1088
1089 /*======================================================================*//**
1090 @brief
1091 Accept new [inbound] socket connetions. (This is typically used in a loop.)
1092
1093 @pre
1094 The resulting rsocket object is created before the actual call to the @c
1095 accept() function.
1096
1097 @note
1098 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1099 select(), and accept()/accept4() when using multiple sockets.
1100
1101 @post
1102 To prevent resource leaks, the resulting rsocket needs to be deleted after
1103 it's no longer needed.
1104
1105 @throws randolf::rex::xEBADF The underlying socket is not open
1106 @throws randolf::rex::xECONNABORTED The connection was aborted
1107 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1108 part of the user address space
1109 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1110 @throws randolf::rex::xEHOSTUNREACH No route to host
1111 @throws randolf::rex::xEINTR Interrupted by a signal
1112 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1113 length of the address is invalid
1114 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1115 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1116 @throws randolf::rex::xENETUNREACH No route to network
1117 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1118 @throws randolf::rex::xENOBUFS Insufficient memory
1119 @throws randolf::rex::xENOMEM Insufficient memory
1120 @throws randolf::rex::xENONET Requested host is not reachable on the network
1121 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1122 is not supported
1123 @throws randolf::rex::xENOSR System ran out of stream resources
1124 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1125 doesn't refer to a socket
1126 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1127 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1128 @throws randolf::rex::xEPROTO Protocol error
1129 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1130 supported within the specified family (a.k.a., communication domain)
1131 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1132 a system call is restartable and can be intercepted-and-redirected
1133 (there is no need to catch this exception unless you are an advanced
1134 developer, in which case you likely still won't need to catch it in
1135 code at this level)
1136 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1137 supported within the specified family (a.k.a., communication domain)
1138 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1139 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1140 no inbound connections are waiting
1141
1142 @returns Newly-created rsocket object representing the connection received
1143 @see accept_sp()
1144 @see accept4()
1145 @see accept4_sp()
1146 @see listen
1147 @qualifier POSIX
1148 @qualifier TLS
1149 *///=========================================================================
1150 rsocket* accept() {
1151 if (__debug) debug("accept(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1152 + ");");
1153
1154 // --------------------------------------------------------------------------
1155 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1156 // new incoming connection.
1157 // --------------------------------------------------------------------------
1158 __tls_new_endpoint = new rsocket(this, false);
1159
1160 // --------------------------------------------------------------------------
1161 // Wait for incoming connection, and update internal flags in client rsocket.
1162 // --------------------------------------------------------------------------
1163 __tls_new_endpoint->__socket_fd = ::accept(__socket_fd, (sockaddr*)&__tls_new_endpoint->__socket_addr, &__tls_new_endpoint->__socket_addr_size);
1164 if (__tls_new_endpoint->__socket_fd == -1) {
1165 delete __tls_new_endpoint; // Memory management
1166 //__tls_new_endpoint = nullptr;
1167 __rc_check(-1); // This part throws the exception
1168 } // -x- if rc -x-
1169 __tls_new_endpoint->__socket_open = true;
1170 __tls_new_endpoint->__socket_connected = true;
1171
1172 // --------------------------------------------------------------------------
1173 // Perform TLS accept() as well, but only if TLS is enabled.
1174 //
1175 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1176 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1177 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1178 // --------------------------------------------------------------------------
1179 if (__tls) {
1180 __tls_new_endpoint->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1181 __tls_new_endpoint->tls(true); // Make sure __tls_fd is configured correctly
1182 __rc_check_tls(SSL_accept(__tls_new_endpoint->__tls_fd));
1183 } // -x- if __tls -x-
1184
1185 return __tls_new_endpoint;
1186 }; // -x- rsocket* accept -x-
1187
1188 /*======================================================================*//**
1189 @brief
1190 Accept new [inbound] socket connetions. (This is typically used in a loop.)
1191
1192 @pre
1193 The resulting rsocket object is created before the actual call to the @c
1194 accept() function.
1195
1196 @note
1197 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1198 select(), and accept()/accept4() when using multiple sockets.
1199
1200 @post
1201 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1202 aids in the prevention of resource leaks).
1203
1204 @throws randolf::rex::xEBADF The underlying socket is not open
1205 @throws randolf::rex::xECONNABORTED The connection was aborted
1206 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1207 part of the user address space
1208 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1209 @throws randolf::rex::xEHOSTUNREACH No route to host
1210 @throws randolf::rex::xEINTR Interrupted by a signal
1211 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1212 length of the address is invalid
1213 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1214 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1215 @throws randolf::rex::xENETUNREACH No route to network
1216 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1217 @throws randolf::rex::xENOBUFS Insufficient memory
1218 @throws randolf::rex::xENOMEM Insufficient memory
1219 @throws randolf::rex::xENONET Requested host is not reachable on the network
1220 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1221 is not supported
1222 @throws randolf::rex::xENOSR System ran out of stream resources
1223 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1224 doesn't refer to a socket
1225 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1226 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1227 @throws randolf::rex::xEPROTO Protocol error
1228 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1229 supported within the specified family (a.k.a., communication domain)
1230 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1231 a system call is restartable and can be intercepted-and-redirected
1232 (there is no need to catch this exception unless you are an advanced
1233 developer, in which case you likely still won't need to catch it in
1234 code at this level)
1235 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1236 supported within the specified family (a.k.a., communication domain)
1237 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1238 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1239 no inbound connections are waiting
1240
1241 @returns Newly-created rsocket object representing the connection received,
1242 wrapped in std::shared_ptr (a C++ smart pointer)
1243 @see accept()
1244 @see accept4()
1245 @see accept4_sp()
1246 @see listen
1247 @qualifier TLS
1248 *///=========================================================================
1249 std::shared_ptr<rsocket> accept_sp() {
1250 if (__debug) debug("accept_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1251 + ");");
1252
1253 // --------------------------------------------------------------------------
1254 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1255 // new incoming connection.
1256 // --------------------------------------------------------------------------
1257 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1258 __tls_new_endpoint = r.get(); // The SNI callback needs this
1259
1260 // --------------------------------------------------------------------------
1261 // Wait for incoming connection, and update internal flags in client rsocket.
1262 // --------------------------------------------------------------------------
1263 r->__socket_fd = __rc_check(::accept(__socket_fd, (sockaddr*)&r->__socket_addr, &r->__socket_addr_size));
1264 r->__socket_open = true;
1265 r->__socket_connected = true;
1266
1267 // --------------------------------------------------------------------------
1268 // Perform TLS accept() as well, but only if TLS is enabled.
1269 //
1270 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1271 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1272 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1273 // --------------------------------------------------------------------------
1274 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1275 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1276 r->tls(true); // Make sure __tls_fd is configured correctly
1277 __rc_check_tls(SSL_accept(r->__tls_fd));
1278 } // -x- if __tls -x-
1279
1280 return r;
1281 }; // -x- std::shared_ptr<rsocket> accept_sp -x-
1282
1283 /*======================================================================*//**
1284 @brief
1285 Accept new [inbound] socket connetions, with socket flags specified. (This
1286 is typically used in a loop.)
1287
1288 @pre
1289 The resulting rsocket object is created before the actual call to the @c
1290 accept4() function.
1291
1292 @note
1293 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1294 select(), and accept()/accept4() when using multiple sockets.
1295
1296 @post
1297 To prevent resource leaks, the resulting rsocket needs to be deleted after
1298 it's no longer needed.
1299
1300 @throws randolf::rex::xEBADF The underlying socket is not open
1301 @throws randolf::rex::xECONNABORTED The connection was aborted
1302 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1303 part of the user address space
1304 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1305 @throws randolf::rex::xEHOSTUNREACH No route to host
1306 @throws randolf::rex::xEINTR Interrupted by a signal
1307 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1308 length of the address is invalid
1309 @throws randolf::rex::xEINVAL Invalid value in flags
1310 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1311 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1312 @throws randolf::rex::xENETUNREACH No route to network
1313 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1314 @throws randolf::rex::xENOBUFS Insufficient memory
1315 @throws randolf::rex::xENOMEM Insufficient memory
1316 @throws randolf::rex::xENONET Requested host is not reachable on the network
1317 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1318 is not supported
1319 @throws randolf::rex::xENOSR System ran out of stream resources
1320 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1321 doesn't refer to a socket
1322 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1323 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1324 @throws randolf::rex::xEPROTO Protocol error
1325 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1326 supported within the specified family (a.k.a., communication domain)
1327 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1328 a system call is restartable and can be intercepted-and-redirected
1329 (there is no need to catch this exception unless you are an advanced
1330 developer, in which case you likely still won't need to catch it in
1331 code at this level)
1332 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1333 supported within the specified family (a.k.a., communication domain)
1334 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1335 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1336 no inbound connections are waiting
1337
1338 @returns Newly-created rsocket object representing the connection received
1339 @see accept()
1340 @see accept_sp()
1341 @see accept4_sp()
1342 @see listen
1343 @qualifier POSIX
1344 @qualifier TLS
1345 *///=========================================================================
1346 rsocket* accept4(
1347 /// SOCK_NONBLOCK@n SOCK_CLOEXEC
1348 const int posix_flags = 0) {
1349 if (__debug) debug("accept4(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1350 + ", " + std::to_string(posix_flags)
1351 + ");");
1352
1353 // --------------------------------------------------------------------------
1354 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1355 // new incoming connection.
1356 // --------------------------------------------------------------------------
1357 rsocket* __tls_new_endpoint = new rsocket(this, false);
1358
1359 // --------------------------------------------------------------------------
1360 // Wait for incoming connection, and update internal flags in client rsocket.
1361 // --------------------------------------------------------------------------
1362 __tls_new_endpoint->__socket_fd = ::accept4(__socket_fd, (sockaddr*)&__tls_new_endpoint->__socket_addr, &__tls_new_endpoint->__socket_addr_size, posix_flags);
1363 if (__tls_new_endpoint->__socket_fd == -1) {
1364 delete __tls_new_endpoint; // Memory management
1365 __rc_check(-1); // This part throws the exception
1366 } // -x- if rc -x-
1367 __tls_new_endpoint->__socket_open = true;
1368 __tls_new_endpoint->__socket_connected = true;
1369
1370 // --------------------------------------------------------------------------
1371 // Perform TLS accept() as well, but only if TLS is enabled.
1372 //
1373 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1374 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1375 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1376 // --------------------------------------------------------------------------
1377 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1378 __tls_new_endpoint->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1379 __tls_new_endpoint->tls(true); // Make sure __tls_fd is configured correctly
1380 __rc_check_tls(SSL_accept(__tls_new_endpoint->__tls_fd));
1381 } // -x- if __tls -x-
1382
1383 return __tls_new_endpoint;
1384 }; // -x- rsocket* accept4 -x-
1385
1386 /*======================================================================*//**
1387 @brief
1388 Accept new [inbound] socket connetions, with socket flags specified. (This
1389 is typically used in a loop.)
1390
1391 @pre
1392 The resulting rsocket object is created before the actual call to the @c
1393 accept4() function.
1394
1395 @note
1396 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1397 select(), and accept()/accept4() when using multiple sockets.
1398
1399 @post
1400 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1401 aids in the prevention of resource leaks).
1402
1403 @throws randolf::rex::xEBADF The underlying socket is not open
1404 @throws randolf::rex::xECONNABORTED The connection was aborted
1405 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1406 part of the user address space
1407 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1408 @throws randolf::rex::xEHOSTUNREACH No route to host
1409 @throws randolf::rex::xEINTR Interrupted by a signal
1410 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1411 length of the address is invalid
1412 @throws randolf::rex::xEINVAL Invalid value in flags
1413 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1414 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1415 @throws randolf::rex::xENETUNREACH No route to network
1416 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1417 @throws randolf::rex::xENOBUFS Insufficient memory
1418 @throws randolf::rex::xENOMEM Insufficient memory
1419 @throws randolf::rex::xENONET Requested host is not reachable on the network
1420 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1421 is not supported
1422 @throws randolf::rex::xENOSR System ran out of stream resources
1423 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1424 doesn't refer to a socket
1425 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1426 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1427 @throws randolf::rex::xEPROTO Protocol error
1428 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1429 supported within the specified family (a.k.a., communication domain)
1430 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1431 a system call is restartable and can be intercepted-and-redirected
1432 (there is no need to catch this exception unless you are an advanced
1433 developer, in which case you likely still won't need to catch it in
1434 code at this level)
1435 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1436 supported within the specified family (a.k.a., communication domain)
1437 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1438 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1439 no inbound connections are waiting
1440
1441 @returns Newly-created rsocket object representing the connection received,
1442 wrapped in std::shared_ptr (a C++ smart pointer)
1443 @see accept()
1444 @see accept_sp()
1445 @see accept4()
1446 @see listen
1447 @qualifier TLS
1448 *///=========================================================================
1449 std::shared_ptr<rsocket> accept4_sp(
1450 /// SOCK_NONBLOCK@n SOCK_CLOEXEC
1451 const int posix_flags = 0) {
1452 if (__debug) debug("accept4_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1453 + ", " + std::to_string(posix_flags)
1454 + ");");
1455
1456 // --------------------------------------------------------------------------
1457 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1458 // new incoming connection.
1459 // --------------------------------------------------------------------------
1460 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1461 __tls_new_endpoint = r.get(); // The SNI callback needs this
1462
1463 // --------------------------------------------------------------------------
1464 // Wait for incoming connection, and update internal flags in client rsocket.
1465 // --------------------------------------------------------------------------
1466 r->__socket_fd = __rc_check(::accept4(__socket_fd, (sockaddr*)&r->__socket_addr, &r->__socket_addr_size, posix_flags));
1467 r->__socket_open = true;
1468 r->__socket_connected = true;
1469
1470 // --------------------------------------------------------------------------
1471 // Perform TLS accept() as well, but only if TLS is enabled.
1472 //
1473 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1474 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1475 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1476 // --------------------------------------------------------------------------
1477 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1478 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1479 r->tls(true); // Make sure __tls_fd is configured correctly
1480 __rc_check_tls(SSL_accept(r->__tls_fd));
1481 } // -x- if __tls -x-
1482// if (__tls_ctx != nullptr) { r->tls(true); r->__tls = (bool)__tls; } // Make sure __tls_fd is allocated appropriately
1483
1484 return r;
1485 }; // -x- std::shared_ptr<rsocket> accept4_sp -x-
1486
1487 /*======================================================================*//**
1488 @brief
1489 Override the default @ref listen backlog for this rsocket.
1490
1491 @note
1492 The default backlog is SOMAXCONN (4096 on Linux, and 128 on older systems).
1493
1494 @returns The same rsocket object so as to facilitate stacking
1495 @see listen
1496 @qualifier TLS
1497 *///=========================================================================
1498 rsocket* backlog(
1499 /// Backlog queue size (0 = use SOMAXCONN, which is the system default of
1500 /// 4096 on Linux, and 128 on older systems)
1501 int backlog = 0) noexcept {
1502 __socket_backlog = backlog == 0 ? SOMAXCONN : backlog;
1503 return this;
1504 }; // -x- rsocket* backlog -x-
1505
1506 /*======================================================================*//**
1507 @brief
1508 Find out what this rsocket's default listen backlog is.
1509 @returns The default listen backlog
1510 @see listen
1511 @qualifier TLS
1512 *///=========================================================================
1513 int backlog() noexcept {
1514 return __socket_backlog;
1515 }; // -x- int backlog -x-
1516
1517 /*======================================================================*//**
1518 @brief
1519 Bind this socket to the specified network address (and port number if the
1520 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1521 used for server deamons, but can also be used to control the address from
1522 which the local host will make an outbound connection via the @ref connect()
1523 method (this bound address is the address from which the endpoint will
1524 recieve your connection).
1525
1526 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
1527 (or 1023 on some Operating Systems that test only for below 1024) in
1528 the absence of elevated access or the absence of a capability flag
1529 having been set
1530 @throws randolf::rex::xEACCES If binding to an interface on systems that
1531 require elevated access for direct interface binding in absence of
1532 said elevated access
1533 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1534 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
1535 address is not available on any local interface
1536 @throws randolf::rex::xEBADF The underlying socket is not open
1537 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1538 part of the user address space
1539 @throws randolf::rex::xEINVAL Socket is already bound
1540 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
1541 valid for this socket's family (a.k.a., communication domain)
1542 @throws randolf::rex::xENOMEM Insufficient memory
1543 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1544 doesn't refer to a socket
1545 @n@n
1546 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1547 @throws randolf::rex::xEACCES Write permission or search permission denied
1548 on a component of the path prefix (@c AF_UNIX family)
1549 @throws randolf::rex::xELOOP Too many symbolic links encountered while
1550 resolving address (@c AF_UNIX family)
1551 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
1552 @throws randolf::rex::xENOENT One of the path's directory components doesn't
1553 exist (@c AF_UNIX family)
1554 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
1555 directory (@c AF_UNIX family)
1556 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
1557
1558 @returns The same rsocket object so as to facilitate stacking
1559 @see bind(std::string, int)
1560 @see connect
1561 @see listen
1562 @qualifier POSIX
1563 @qualifier TLS
1564 *///=========================================================================
1565 rsocket* bind(
1566 /// Socket address structure
1567 const struct sockaddr* addr,
1568 /// Length of socket structure
1569 const socklen_t addrlen = sizeof(sockaddr)) {
1570 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1571 + ", <sockaddr*>"
1572 + ", size=" + std::to_string(addrlen)
1573 + ");");
1574 if (addrlen > sizeof(sockaddr_storage)) randolf::rex::mk_exception("addlen is too large for bind()", EINVAL);
1575 __rc_check(::bind(__socket_fd, addr, addrlen));
1576 std::memcpy(&__socket_addr, addr, addrlen); // No errors were returned by bind(), so now we'll copy addr to __socket_addr
1577 return this;
1578 } // -x- rsocket* bind -x-
1579
1580 //===========================================================================
1581 //
1582 // TODO: Document usage of IP_BIND_ADDRESS_NO_PORT to support connecting
1583 // from a specific IP address without losing ephemeral port selection.
1584 // Notes: https://idea.popcount.org/2014-04-03-bind-before-connect/
1585 //
1586 // TODO: Support AF_BLUETOOTH addresses
1587 // Reference:
1588 // https://man7.org/linux/man-pages/man7/address_families.7.html
1589 //
1590 // TODO: Support AF_IPX addresses
1591 // Reference:
1592 // https://man7.org/linux/man-pages/man7/address_families.7.html
1593 //
1594 // TODO: Support AF_APPLETALK addresses
1595 // Reference: https://man7.org/linux/man-pages/man7/ddp.7.html
1596 //
1597 // TODO: Support AF_PACKET addresses
1598 // Reference: https://man7.org/linux/man-pages/man7/packet.7.html
1599 //
1600 // TODO: Support AF_X25 addresses
1601 // Reference: https://man7.org/linux/man-pages/man7/x25.7.html
1602 //
1603 // TODO: Support AF_NETLINK addresses
1604 // Reference: https://man7.org/linux/man-pages/man7/netlink.7.html
1605 //
1606 /*======================================================================*//**
1607 @brief
1608 Bind this socket to the specified network address (and port number if the
1609 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1610 used for server deamons, but can also be used to control the address from
1611 which the local host will make an outbound connection via the @ref connect()
1612 method (this bound address is address from which the endpoint will recieve
1613 your connection).
1614
1615 In addition to the standard IPv4 (AF_INET family) and IPv6 (AF_INET6 family)
1616 support, rsocket also supports a few other binding options in a manner that
1617 makes it easy to utilize, without having to handle special implementation
1618 details (because they're handled behind-the-scenese by this rsocket class).
1619
1620 The address string supports a number of different notations and formats,
1621 which are documented, hereunder, with examples:
1622 - IPv4 addresses
1623 - IPv6 addresses
1624 - Network interfaces
1625 - UNIX domain sockets
1626
1627 <hr>
1628 @par IPv4 address notations (address family @c AF_INET)
1629 Takes the form of @c x.x.x.x where each "x" represents an octet in the range
1630 of @c 0 through @c 255 (e.g., @c 127.0.0.1).
1631
1632 Under a proper implenetation of TCP/IP, an IPv4 address can be abbreviated
1633 to fewer octets. The following examples demonstrate this (an unabbreviated
1634 IPv4 address is included for completeness):
1635 - @c 0.0.0.1 may be abbreviated to @c 1
1636 - @c 4.0.0.1 may be abbrevaited to @c 4.1
1637 - @c 4.3.0.1 may be abbreviated to @c 4.3.1
1638 - @c 4.3.2.1 is not abbreviated (this is the most common usage)
1639
1640 @par Example of binding to IPv4 localhost
1641
1642 @code{.cpp}
1643 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1644 #include <randolf/rex>
1645 #include <randolf/rsocket>
1646
1647 int main(int argc, char *argv[]) {
1648 try {
1649 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1650 r.bind("127.0.0.1", 32768); // <-- You are here
1651 // ... other socket I/O operations
1652 r.close();
1653 } catch (const randolf::rex::xEACCES e) {
1654 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1655 return EXIT_FAILURE;
1656 } catch (const randolf::rex::xALL e) {
1657 std::cerr << "Socket exception: " << e.what() << std::endl;
1658 return EXIT_FAILURE;
1659 } catch (const std::exception e) {
1660 std::cerr << "Other exception: " << e.what() << std::endl;
1661 return EXIT_FAILURE;
1662 }
1663 return EXIT_SUCCESS;
1664 } // -x- int main -x-
1665 @endcode
1666
1667 @note
1668 Specifying the IP address of @c 0.0.0.0 will bind to all IPv4 addresses that
1669 are assigned to all of the host machine's network interfaces with IPv4
1670 bindings.
1671 @n@n
1672 Specifying the IP address of @c 127.0.0.1 (localhost) is useful for serving
1673 only those applications that are running on the local host and use an IPv4
1674 socket to communicate.
1675
1676 <hr>
1677 @par IPv6 address notations (address family @c AF_INET6)
1678 Takes the form of @c x:x:x:x:x:x:x:x where each "x" represents a segment in
1679 the range of @c 0 through @c ffff in hexadecimal (e.g., `0:0:0:0:0:0:0:1`),
1680 or `x:x:x:x:x:x:y.y.y.y` where `y.y.y.y` represents an [unabbreviated] IPv4
1681 address (which merely replaces the last two IPv6 segments).
1682
1683 Under a proper implenetation of TCP/IP, an IPv6 address can be abbreviated
1684 to fewer segments by using a sequence of two colons (`::`) to indicate that
1685 the undefined segments are @c 0 (this abbreviation can only be used once,
1686 and may represent segments at the beginning or end, or anywhere in between).
1687 The following examples demonstrate this (an unabbreviated IPv6 address is
1688 included for completeness):
1689 - `0:0:0:0:0:0:0:1` may be abbreviated to `::1`
1690 - `0:0:0:0:0:0:2:1` may be abbreviated to `::2:1`
1691 - `8:0:0:0:0:0:2:1` may be abbreviated to `8::2:1`
1692 - `8:7:0:0:0:0:2:1` may be abbreviated to `8:7::2:1`
1693 - `8:7:0:0:0:0:0:0` may be abbreviated to `8:7::`
1694 - `8:0:0:0:0:0:0:0` may be abbreviated to `8::`
1695 - `8:7:6:5:4:3:2:1` is not abbreviated
1696
1697 @par Example of binding to IPv6 localhost
1698
1699 @code{.cpp}
1700 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1701 #include <randolf/rex>
1702 #include <randolf/rsocket>
1703
1704 int main(int argc, char *argv[]) {
1705 try {
1706 randolf::rsocket r(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1707 r.bind("::1", 32768); // <-- You are here
1708 // ... other socket I/O operations
1709 r.close();
1710 } catch (const randolf::rex::xEACCES e) {
1711 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1712 return EXIT_FAILURE;
1713 } catch (const randolf::rex::xALL e) {
1714 std::cerr << "Socket exception: " << e.what() << std::endl;
1715 return EXIT_FAILURE;
1716 } catch (const std::exception e) {
1717 std::cerr << "Other exception: " << e.what() << std::endl;
1718 return EXIT_FAILURE;
1719 }
1720 return EXIT_SUCCESS;
1721 } // -x- int main -x-
1722 @endcode
1723
1724 @note
1725 Specifying the IP address of @c :: will bind to all IPv6 addresses that are
1726 assigned to all of the host machine's network interfaces with IPv6 bindings.
1727 @n@n
1728 Specifying the IP address of @c ::1 (localhost) is useful for serving only
1729 those applications that are running on the local host and use an IPv6 socket
1730 to communicate.
1731
1732 <hr>
1733 @par Interface address notation (address families @c AF_INET and @c AF_INET6)
1734 Takes the form of @c if=name where "name" represents the name of a local
1735 network interface.
1736
1737 Interface binding is useful when binding to interfaces that aren't configured
1738 with a static IP address because they were dymamically configured via the
1739 Dynamic Host Configuration Protocol (DHCP) or Stateless Address Configuration
1740 (SLAAC), or the configuration was changed manually by an administrator and
1741 you don't want your software to handle such changes gracefully, even if the
1742 new IP address is within the scope of an entirely different CIDR. (Changing
1743 between the IPv4 and IPv6 addresses is not supported, however, due to the
1744 fundamental differences between these two address families that includes
1745 differences beyond that of IP address format, although under a proper
1746 implementation of TCP/IP the binding should survive such changes when the IP
1747 address is reverted to the initial IP address family of the bound interface.)
1748
1749 Examples of interfaces include:
1750 - `if=lo` typical for localhost virtual network adapter
1751 - `if=bond0` typical for the first bonded virtual network adapter (used in
1752 failover and load-balancing network configurations)
1753 - `if=br0` typical for the first bridge virtual network adapter
1754 - `if=eth0` typical for the first ethernet network adapter
1755 - `if=tap0` typical for the first virtual layer 2 ethernet switch (used by
1756 VPNs to extend a remote network into the local network stack)
1757 - `if=tun0` typical for the first virtual layer 3 ethernet tunnel (used by
1758 VPNs to provide access to a remote network)
1759 - `if=wlo1` typical for the first wireless network adapter
1760
1761 @par Example of binding to interface localhost
1762
1763 @code{.cpp}
1764 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1765 #include <randolf/rex>
1766 #include <randolf/rsocket>
1767
1768 int main(int argc, char *argv[]) {
1769 try {
1770 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1771 r.bind("if=lo", 32768); // <-- You are here
1772 // ... other socket I/O operations
1773 r.close();
1774 } catch (const randolf::rex::xEACCES e) {
1775 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1776 return EXIT_FAILURE;
1777 } catch (const randolf::rex::xALL e) {
1778 std::cerr << "Socket exception: " << e.what() << std::endl;
1779 return EXIT_FAILURE;
1780 } catch (const std::exception e) {
1781 std::cerr << "Other exception: " << e.what() << std::endl;
1782 return EXIT_FAILURE;
1783 }
1784 return EXIT_SUCCESS;
1785 } // -x- int main -x-
1786 @endcode
1787
1788 @note
1789 This is not a standard feature of the POSIX bind() function. This rsocket
1790 class uses the setsockopt() function behind-the-scenes to configure (enable)
1791 the @c SO_BINDTODEVICE option, and then the bind() function is called with
1792 the IPv4 address @c 0.0.0.0 if the underlying socket was initialized to the
1793 @c AF_INET family, or the IPv6 address @c :: if the underlying socket was
1794 initialized to the @c AF_INET6 family.
1795 @n@n
1796 Specifying the interface address of `if=lo` (localhost) is useful for serving
1797 only those applications that are running on the local host and use an IPv4 or
1798 IPv6 socket to communicate.
1799
1800 <hr>
1801 @par UNIX Domain Sockets address-notation (address family @c AF_UNIX)
1802 Takes the form of @c /path where "/path" represents an absolute path on the
1803 local file system (the path can also be relative, in which case it doesn't
1804 begin with a slash (`/`) character, but extra care is needed to ensure that
1805 the path will actually be at its expected location).
1806
1807 @par Example of binding to UNIX domain sockets
1808
1809 @code{.cpp}
1810 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1811 #include <randolf/rex>
1812 #include <randolf/rsocket>
1813
1814 int main(int argc, char *argv[]) {
1815 try {
1816 randolf::rsocket r(AF_UNIX, SOCK_STREAM);
1817 r.bind("/var/run/rsocket-test-socket.tmp"); // <-- You are here
1818 // ... other socket I/O operations
1819 r.close();
1820 } catch (const randolf::rex::xEACCES e) {
1821 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1822 return EXIT_FAILURE;
1823 } catch (const randolf::rex::xALL e) {
1824 std::cerr << "Socket exception: " << e.what() << std::endl;
1825 return EXIT_FAILURE;
1826 } catch (const std::exception e) {
1827 std::cerr << "Other exception: " << e.what() << std::endl;
1828 return EXIT_FAILURE;
1829 }
1830 return EXIT_SUCCESS;
1831 } // -x- int main -x-
1832 @endcode
1833
1834 @note
1835 Normal socket I/O functionality is used to exchange data with another process
1836 that can open the same UNIX domain socket (normally on the same host,
1837 although it may not be outside the realm of possibility for future Linux
1838 kernels to support UNIX domain sockets on remote hosts).
1839
1840 <hr>
1841 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
1842 (or 1023 on some Operating Systems that test only for below 1024) in
1843 the absence of elevated access or the absence of a capability flag
1844 having been set
1845 @throws randolf::rex::xEACCES If binding to an interface on systems that
1846 require elevated access for direct interface binding in absence of
1847 said elevated access
1848 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1849 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
1850 address is not available on any local interface
1851 @throws randolf::rex::xEBADF The underlying socket is not open
1852 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1853 part of the user address space
1854 @throws randolf::rex::xEINVAL Socket is already bound
1855 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
1856 valid for this socket's family (a.k.a., communication domain)
1857 @throws randolf::rex::xENOMEM Insufficient memory
1858 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1859 doesn't refer to a socket
1860 @n@n
1861 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1862 @throws randolf::rex::xEACCES Write permission or search permission denied
1863 on a component of the path prefix (@c AF_UNIX family)
1864 @throws randolf::rex::xELOOP Too many symbolic links encountered while
1865 resolving address (@c AF_UNIX family)
1866 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
1867 @throws randolf::rex::xENOENT One of the path's directory components doesn't
1868 exist (@c AF_UNIX family)
1869 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
1870 directory (@c AF_UNIX family)
1871 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
1872
1873 @returns The same rsocket object so as to facilitate stacking
1874 @see bind(const struct sockaddr*, const socklen_t)
1875 @see connect
1876 @see listen
1877 @qualifier TLS
1878 *///=========================================================================
1879 rsocket* bind(
1880 /// IPv4 address, IPv6 address, hostname, UNIX domain sockets path, or specialized variant (see notes, above)
1881 const std::string address,
1882 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
1883 const int port = 0) {
1884 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1885 + ", " + address
1886 + " port=" + std::to_string(port) + (port == 0 ? " (emphemeral or not-applicable)" : "")
1887 + ", size=" + std::to_string(__socket_addr_size)
1888 + ");");
1889
1890//std::cout << " address: " << address << std::endl;
1891 if (address.starts_with("if=")) { // Bind to interface
1892// TODO: Use family() to simplify this section of code
1893 struct ifreq ifr{};
1894 if (address.size() - 3 >= sizeof(ifr.ifr_name)) {} // Throw exception because ASCIIZ name will be too long
1895 address.copy(ifr.ifr_name, address.size() - 3, 3); // Copy address, excluding the "if=" portion
1896 __rc_check(::setsockopt(__socket_fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)));
1897 // TODO: Figure out how to use INADDR_ANY instead of 0 or :: (assuming this is even possible)
1898 // TODO: Test IPv6 more (telnet doesn't seem to be able to connect, and continues to work on IPv4 address)
1899 __socket_addr = *mk_sockaddr_storage(__socket_addr.ss_family == AF_INET6 ? "::" : "0", port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
1900 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
1901
1902// } else if (address.starts_with("if_cidr=")) { // Bind to interface based on matching CIDR
1903// // REMINDER: Update static family() method as well
1904// getifaddrs: https://man7.org/linux/man-pages/man3/getifaddrs.3.html
1905// } else if (address.starts_with("if_mac=")) { // Bind to interface based on its physical/hardware machine address
1906// // REMINDER: Update static family() method as well
1907// // Resolve interface name, and then just do what if= does (above)
1908
1909 } else {
1910 __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
1911 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
1912// __rc_check(::bind(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
1913 }
1914 return this;
1915 } // -x- rsocket* bind -x-
1916
1917 /*======================================================================*//**
1918 @brief
1919 Find out what buffer size is used by the various recv() methods.
1920 @returns Buffer size (in bytes)
1921 @see buffer_size(const size_t nbytes)
1922 @see is_buffered
1923 @qualifier TLS
1924 *///=========================================================================
1925 const size_t buffer_size() noexcept { return __buffer->get_size(); }; // -x- size_t buffer_size -x-
1926
1927 /*======================================================================*//**
1928 @brief
1929 Override the default buffer size (typically 8,192 bytes) used by the various
1930 recv() methods.
1931
1932 If resetting to the compiled-in default, use the buffer_size_reset() method
1933 instead of setting the value directly. This ensures that future versions,
1934 with a different compiled-in default, will be reset to the compiled-in value.
1935 @exception std::overflow_error If the new size exceeds the amount of data
1936 that's already present in the ring buffer
1937 @returns The same rsocket object so as to facilitate stacking
1938 @see buffer_size
1939 @see buffer_size_reset
1940 @qualifier TLS
1941 *///=========================================================================
1942 rsocket* buffer_size(
1943 /// Size of the new buffer (in bytes)
1944 const size_t nbytes) {
1945 __buffer_size = nbytes;
1946 if (__debug) debug("buffer_size(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1947 + " nbytes=" + std::to_string(nbytes)
1948 + ");");
1949 if (__buffer != nullptr) __buffer->set_size(nbytes);
1950 return this;
1951 }; // -x- rsocket* buffer_size -x-
1952
1953 /*======================================================================*//**
1954 @brief
1955 Reset the default buffer size (typically 1024) used by the various recv()
1956 methods.
1957
1958 This method is preferred for resetting to the compiled-in default instead of
1959 setting the value directly. This ensures that future versions, with a
1960 different compiled-in default, will be reset to the compiled-in value.
1961 @exception std::overflow_error If the new size exceeds the amount of data
1962 that's already present in the ring buffer
1963 @returns The same rsocket object so as to facilitate stacking
1964 @see buffer_size(const size_t nbytes)
1965 @qualifier TLS
1966 *///=========================================================================
1967 rsocket* buffer_size_reset() {
1968 __buffer_size = RSOCKET_BUFFER_SIZE;
1969 if (__debug) debug("buffer_size_reset(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1970 + " nbytes=" + std::to_string(RSOCKET_BUFFER_SIZE)
1971 + ");");
1972 if (__buffer != nullptr) __buffer->set_size(__buffer_size);
1973 return this;
1974 }; // -x- rsocket* buffer_size_reset -x-
1975
1976 //===========================================================================
1977 //
1978 // TODO: If family is AF_UNIX then also unlink the domain socket file.
1979 //
1980 /*======================================================================*//**
1981 @brief
1982 Close this rsocket. (If this rsocket was already closed, then calling this
1983 method additional times will have no effect, and will not cause exceptions to
1984 be thrown.)
1985 @warning
1986 This method may throw exceptions in some circumstances, which is extremely
1987 rare. If you prefer to close without having to contend with any exceptions
1988 (e.g., while calling close() from within a destructor), the close_passive()
1989 method will return an integer indicating success/failure instead of throwing
1990 an exception, and then you'll have to handle errno manually (which is useful
1991 in destructors because any error can merely be handled procedurally).
1992
1993 @throws randolf::rex::xEBADF The underlying socket is not open
1994 @throws randolf::rex::xEINTR Interrupted by a signal
1995 @throws randolf::rex::xEIO An I/O error occurred
1996
1997 @returns The same rsocket object so as to facilitate stacking
1998 @see is_closed()
1999 @qualifier POSIX
2000 @qualifier TLS
2001 *///=========================================================================
2002 rsocket* close() {
2003 if (__socket_open) {
2004 __rc_check(::close(__socket_fd));
2005 __socket_open = false;
2006 } // -x- if __socket_open -x-
2007 return this;
2008 }; // -x- rsocket* close -x-
2009
2010 /*======================================================================*//**
2011 @brief
2012 Close this rsocket without throwing any exceptions (an error code is returned
2013 instead, which is useful while calling close_passive() from within a
2014 destructor).
2015 @returns 0 = success
2016 @returns -1 = error (errno will be set accordingly)
2017 @see is_closed()
2018 @qualifier TLS
2019 *///=========================================================================
2020 int close_passive() noexcept {
2021 if (__socket_open) {
2022 int rc = ::close(__socket_fd);
2023 if (rc == 0) __socket_open = false;
2024 return rc;
2025 } // -x- if __socket_open -x-
2026 return 0; // Indicate success (because the socket was previously closed successfully)
2027 }; // -x- int close_passive -x-
2028
2029 /*======================================================================*//**
2030 @brief
2031 Connect this socket to a specific endpoint (which may differ from this
2032 rsocket's address that was previously configured by the @ref bind() method).
2033
2034 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
2035 @throws randolf::rex::xEADDRNOTAVAIL No ephemeral ports are available for
2036 assignment to unbound socket
2037 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
2038 @throws randolf::rex::xEAGAIN Insufficient entries in the routing cache
2039 @throws randolf::rex::xEALREADY Previous connection attempt not completed on
2040 nonblocking socket
2041 @throws randolf::rex::xEBADF The underlying socket is not open
2042 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
2043 connections
2044 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2045 part of the user address space
2046 @throws randolf::rex::xEINPROGRESS Connection cannot be completed immediately
2047 on nonblocking socket (@c AF_UNIX family fails with xEAGAIN instead)
2048 @throws randolf::rex::xEINTR Interrupted by a signal
2049 @throws randolf::rex::xEISCONN Socket is already connected
2050 @throws randolf::rex::xENETUNREACH No route to network
2051 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2052 doesn't refer to a socket
2053 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
2054 @throws randolf::rex::xEPERM Can't connect to a broadcast address when the
2055 socket's broadcast flag isn't enabled
2056 @throws randolf::rex::xEPROTOTYPE Socket type doesn't support the requested
2057 communications protocol (e.g., connecting a UNIX domain socket of
2058 type @c SOCK_STREAM to an endpoint expecting type @c SOCK_DGRAM)
2059 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
2060 @n@n
2061 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
2062 @throws randolf::rex::xEACCES Write permission or search permission denied
2063 on a component of the path prefix (@c AF_UNIX family)
2064 @throws randolf::rex::xEAGAIN Connection cannot be completed immediately on
2065 nonblocking socket (@c AF_UNIX family)
2066
2067 @returns The same rsocket object so as to facilitate stacking
2068 @see tls_do_handshake()
2069 @qualifier POSIX
2070 @qualifier TLS
2071 *///=========================================================================
2072 rsocket* connect(
2073 /// IPv4 address, IPv6 address, hostname, or UNIX domain sockets path
2074 const std::string address,
2075 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
2076 const int port = 0) {
2077 if (__debug) debug("connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2078 + ", " + address
2079 + " (port=" + std::to_string(port) + ")"
2080 + ", " + std::to_string(__socket_addr_size)
2081 + ");");
2082
2083 __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
2084 __rc_check(::connect(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size), 0, rex::rex::REX_FLAGS::REX_ALT_EAGAIN);
2085
2086// __rc_check(::connect(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
2087 __socket_connected = true;
2088
2089 // --------------------------------------------------------------------------
2090 // TLS.
2091 // --------------------------------------------------------------------------
2092 if (__tls) {
2093 if (__debug) debug("tls_connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2094 + ");");
2095 __rc_check_tls(SSL_connect(__tls_fd));
2096 } // -x- if __tls -x-
2097
2098 return this;
2099 }; // -x- rsocket* connect -x-
2100
2101 /*======================================================================*//**
2102 @brief
2103 Find out whether debug mode is enabled.
2104
2105 @par Threads
2106 This method is threadsafe.
2107 @returns TRUE = enabled
2108 @returns FALSE = not enabled
2109 @qualifier TLS
2110 *///=========================================================================
2111 const bool debug() noexcept { return __debug; };
2112
2113 /*======================================================================*//**
2114 @brief
2115 Debug mode. When debug mode is enabled, output is sent to stderr by default,
2116 unless a second parameter specifies a different file handle (e.g., stdout, or
2117 even a socket).
2118
2119 debug(...) returns -1 if fd can't be written to (errno will be set in
2120 accordance with std::fprintf's behaviour since std::fprintf is used for
2121 writing debug output). Normally we'd throw an exception for such errors, but
2122 with debug is a special case because debugging needs to be as quick and
2123 convenient as possible for developers.
2124
2125 debug(...) returns -2 if writing to stderr failed when attempting to announce
2126 that fd can't be written to (as described above).
2127
2128 debug(...) returns -3 if some other error occurs (e.g., internal formatting
2129 error {which should not happen} or memory allocation failed, in which case
2130 there's a more serious problem that will be causing other problems elsewhere
2131 anyway).
2132
2133 Developers may add their own debug messages by using debug(std::string),
2134 which will only be written out if debug mode is enabled. This same method is
2135 used internally, so messages will be indistinguishable from developer's
2136 messages.
2137
2138 @par Threads
2139 This method is thread-safe.
2140 @returns 0 = success
2141 @returns -1 = error writing to stream (errno will be set accordingly)
2142 @returns -2 = error writing to stderr (errno will be set accordingly)
2143 @returns -3 = error (errno will be set accordingly)
2144 @qualifier TLS
2145 *///=========================================================================
2146 const int debug(
2147 /// TRUE = enable debug mode@n
2148 /// FALSE = disable debug mode (does not close any file handles)
2149 const bool debug_flag,
2150 /// File descriptor/handle to use for debug output
2151 std::FILE* fd = stderr) noexcept {
2152
2153 int rc = debug_fd(fd); // Attempt to change debug handle
2154 if (rc != 0) return rc; // Return error id new debug handle failed
2155 time_t tm = std::time(nullptr);
2156 std::string dt(27, 0);
2157 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2158 try {
2159 if (debug_flag || __debug) std::fprintf(__debug_fd,
2160 "[%s] %s: Debug mode is %s\n",
2161 dt.c_str(),
2162 __debug_prefix.c_str(),
2163 debug_flag ? "ON" : "OFF");
2164 } catch (const std::system_error& e) { // Error writing to fd
2165 try {
2166 std::fprintf(stderr,
2167 "[%s] %s: error writing to stream\n",
2168 dt.c_str(),
2169 __debug_prefix.c_str());
2170 } catch (const std::exception& e) {
2171 return -2;
2172 }
2173 return -1;
2174 } catch (const std::exception& e) {
2175 return -3;
2176 }
2177 __debug = debug_flag; // Save debug flag
2178
2179 return 0; // Indicate success (no errors)
2180 }; // -x- int debug -x-
2181
2182 /*======================================================================*//**
2183 @brief
2184 Send the specified message as debug output (as long as debug mode is enabled;
2185 if disabled, no debug output will be sent).
2186 @returns 0 = success
2187 @returns -1 = error writing to stream (errno will be set accordingly)
2188 @returns -2 = error writing to stderr (errno will be set accordingly)
2189 @returns -3 = error (errno will be set accordingly)
2190 @qualifier TLS
2191 *///=========================================================================
2192 const int debug(
2193 /// Debug message as an ASCIIZ string
2194 const char* msg) noexcept {
2195 return __debug ? __debug_out(std::string(msg)) : 0;
2196 }; // -x- int debug -x-
2197
2198 /*======================================================================*//**
2199 @copydoc debug(const char*)
2200 @qualifier TLS
2201 *///=========================================================================
2202 const int debug(
2203 /// Debug message as an std::string object
2204 const std::string msg) noexcept {
2205 return __debug ? __debug_out(msg) : 0;
2206 }; // -x- int debug -x-
2207
2208 /*======================================================================*//**
2209 @brief
2210 Find out which file descriptor/handle is used for debug output.
2211 @returns file handle/descriptor currently used for debug output
2212 *///=========================================================================
2213 const std::FILE* debug_fd() noexcept { return __debug_fd; }; // Get debug-output file handle
2214
2215 /*======================================================================*//**
2216 @brief
2217 Specify a different file descriptor/handle to use for debug output
2218 @returns 0 = success
2219 @returns -1 = error writing to stream (errno will be set accordingly)
2220 @returns -2 = error writing to stderr (errno will be set accordingly)
2221 @returns -3 = error (errno will be set accordingly)
2222 *///=========================================================================
2223 const int debug_fd(
2224 /// File descriptor/handle to use for debug output
2225 std::FILE* fd) noexcept { // Set debug-output file handle
2226 if (__debug_fd != fd) {
2227 debug("Current debug-output file handle unset"); // For this message, we only want to send this out if debug mode is enabled
2228 __debug_fd = fd; // Save new debug-output file handle
2229 return debug("New debug-output file handle set"); // For this message, we only want to send this out if debug mode is enabled
2230 } // -x- if ~fd -x-
2231 return 0; // Indicate success (no errors)
2232 }; // -x- int debug_fd -x-
2233
2234 /*======================================================================*//**
2235 @brief
2236 Find out what the current prefix is set to that's used in debug output.
2237
2238 This may be set at any time, including before or after enabling or disabling
2239 debug mode.
2240 @returns debug prefix string
2241 @qualifier TLS
2242 *///=========================================================================
2243 const std::string debug_prefix() noexcept { return __debug_prefix; }; // -x- std::string debug_prefix -x-
2244
2245 /*======================================================================*//**
2246 @brief
2247 Change the prefix used in debug output.
2248 @returns The same rsocket object so as to facilitate stacking
2249 @qualifier TLS
2250 *///=========================================================================
2251 rsocket* debug_prefix(
2252 /// New debug prefix string
2253 const std::string prefix) noexcept {
2254 __debug_prefix = prefix;
2255 return this;
2256 }; // -x- std::string debug_prefix -x-
2257
2258 private:
2259 /*----------------------------------------------------------------------*//**
2260 @brief
2261 Internal debug-output function. This doesn't check __debug flag because it's
2262 expected that the normal debug() methods are taking care of this.
2263 @returns 0 = success
2264 @returns -1 = error writing to stream (errno will be set accordingly)
2265 @returns -2 = error writing to stderr (errno will be set accordingly)
2266 @returns -3 = error (errno will be set accordingly)
2267 *///-------------------------------------------------------------------------
2268 const int __debug_out(
2269 /// Debugging message
2270 const std::string msg) noexcept {
2271
2272 time_t tm = std::time(nullptr);
2273 std::string dt(27, 0);
2274 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2275 try {
2276 std::fprintf(__debug_fd,
2277 "[%s] %s: %s\n",
2278 dt.c_str(),
2279 __debug_prefix.c_str(),
2280 msg.c_str());
2281 } catch (const std::system_error& e) { // Error writing to fd
2282 try {
2283 std::fprintf(stderr,
2284 "[%s] %s: error writing to stream\n",
2285 dt.c_str(),
2286 __debug_prefix.c_str());
2287 } catch (const std::exception& e) {
2288 return -2;
2289 }
2290 return -1;
2291 } catch (const std::exception& e) {
2292 return -3;
2293 }
2294
2295 return 0; // Indicate success (no errors)
2296 }; // -x- int __debug_out -x-
2297
2298 // --------------------------------------------------------------------------
2299 // These are specialized internal debug output methods for presenting socket
2300 // options when they are get or set. These methods are marked as "private"
2301 // because they are really aren't useful outside of internal-use contexts.
2302 //
2303 // @par Threads
2304 // Some of these methods are thread-safe.
2305 //
2306 // These methods are thread-safe when they don't reference structures (e.g.,
2307 // integer {int}, unsigned integer {u_int}, and unsigned character {u_char}).
2308 //
2309 // These methods are not necessarily thread-safe if they reference structures
2310 // unless those structures are defined with std::atomic, or they were
2311 // constructed on-the-fly as anonymous parameters.
2312 // --------------------------------------------------------------------------
2313 const int __debug_sockopt(
2314 /// Name of function or method
2315 const std::string func,
2316 /// The level at which the option resides; typically @c SOL_SOCKET
2317 const int level,
2318 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2319 const int option,
2320 /// The structure that this socket option will be set to
2321 const int value) {
2322 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2323 + ", " + std::to_string(level)
2324 + ", " + std::to_string(option)
2325 + ", " + std::to_string(value)
2326 + ", size=" + std::to_string(sizeof(value))
2327 + ");");
2328 }; // -x- int __debug_sockopt -x-
2329 const int __debug_sockopt(
2330 /// Name of function or method
2331 const std::string func,
2332 /// The level at which the option resides; typically @c SOL_SOCKET
2333 const int level,
2334 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2335 const int option,
2336 /// The structure that this socket option will be set to
2337 const u_int value) {
2338 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2339 + ", " + std::to_string(level)
2340 + ", " + std::to_string(option)
2341 + ", u_int{" + std::to_string(value) + "}"
2342 + ", size=" + std::to_string(sizeof(value))
2343 + ");");
2344 }; // -x- int __debug_sockopt -x-
2345 const int __debug_sockopt(
2346 /// Name of function or method
2347 const std::string func,
2348 /// The level at which the option resides; typically @c SOL_SOCKET
2349 const int level,
2350 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2351 const int option,
2352 /// The structure that this socket option will be set to
2353 const u_char value) {
2354 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2355 + ", " + std::to_string(level)
2356 + ", " + std::to_string(option)
2357 + ", u_char{" + std::to_string(value) + "}"
2358 + ", size=" + std::to_string(sizeof(value))
2359 + ");");
2360 }; // -x- int __debug_sockopt -x-
2361 const int __debug_sockopt(
2362 /// Name of function or method
2363 const std::string func,
2364 /// The level at which the option resides; typically @c SOL_SOCKET
2365 const int level,
2366 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2367 const int option,
2368 /// The structure that this socket option will be set to
2369 const linger* value) {
2370 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2371 + ", " + std::to_string(level)
2372 + ", " + std::to_string(option)
2373 + ", linger{l_onoff=" + std::to_string(value->l_onoff)
2374 + ", l_linger=" + std::to_string(value->l_linger) + "}"
2375 + ", " + std::to_string(sizeof(value))
2376 + ");");
2377 }; // -x- int __debug_sockopt -x-
2378 const int __debug_sockopt(
2379 /// Name of function or method
2380 const std::string func,
2381 /// The level at which the option resides; typically @c SOL_SOCKET
2382 const int level,
2383 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2384 const int option,
2385 /// The structure that this socket option will be set to
2386 const timeval* value) {
2387 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2388 + ", " + std::to_string(level)
2389 + ", " + std::to_string(option)
2390 + ", timeval{tv_sec=" + std::to_string(value->tv_sec)
2391 + ", tv_usec=" + std::to_string(value->tv_usec) + "}"
2392 + ", " + std::to_string(sizeof(value))
2393 + ");");
2394 }; // -x- int __debug_sockopt -x-
2395 const int __debug_sockopt(
2396 /// Name of function or method
2397 const std::string func,
2398 /// The level at which the option resides; typically @c SOL_SOCKET
2399 const int level,
2400 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2401 const int option,
2402 /// The structure that this socket option will be set to
2403 const in_addr* value) {
2404 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2405 + ", " + std::to_string(level)
2406 + ", " + std::to_string(option)
2407 + ", in_addr{" + std::to_string(value->s_addr) + "}"
2408 + ", " + std::to_string(sizeof(value))
2409 + ");");
2410 }; // -x- int __debug_sockopt -x-
2411 const int __debug_sockopt(
2412 /// Name of function or method
2413 const std::string func,
2414 /// The level at which the option resides; typically @c SOL_SOCKET
2415 const int level,
2416 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2417 const int option,
2418 /// The structure that this socket option will be set to
2419 const ip_mreq* value) {
2420 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2421 + ", " + std::to_string(level)
2422 + ", " + std::to_string(option)
2423 + ", ip_mreq{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2424 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2425 + ", " + std::to_string(sizeof(value))
2426 + ");");
2427 }; // -x- int __debug_sockopt -x-
2428 const int __debug_sockopt(
2429 /// Name of function or method
2430 const std::string func,
2431 /// The level at which the option resides; typically @c SOL_SOCKET
2432 const int level,
2433 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2434 const int option,
2435 /// The structure that this socket option will be set to
2436 const ip_mreq_source* value) {
2437 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2438 + ", " + std::to_string(level)
2439 + ", " + std::to_string(option)
2440 + ", ip_mreq_source{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2441 + ", source=" + std::to_string(value->imr_sourceaddr.s_addr)
2442 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2443 + ", " + std::to_string(sizeof(value))
2444 + ");");
2445 }; // -x- int __debug_sockopt -x-
2446 const int __debug_sockopt(
2447 /// Name of function or method
2448 const std::string func,
2449 /// The level at which the option resides; typically @c SOL_SOCKET
2450 const int level,
2451 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2452 const int option,
2453 /// The structure that this socket option will be set to
2454 const icmp6_filter* value) {
2455 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2456 + ", " + std::to_string(level)
2457 + ", " + std::to_string(option)
2458 + ", icmp6_filter{[0]=" + std::to_string(value->icmp6_filt[0])
2459 + ", [1]=" + std::to_string(value->icmp6_filt[1])
2460 + ", [2]=" + std::to_string(value->icmp6_filt[2])
2461 + ", [3]=" + std::to_string(value->icmp6_filt[3])
2462 + ", [4]=" + std::to_string(value->icmp6_filt[4])
2463 + ", [5]=" + std::to_string(value->icmp6_filt[5])
2464 + ", [6]=" + std::to_string(value->icmp6_filt[6])
2465 + ", [7]=" + std::to_string(value->icmp6_filt[7]) + "}"
2466 + ", " + std::to_string(sizeof(value))
2467 + ");");
2468 }; // -x- int __debug_sockopt -x-
2469 const int __debug_sockopt(
2470 /// Name of function or method
2471 const std::string func,
2472 /// The level at which the option resides; typically @c SOL_SOCKET
2473 const int level,
2474 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2475 const int option,
2476 /// The structure that this socket option will be set to
2477 const sockaddr_in6* value) {
2478 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2479 + ", " + std::to_string(level)
2480 + ", " + std::to_string(option)
2481 + ", sockaddr_in6{family=" + std::to_string(value->sin6_family)
2482 + ", port=" + std::to_string(value->sin6_port)
2483 + ", flowinfo=" + std::to_string(value->sin6_flowinfo)
2484 + ", addr=" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[0])
2485 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[1])
2486 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[2])
2487 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[3])
2488 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[4])
2489 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[5])
2490 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[6])
2491 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[7])
2492 + ", scope_id=" + std::to_string(value->sin6_scope_id) + "}"
2493 + ", " + std::to_string(sizeof(value))
2494 + ");");
2495 }; // -x- int __debug_sockopt -x-
2496 const int __debug_sockopt(
2497 /// Name of function or method
2498 const std::string func,
2499 /// The level at which the option resides; typically @c SOL_SOCKET
2500 const int level,
2501 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2502 const int option,
2503 /// The structure that this socket option will be set to
2504 const ip6_mtuinfo* value) {
2505 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2506 + ", " + std::to_string(level)
2507 + ", " + std::to_string(option)
2508 + ", ip6_mtuinfo{addr=" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[0])
2509 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[1])
2510 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[2])
2511 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[3])
2512 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[4])
2513 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[5])
2514 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[6])
2515 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[7])
2516 + ", mtu=" + std::to_string(value->ip6m_mtu) + "}"
2517 + ", " + std::to_string(sizeof(value))
2518 + ");");
2519 }; // -x- int __debug_sockopt -x-
2520 const int __debug_sockopt(
2521 /// Name of function or method
2522 const std::string func,
2523 /// The level at which the option resides; typically @c SOL_SOCKET
2524 const int level,
2525 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2526 const int option,
2527 /// The structure that this socket option will be set to
2528 const ipv6_mreq* value) {
2529 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2530 + ", " + std::to_string(level)
2531 + ", " + std::to_string(option)
2532 + ", ipv6_mreq{addr=" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[0])
2533 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[1])
2534 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[2])
2535 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[3])
2536 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[4])
2537 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[5])
2538 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[6])
2539 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[7])
2540 + ", iface=" + std::to_string(value->ipv6mr_interface) + "}"
2541 + ", " + std::to_string(sizeof(value))
2542 + ");");
2543 }; // -x- int __debug_sockopt -x-
2544 const int __debug_sockopt(
2545 /// Name of function or method
2546 const std::string func,
2547 /// The level at which the option resides; typically @c SOL_SOCKET
2548 const int level,
2549 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2550 const int option,
2551 /// The structure that this socket option will be set to
2552 const group_req* value) {
2553 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2554 + ", " + std::to_string(level)
2555 + ", " + std::to_string(option)
2556 + ", group_req{iface=" + std::to_string(value->gr_interface)
2557 + ", <group_req>}"
2558 + ", " + std::to_string(sizeof(value))
2559 + ");");
2560 }; // -x- int __debug_sockopt -x-
2561 const int __debug_sockopt(
2562 /// Name of function or method
2563 const std::string func,
2564 /// The level at which the option resides; typically @c SOL_SOCKET
2565 const int level,
2566 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2567 const int option,
2568 /// The structure that this socket option will be set to
2569 const group_source_req* value) {
2570 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2571 + ", " + std::to_string(level)
2572 + ", " + std::to_string(option)
2573 + ", group_source_req{iface=" + std::to_string(value->gsr_interface)
2574 + ", <group_source_req>}"
2575 + ", " + std::to_string(sizeof(value))
2576 + ");");
2577 }; // -x- int __debug_sockopt -x-
2578 const int __debug_sockopt_other(
2579 /// Name of function or method
2580 const std::string func,
2581 /// The level at which the option resides; typically @c SOL_SOCKET
2582 const int level,
2583 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2584 const int option,
2585 /// The structure that this socket option will be set to
2586 const socklen_t size) {
2587 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2588 + ", " + std::to_string(level)
2589 + ", " + std::to_string(option)
2590 + ", other=?"
2591 + ", " + std::to_string(size)
2592 + ");");
2593 }; // -x- int __debug_sockopt_other -x-
2594
2595 public:
2596 /*======================================================================*//**
2597 @brief
2598 Discards the specified number of 8-bit bytes efficiently, and without closing
2599 the stream, and without consuming excessive quantities of memory.
2600 @exception randolf::rex::xERANGE An invalid value was specified for either
2601 the @c nbytes or @c memory_size parameter (e.g., a negative value,
2602 except for -1 with the @c nbytes parameter)
2603 @returns Number of bytes that were successfully discarded
2604 @see buffer_size
2605 @see recv
2606 @qualifier TLS
2607 *///=========================================================================
2608 int discard(
2609 /// Number of bytes to discard@n
2610 /// 0 = use internal @ref buffer_size() @n
2611 /// -1 = all remaining data waiting to be received (in other words, this
2612 /// method will repeatedly consume all data until encountering @ref eos)
2613 int nbytes,
2614 /// MSG_OOB@n
2615 /// MSG_PEEK (ignored, to prevent an endless loop)@n
2616 /// MSG_WAITALL@n
2617 /// MSG_DONTWAIT@n
2618 /// MSG_CMSG_CLOEXEC
2619 int posix_flags = 0,
2620 /// Maximum internal read size@n
2621 /// 0 = default to @ref buffer_size (when @c nbytes is larger than this size,
2622 /// multiple reads into this buffer will be utilized behind-the-scenes to
2623 /// consume the quantity of data specified by the @c nbytes parameter)
2624 const size_t memory_size = 0) {
2625 int buf_size = memory_size != 0 ? memory_size : __buffer_size;
2626 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2627 + ", " + std::to_string(nbytes)
2628 + ", " + std::to_string(posix_flags)
2629 + ", " + std::to_string(buf_size)
2630 + ");");
2631
2632 // --------------------------------------------------------------------------
2633 // Syntax checks.
2634 // --------------------------------------------------------------------------
2635 if (nbytes == 0) nbytes = __buffer_size;
2636 else if (nbytes < -1) throw randolf::rex::xERANGE( "nbytes parameter of " + std::to_string(nbytes) + " is below -1");
2637 if (memory_size < 0) throw randolf::rex::xERANGE("memory_size parameter of " + std::to_string(memory_size) + " is below 0");
2638
2639 // --------------------------------------------------------------------------
2640 // The MSG_PEEK flag is incompatible with this method, so we're turning it
2641 // off to prevent an endless loop. We're providing a debug message for this
2642 // in case someone's trying to track something down where they're using the
2643 // MSG_PEEK flag with a misunderstanding of the purpose of this method.
2644 // --------------------------------------------------------------------------
2645 if (posix_flags & MSG_PEEK) {
2646 posix_flags &= (~MSG_PEEK); // Remove MSG_PEEK flag if it was enabled
2647 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2648 + " ignoring incompatible MSG_PEEK flag"
2649 + ");");
2650 } // -x- if MSG_PEEK -x-
2651
2652 // --------------------------------------------------------------------------
2653 // Internal variables.
2654 // --------------------------------------------------------------------------
2655 int discarded = 0; // Total number of bytes discarded
2656 std::vector<char> buf(buf_size);
2657 buf.reserve(buf_size); // Pre-allocate underlying array to prevent memory corruption when __recv writes directly into it
2658
2659 // --------------------------------------------------------------------------
2660 // Discard loop. We are intentionally handling nbytes in two different ways
2661 // hear with a little bit more code to benefit from the trade-off of faster
2662 // overall performance.
2663 // --------------------------------------------------------------------------
2664 try {
2665
2666 // --------------------------------------------------------------------------
2667 // Discard all remaining data waiting to be received.
2668 // --------------------------------------------------------------------------
2669 if (nbytes < 0) {
2670 int n = 0;
2671 while (!eos()) {
2672 n = __recv(buf.data(), buf_size, posix_flags);
2673 discarded += n;
2674 }; // -x- while n -x-
2675
2676 // --------------------------------------------------------------------------
2677 // Discard specific amount of data waiting to be received.
2678 // --------------------------------------------------------------------------
2679 } else {
2680 int n = 0;
2681 int inbytes = nbytes;
2682 while (inbytes > 0 && !eos()) {
2683 int n = __recv(buf.data(), std::min(inbytes, buf_size), posix_flags);
2684 inbytes -= n;
2685 discarded += n;
2686 } // -x- while nbytes -x-
2687 } // -x- if nbytes -x-
2688
2689 // --------------------------------------------------------------------------
2690 // Ignore exception so that we can simply indicate how many bytes were
2691 // successfully received and discarded.
2692 // --------------------------------------------------------------------------
2693 } catch(const rex::xALL e) {}
2694
2695 // --------------------------------------------------------------------------
2696 // Erase all data to prevent data from being unexpectedly leaked to any other
2697 // memory allocations later that use some or all of the same memory.
2698 // --------------------------------------------------------------------------
2699 for (int i = nbytes <= 0 ? buf_size : std::min(nbytes, buf_size); i > 0; i--) {
2700 buf[i] = '\0';
2701 } // -x- for i -x-
2702
2703 return discarded;
2704 }; // -x- int discard -x-
2705
2706 /*======================================================================*//**
2707 @brief
2708 Discards the specified number of 8-bit bytes efficiently, but stops upon
2709 encountering an EoL sequence (which will also be discarded; to find out which
2710 EoL sequence was consumed, use the @ref eol_consumed_seq method which will
2711 also return an empty string if no EoL sequence was consumed), and without
2712 closing the stream, and without consuming excessive quantities of memory.
2713
2714 This method is particularly useful for discarding lines that are too long,
2715 which, for example, makes it possible to ignore lines that are too long (and
2716 optionally present or log an error) and continue reading the next line(s)
2717 that follow.
2718
2719 @exception randolf::rex::xERANGE An invalid value was specified for either
2720 the @c nbytes or @c memory_size parameter (e.g., a negative value,
2721 except for -1 with the @c nbytes parameter)
2722 @returns Number of bytes that were successfully discarded
2723 @see buffer_size
2724 @see recv
2725 @qualifier TLS
2726 *///=========================================================================
2727 int discard_line(
2728 /// Number of bytes to discard@n
2729 /// 0 = use internal @ref buffer_size() @n
2730 /// -1 = all remaining data waiting to be received (in other words, this
2731 /// method will repeatedly consume all data until encountering @ref eos)
2732 int nbytes = -1,
2733 /// MSG_OOB@n
2734 /// MSG_PEEK (ignored, to prevent an endless loop)@n
2735 /// MSG_WAITALL@n
2736 /// MSG_DONTWAIT@n
2737 /// MSG_CMSG_CLOEXEC
2738 int posix_flags = 0,
2739 /// Line timeout (in seconds)@n
2740 /// 0 = no timeout (default), unless it was configured by way of the
2741 /// @ref timeout_recvline(long) method
2742 long timeout = 0,
2743 /// Maximum internal read size@n
2744 /// 0 = default to @ref buffer_size (when @c nbytes is larger than this size,
2745 /// multiple reads into this buffer will be utilized behind-the-scenes to
2746 /// consume the quantity of data specified by the @c nbytes parameter)
2747 const size_t memory_size = 0) {
2748 int buf_size = memory_size != 0 ? memory_size : __buffer_size;
2749 if (__debug) debug("discard_line(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2750 + ", " + std::to_string(nbytes)
2751 + ", " + std::to_string(posix_flags)
2752 + ", " + std::to_string(buf_size)
2753 + ");");
2754
2755 // --------------------------------------------------------------------------
2756 // Syntax checks.
2757 // --------------------------------------------------------------------------
2758 bool infinite = nbytes == -1; // Keep track of whether nbytes was -1 before changing its value
2759 if (nbytes == 0) nbytes = __buffer_size;
2760 else if (nbytes == -1) nbytes = INT_MAX;
2761 else if (nbytes < -1) throw randolf::rex::xERANGE( "nbytes parameter of " + std::to_string(nbytes) + " is below -1");
2762 if (memory_size < 0) throw randolf::rex::xERANGE("memory_size parameter of " + std::to_string(memory_size) + " is below 0");
2763
2764 // --------------------------------------------------------------------------
2765 // Syntax checks as part of preparation of timeout variable for faster
2766 // comparisons within line-reading loop.
2767 // --------------------------------------------------------------------------
2768 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
2769 if (timeout == 0) timeout = __recvline_timeout;
2770 timeout = timeout == 0 ? LONG_MAX : timeout + time(0);
2771
2772 // --------------------------------------------------------------------------
2773 // The MSG_PEEK flag is incompatible with this method, so we're turning it
2774 // off to prevent an endless loop. We're providing a debug message for this
2775 // in case someone's trying to track something down where they're using the
2776 // MSG_PEEK flag with a misunderstanding of the purpose of this method.
2777 // --------------------------------------------------------------------------
2778 if (posix_flags & MSG_PEEK) {
2779 posix_flags &= (~MSG_PEEK); // Remove MSG_PEEK flag if it was enabled
2780 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2781 + " ignoring incompatible MSG_PEEK flag"
2782 + ");");
2783 } // -x- if MSG_PEEK -x-
2784
2785 // --------------------------------------------------------------------------
2786 // Internal variables.
2787 // --------------------------------------------------------------------------
2788 int discarded = 0; // Total number of bytes discarded
2789 std::string buf;
2790 int buf_size_eol = buf_size + 1;
2791 buf.reserve(buf_size_eol); // Pre-allocate underlying array to prevent memory corruption when __recv writes directly into it, with +1 for straddled EoL
2792// char straddle_eol = 0;
2793
2794 // --------------------------------------------------------------------------
2795 // Discard loop. We are intentionally handling nbytes in two different ways
2796 // hear with a little bit more code to benefit from the trade-off of faster
2797 // overall performance.
2798 // --------------------------------------------------------------------------
2799 try {
2800
2801 // --------------------------------------------------------------------------
2802 // Discard all remaining data waiting to be received.
2803 // --------------------------------------------------------------------------
2804 if (infinite) {
2805 int n = 0;
2806 while (!eos()) {
2807 n = __recv(buf.data(), buf_size_eol, posix_flags | MSG_PEEK);
2808
2809 // --------------------------------------------------------------------------
2810 // Affect an actual discard of the correct amount of data. If "len" is -1,
2811 // then just discard everything because it means that we didn't encounter an
2812 // EoL sequence in this iteration.
2813 // --------------------------------------------------------------------------
2814 int len_with_eol = 0;
2815 int len = eol_index(buf, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
2816std::cout << "///// n=" << n << " len=" << len << " len_with_eol=" << len_with_eol << " buf_size_eol=" << buf_size_eol << std::endl;
2817 n = __recv(buf.data(), len > 0 ? len : n, posix_flags); // Consume data
2818std::cout << " n=" << n << std::endl;
2819 discarded += n;
2820 } // -x- while n -x-
2821
2822 // --------------------------------------------------------------------------
2823 // Discard specific amount of data waiting to be received.
2824 // --------------------------------------------------------------------------
2825 } else {
2826 int n = 0;
2827 int inbytes = nbytes;
2828 while (inbytes > 0 && !eos()) {
2829 n = __recv(buf.data(), std::min(inbytes + 1, buf_size_eol), posix_flags | MSG_PEEK);
2830
2831 // --------------------------------------------------------------------------
2832 // Affect an actual discard of the correct amount of data. If "len" is -1,
2833 // then just discard everything because it means that we didn't encounter an
2834 // EoL sequence in this iteration.
2835 // --------------------------------------------------------------------------
2836 int len_with_eol = 0;
2837 int len = eol_index(buf, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
2838std::cout << "||||| n=" << n << " len=" << len << " len_with_eol=" << len_with_eol << " buf_size_eol=" << buf_size_eol << std::endl;
2839 n = __recv(buf.data(), len >= 0 ? len : n, posix_flags); // Consume data
2840std::cout << " n=" << n << std::endl;
2841 inbytes -= n;
2842 discarded += n;
2843 } // -x- while nbytes -x-
2844 } // -x- if nbytes -x-
2845
2846 // --------------------------------------------------------------------------
2847 // Ignore exception so that we can simply indicate how many bytes were
2848 // successfully received and discarded.
2849 // --------------------------------------------------------------------------
2850 } catch(const rex::xALL e) {
2851std::cout << "Exception: " << e.what() << std::endl;
2852 }
2853
2854std::cout << "discarded=" << discarded << std::endl;
2855
2856 // --------------------------------------------------------------------------
2857 // Erase all data to prevent data from being unexpectedly leaked to any other
2858 // memory allocations later that use some or all of the same memory.
2859 //
2860 // Note: The +1 in the buffer size calcuation is to include a straddled EoL.
2861 // --------------------------------------------------------------------------
2862 for (int i = infinite ? buf_size : std::min(nbytes + 1, buf_size_eol); i > 0; i--) {
2863 buf[i] = '\0';
2864 } // -x- for i -x-
2865
2866 return discarded;
2867 }; // -x- int discard_line -x-
2868
2869 /*======================================================================*//**
2870 @brief
2871 Find out what the current EoL (End of Line) sequence is set to.
2872 @warning
2873 To send an EoL sequence do not use `send(r.eol())` because it may not be
2874 initialized yet and the endpoint you're sending to may seem unresponsive or
2875 other unexpected behaviour may occur.@n
2876 @n
2877 To send an EoL sequence properly, use @ref sendline(); although specifying no
2878 parameters is more efficient than specifying an empty string (@c ""), the
2879 specialized @ref send_eol() method is the most efficient option for sending
2880 an EoL sequence separately.
2881 @note
2882 An empty EoL sequence means that CRLF will be sent by @ref sendline(), and
2883 that @ref recvline(), and @ref recv_rline() will automatically detect from
2884 one of @c CR, @c CRLF, @c LF, and @c LFCR.
2885 @returns Current EoL sequence
2886 @see eol_adoption
2887 @see eol_fix_printf
2888 @see printfline
2889 @see recvline
2890 @see recv_rline
2891 @see sendline
2892 @see send_eol
2893 @see vprintfline
2894 @qualifier TLS
2895 *///=========================================================================
2896 const std::string eol() noexcept { return __eol; }; // -x- std::string eol -x-
2897
2898 /*======================================================================*//**
2899 @brief
2900 Set EoL (End of Line) sequence. This sequence is used by recvline(),
2901 recv_rline(), sendline(), and related functions, and it defaults to an empty
2902 string which results in the EoL sequence being detected automatically
2903 on-the-fly.
2904
2905 - @c "" (empty string) = automatically detect on-the-fly (default)
2906 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
2907 - @c \\r (CR) = Carriage Return (typical for MacOS)
2908 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
2909
2910 @note
2911 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
2912 IMAP4, FINGER, NICNAME/WHOIS, etc.).
2913 @returns The same rsocket object so as to facilitate stacking
2914 @see eol_adoption
2915 @see eol_fix_printf
2916 @see printfline
2917 @see vprintfline
2918 @see recv_rline
2919 @see recvline
2920 @see sendline
2921 @see send_eol
2922 @qualifier TLS
2923 *///=========================================================================
2924 rsocket* eol(
2925 /// EoL sequence as an ASCIIZ string
2926 const char* eol) noexcept {
2927 __eol = std::string(eol);
2928 __eol_out = __eol.empty() ? __CRLF : __eol;
2929 return this;
2930 }; // -x- rsocket* eol -x-
2931
2932 /*======================================================================*//**
2933 @brief
2934 Set EoL (End of Line) sequence. This sequence is used by recvline(),
2935 recv_rline(), sendline(), and related functions, and it defaults to an empty
2936 string which results in the EoL sequence being detected automatically
2937 on-the-fly.
2938
2939 - @c "" (empty string) = automatically detect on-the-fly (default)
2940 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
2941 - @c \\r (CR) = Carriage Return (typical for MacOS)
2942 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
2943
2944 @note
2945 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
2946 IMAP4, FINGER, NICNAME/WHOIS, etc.).
2947 @returns The same rsocket object so as to facilitate stacking
2948 @see eol_fix_printf
2949 @see printfline
2950 @see vprintfline
2951 @see recv_rline
2952 @see recvline
2953 @see sendline
2954 @see send_eol
2955 @qualifier TLS
2956 *///=========================================================================
2957 rsocket* eol(
2958 /// EoL sequence as an std::string object
2959 const std::string eol) noexcept {
2960 __eol = eol;
2961 return this;
2962 }; // -x- rsocket* eol -x-
2963
2964 /*======================================================================*//**
2965 @brief
2966 Configure EoL adoption policy for the @ref recvline() and @ref recv_rline()
2967 methods. By default, @ref rsocket is configured with the EoL adoption policy
2968 enabled alongside an empty @ref eol() sequence, which results in the default
2969 operation being that the EoL sequence automatically gets detected and updated
2970 internally upon the first use of either the @ref recvline() or
2971 @ref recv_rline method.
2972
2973 The EoL adoption policy is only effective when the @ref eol() sequence is not
2974 defined (which is indicated by an empty EoL sequence string).
2975
2976 The EoL sequence is updated only when the EoL sequence string is empty, and
2977 when this EoL adoption policy is enabled.
2978 @returns The same rsocket object so as to facilitate stacking
2979 @see eol
2980 @see eol_index
2981 @see is_eol_adoption
2982 @see recv_rline
2983 @see recvline
2984 @see sendline
2985 @see send_eol
2986 @qualifier TLS
2987 *///=========================================================================
2988 rsocket* eol_adoption(
2989 /// TRUE = enable EoL adoption (default)@n
2990 /// FALSE = disable EoL adoption
2991 const bool flag) noexcept {
2992 __eol_adoption = flag;
2993 return this;
2994 }; // -x- rsocket* eol_adoption -x-
2995
2996 /*======================================================================*//**
2997 @brief
2998 Returns a String containing the EoL character sequence that was consumed by
2999 the most recent successful call to the @ref recvline() or @ref recv_rline
3000 method ("successful" in this context means that the received line was
3001 terminated by a valid EoL character sequence; otherwise the
3002 previous/unmodified value is returned).
3003 @warning
3004 This method must not be used to determine whether the @ref recvline() method
3005 successfully consumed an EoL character sequence.&nbsp; The reason for this is
3006 that @ref recvline() doesn't update this string when it doesn't consume an
3007 EoL character sequence, hence interpreting its results can (and likely will)
3008 lead to false positives.
3009 @returns EoL character sequence
3010 @see eol
3011 @see eol_adoption
3012 @see recv_rline
3013 @see recvline
3014 @qualifier TLS
3015 *///=========================================================================
3016 const std::string eol_consumed_seq() noexcept { return __eol_consumed_seq; }; // -x- std::string eol_consumed_seq -x-
3017
3018 /*======================================================================*//**
3019 @brief
3020 Configure EoL substitution policy for the @ref printf(), @ref printfline(),
3021 @ref vprintf(), and @ref vprintfline() methods. By default, @ref rsocket
3022 substitutes printf's @c \\n sequence with the EoL sequence (if defined), but
3023 this method can be used to disable this behaviour.
3024 @note
3025 The @c \\n sequence used in the printf format string normally coincides with
3026 the local Operating System's newline standard, which is very likely different
3027 from the @ref rsocket endpoint's newline standard, and/or the newline
3028 standard of the protocol being implemented. This policy setting makes it
3029 possible to control whether to use the configured EoL sequence when sending a
3030 formatted string to the endpoint.
3031 @returns The same rsocket object so as to facilitate stacking
3032 @see eol
3033 @see is_eol_fix_printf
3034 @see printf
3035 @see printfline
3036 @see vprintf
3037 @see vprintfline
3038 @qualifier TLS
3039 *///=========================================================================
3040 rsocket* eol_fix_printf(
3041 /// TRUE = enable EoL substitution (default)@n
3042 /// FALSE = disable EoL substitution
3043 const bool flag) noexcept {
3044 __eol_fix_printf = flag;
3045 return this;
3046 }; // -x- rsocket* eol_fix_printf -x-
3047
3048 /*======================================================================*//**
3049 @brief
3050 Finds the first instance of the EoL sequence and returns its offset (which is
3051 effectively the same as the size of the text, not including the characters
3052 that the EoL sequence is comprised of).
3053
3054 @note
3055 This method is specialized primarily for internal use by the @ref recvline()
3056 and @ref recv_rline() methods, but is made available here in case there's a
3057 need to check in-memory text using this rsocket's EoL detection policy.
3058 @returns Size of EoL sequence
3059 @returns -1 if EoL sequence wasn't found
3060 @see eol
3061 @see eol_adoption
3062 @qualifier TLS
3063 *///=========================================================================
3064 const int eol_index(
3065 /// Buffer that probably contains at least one EoL sequence
3066 const std::string buffer,
3067 /// Size of string with EoL character sequence included (will be updated by this method)
3068 int* with_eol_size) noexcept {
3069
3070 // --------------------------------------------------------------------------
3071 // An EoL character sequence was specified, a simple search will suffice.
3072 // --------------------------------------------------------------------------
3073 if (!__eol.empty()) {
3074//std::cout << "!__eol.empty() ------------------------------ " << randolf::rtools::to_hex(__eol) << std::endl;
3075 int pos = buffer.find(__eol);
3076 if (pos >= 0) {
3077 *with_eol_size = pos + __eol.size(); // Update size of EoL sequence
3078//std::cout << "------0------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3079 //return pos;
3080 } // -x- if pos -x-
3081//std::cout << "------1------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3082 if (!__eol_adoption) return pos; // Return the -1 error if we're not adopting different EoF sequences on-the-fly
3083 } // -x- if !__eol.empty() -x-
3084//std::cout << "__eol.empty() ------------------------------ String is empty" << std::endl;
3085
3086 // --------------------------------------------------------------------------
3087 // Automatic detection of EoL sequence, so a more flexible approach will be
3088 // required.
3089 //
3090 // Search for all four possible EoL sequences (as indicated by an empty EoL
3091 // character sequence string):
3092 //
3093 // CRLF: Common for text lines with internet protocols such as SMTP, POP3,
3094 // IMAP4, HTTP, FINGER, WHOIS, etc. Also: DOS and OS/2 EoL sequence.
3095 //
3096 // CR: MacOS EoL character.
3097 //
3098 // LF: UNIX/Linux EoL character.
3099 //
3100 // LFCR: Extremely rare, but I've encountered multiple instances where the
3101 // intended CRLF was reversed to LFCR, possibly due to a programming
3102 // error or an artifact of inappropriate/unintended endian conversion.
3103 // --------------------------------------------------------------------------
3104 int pos = buffer.find(__CR); // CR or CRLF
3105//std::cout << "------2------ <CR> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3106 if (pos == (buffer.size() - 1)) { // EoL sequence not found (this handles a specific empty-buffer edge case caused by SSL background chatter that's very rare but causes problems that are nearly impossible to debug when they do occur)
3107 *with_eol_size = pos;// + 1;
3108// *with_eol_size = 1;
3109//std::cout << "------3------ <!CR> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3110 return pos;
3111 } else if (pos >= 0) { // EoL sequence found
3112 *with_eol_size = pos + (buffer[pos + 1] == __LF ? 2 : 1); // 2=CRLF, 1=CR
3113// *with_eol_size = buffer[pos + 1] == __LF ? 2 : 1; // 2=CRLF, 1=CR
3114 if (__eol_adoption) {
3115 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
3116 __eol.resize(*with_eol_size - pos); // Truncate extra characters
3117 } // -x- if __eol_adoption -x-
3118//std::cout << "------4------ <CR/CRLF-adopted> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3119 return pos; // Either way, we're done
3120 } // -x- if pos -x-
3121
3122 pos = buffer.find(__LF); // LF or LFCR
3123 if (pos == (buffer.size() - 1)) { // EoL sequence not found (this handles a specific empty-buffer edge case caused by SSL background chatter that's very rare but causes problems that are nearly impossible to debug when they do occur)
3124 *with_eol_size = pos;// + 1;
3125// *with_eol_size = 1;
3126//std::cout << "------5------ <LF> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3127 return pos;
3128 } else if (pos >= 0) { // EoL sequence found
3129 *with_eol_size = pos + (buffer[pos + 1] == __CR ? 2 : 1); // 2=LFCR, 1=LF
3130// *with_eol_size = buffer[pos + 1] == __CR ? 2 : 1; // 2=LFCR, 1=LF
3131 if (__eol_adoption) {
3132 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
3133 __eol.resize(*with_eol_size - pos); // Truncate extra characters
3134 } // -x- if __eol_adoption -x-
3135//std::cout << "------6------ <LF/LFCR-adopted> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3136 return pos; // Either way, we're done
3137 } // -x- if pos -x-
3138
3139//std::cout << "------7------ <LF> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3140 return pos;
3141 }; // -x- int eol_index -x-
3142
3143 /*======================================================================*//**
3144 @brief
3145 Find out if the stream is at its end and that this @ref rsocket's internal
3146 buffer (if one had been set up by the @ref recvline() method) is empty. This
3147 doesn't necessarily mean that the stream is closed; but rather that the
3148 endpoint just hasn't sent any more data (yet).
3149
3150 If the stream isn't open, then this method will always return @c true to
3151 implicitly indicate that there's no data pending to be received.
3152
3153 @pre
3154 You can optionally specify a timeout with the timeout parameter, which will
3155 cause this method to wait for the specified period of time for pending data.
3156
3157 It's better (more efficient) to use this method instead of the @c MSG_PEEK
3158 flag (with the various @c recv methods) to determine whether any data is
3159 waiting on the stream (e.g., data that's received by the sockets, but not by
3160 any @c recv methods yet) because this method is specialized in handling this
3161 particular condition and responds with an easy-to-use boolean flag.
3162
3163 @note
3164 EoS is an acronym for: End of Stream
3165
3166 @throws randolf::rex::xEBADF The underlying socket is not open
3167 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3168 part of the user address space
3169 @throws randolf::rex::xEINTR Interrupted by a signal
3170 @throws randolf::rex::xENOMEM Insufficient memory
3171 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3172 doesn't refer to a socket
3173 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
3174 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
3175 is a highly improbable chance that a timeout could still occur if the
3176 data is read by another thread before the `recv(..., MSG_PEEK)` call)
3177 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
3178 there's no new data
3179
3180 @returns TRUE = End of Stream (no buffered data, the stream @ref is_closed(),
3181 or there's no pending data, checked in this order)
3182 @returns FALSE = Pending data available
3183 @see is_closed
3184 @see is_open
3185 @see recv
3186 @qualifier TLS
3187 *///=========================================================================
3188 const bool eos(
3189 /// Number of milliseconds to wait
3190 const int timeout = 0) {
3191
3192 // --------------------------------------------------------------------------
3193 // Check internal buffer first, if there is one, and return immediately if
3194 // the buffer is not empty.
3195 // --------------------------------------------------------------------------
3196 if (__buffer != nullptr && !__buffer->empty()) return false;
3197
3198 // --------------------------------------------------------------------------
3199 // If socket is closed, then just indicate EoS to be done with it. No need
3200 // to fall through to timeouts as this is not applicable.
3201 // --------------------------------------------------------------------------
3202 if (!__socket_open) return true;
3203
3204 // --------------------------------------------------------------------------
3205 // Start by checking whether OpenSSL has pending data, and if it doesn't (or
3206 // there's an error, which is also indicated by 0) then fall through to
3207 // checking the underlying socket using ioctl's FIONREAD...
3208 //
3209 // Use ioctl's FIONREAD to determine if any data is waiting to be read. If
3210 // ioctl returns non-zero, then an error occurred which always equates to
3211 // EoS (otherwise the FIONREAD operation would have been successful).
3212 // --------------------------------------------------------------------------
3213 if (__tls && SSL_has_pending(__tls_fd) == 1) return false;
3214 int n = 0; // Number of bytes waiting (default to 0 in case of ioctl error)
3215 ioctl(__socket_fd, FIONREAD, &n); // Ignore return code because "n" will be zero if there's an error
3216 if (n > 0) return false; // 0 bytes remaining means EoS
3217
3218 // --------------------------------------------------------------------------
3219 // Timeout polling (if a timeout was specified).
3220 // --------------------------------------------------------------------------
3221 if (timeout != 0) {
3222 try {
3223 poll(POLLIN, timeout);
3224 return false; // EoS, because the "poll" method was triggered by new data
3225 } catch (const randolf::rex::xETIMEDOUT e) {
3226 return true; // EoS, because the "poll" method timed out while waiting for new data
3227 } // This is a specialized catch situation -- the ::recv function could still return an ETIMEDOUT error
3228 } // -x- if timeout -x-
3229
3230 return true; // EoS, because new data was not detected
3231 }; // -x- bool eos -x-
3232
3233 /*======================================================================*//**
3234 @brief
3235 Find out what the specified IP address string's family (@c SO_DOMAIN) is.
3236 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
3237 address is not available on any local interface
3238 @throws randolf::rex::xEAI_NONAME If @c address is an empty string
3239 @returns @c AF_UNSPEC (if family couldn't be determined)
3240 @returns @c AF_INET (IPv4 address)
3241 @returns @c AF_INET6 (IPv6 address)
3242 @returns @c AF_UNIX (UNIX Domain address)
3243 @returns ...or other family as applicable
3244 @qualifier TLS
3245 *///=========================================================================
3246 int static family(
3247 /// Address, similar to @ref bind() addressing, including non-standard "if="
3248 /// variant that names a network interface
3249 const std::string address,
3250 /// Preferred family to return first (used only with interface mode where the
3251 /// network interface is specified after the "if=" prefix); the default value
3252 /// of @c AF_UNSPEC will return the first family interface found
3253 const int preferred_family = AF_UNSPEC) {
3254
3255 // --------------------------------------------------------------------------
3256 // Simple checks first.
3257 // --------------------------------------------------------------------------
3258 if (address.size() == 0) randolf::rex::mk_exception("", EAI_NONAME);
3259 if (address.front() == '/') return AF_UNIX;
3260
3261 // --------------------------------------------------------------------------
3262 // if=<interface-name>: Same "interface" option that we support in bind()
3263 // --------------------------------------------------------------------------
3264 if (address.starts_with("if=")) {
3265 //std::cout << "address=" << address.substr(3).c_str() << std::endl; // Debug
3266 struct ifaddrs *ifaddr; // Chained interface addresses structure
3267 if (::getifaddrs(&ifaddr) == -1) { // Error occurred, so we're done here
3268 freeifaddrs(ifaddr); // Memory management
3269 randolf::rex::mk_exception("getifaddrs() error (prequisite to searching for " + address + ")", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
3270 } // -x- if getifaddrs -x
3271
3272 // --------------------------------------------------------------------------
3273 // Iterate through interface addresses. Each address should have a different
3274 // address family/domain, and is never AF_UNSPEC (this should never happen
3275 // because it wouldn't make sense, but if the network implementation was
3276 // broken and including an AF_UNSPEC family/domain, it would be useless for
3277 // the purposes of calling the ::socket() function so our code will just
3278 // ignore it anyway.
3279 // --------------------------------------------------------------------------
3280 int first_family = AF_UNSPEC; // We're using AF_UNSPEC (0) to indicate that there isn't a family/domain
3281 for (struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
3282 if (ifa->ifa_addr == nullptr) continue; // No address structure, so this isn't useful to us at all
3283 if (ifa->ifa_addr->sa_family == AF_UNSPEC) continue; // Skip AF_UNSPEC family/domain, as we can't use this
3284 if (address.compare(3, -1, ifa->ifa_name) != 0) continue; // Not the interface name we're looking for
3285
3286 // --------------------------------------------------------------------------
3287 // Return current address's family if preferred_family is not specified (if
3288 // it's set to AF_UNSPEC {0}), or if it matches the current address.
3289 // --------------------------------------------------------------------------
3290 //std::cout << "if=" << ifa->ifa_name << " family=" << ifa->ifa_addr->sa_family << std::endl; // Debug
3291 if (preferred_family == AF_UNSPEC || preferred_family == ifa->ifa_addr->sa_family) {
3292 first_family = ifa->ifa_addr->sa_family; // Arbitrarily re-using first_family variable
3293 freeifaddrs(ifaddr); // Memory management
3294 return first_family; // Return current family/domain (re-used first_family variable)
3295 } // -x- if preferred_family -x-
3296
3297 // --------------------------------------------------------------------------
3298 // Keep track of only the first_family/domain that we found; if we can't find
3299 // the preferred_family, then we'll be able to return the first one we found.
3300 // --------------------------------------------------------------------------
3301 if (first_family == AF_UNSPEC && ifa->ifa_addr->sa_family != AF_UNSPEC) first_family = ifa->ifa_addr->sa_family;
3302 //std::cout << " first_family=" << first_family << std::endl; // Debug
3303 } // -x- for *ifa -x-
3304
3305 // --------------------------------------------------------------------------
3306 // Return first_family/domain, if there was one; otherwise, throw exception.
3307 // --------------------------------------------------------------------------
3308 freeifaddrs(ifaddr); // Memory management
3309 if (first_family != AF_UNSPEC) return first_family;
3310 randolf::rex::mk_exception("Interface (" + address.substr(3) + ") doesn't exist" + (preferred_family == AF_UNSPEC ? "" : " or the preferred family/domain (" + std::to_string(preferred_family) + ") is not supported"), EADDRNOTAVAIL);
3311 } // -x- if /^if=/ -x-
3312
3313 // --------------------------------------------------------------------------
3314 // Attempt to detect IPv4 or IPv6 using a simple shortcut with ::inet_pton(),
3315 // which requires some additional memory allocation (that the simple checks
3316 // from earlier don't need).
3317 // --------------------------------------------------------------------------
3318 char buf[sizeof(struct in6_addr)]; // Binary address storage
3319 if (::inet_pton(AF_INET, address.c_str(), buf) == 1) return AF_INET;
3320 if (::inet_pton(AF_INET6, address.c_str(), buf) == 1) return AF_INET6;
3321
3322 // --------------------------------------------------------------------------
3323 // Throw xEAI_FAMILY exception because no family/domain was found.
3324 // --------------------------------------------------------------------------
3325 randolf::rex::mk_exception("No family/domain available for: " + address, EAI_FAMILY);
3326 }; // -x- int family -x-
3327
3328 /*======================================================================*//**
3329 @brief
3330 Get peer name returns the address of the socket as a sockaddr_storage
3331 structure.
3332
3333 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
3334 that aids in the prevention of resource leaks).
3335
3336 @throws randolf::rex::xEBADF The underlying socket is not open
3337 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3338 part of the user address space
3339 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3340 valid for this socket's family (a.k.a., communication domain)
3341 @throws randolf::rex::xENOBUFS Insufficient memory
3342 @throws randolf::rex::xENOTCONN Underlying socket is not connected
3343 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3344 doesn't refer to a socket
3345
3346 @returns sockaddr_storage structure
3347 @qualifier POSIX
3348 @qualifier TLS
3349 *///=========================================================================
3350 const std::shared_ptr<sockaddr_storage> getpeername() {
3351 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3352 socklen_t len = sizeof(sockaddr_storage);
3353 __rc_check(::getpeername(__socket_fd, (struct sockaddr*)sa.get(), &len));
3354 return sa;
3355 }; // -x- std::shared_ptr<sockaddr_storage> getpeername -x-
3356
3357 /*======================================================================*//**
3358 @brief
3359 Get peer name returns the address of the socket as a std::string object.
3360
3361 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3362 (e.g., because the @c family doesn't utilize or support an address
3363 {or the format isn't known}
3364 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3365 @throws randolf::rex::xEBADF The underlying socket is not open
3366 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3367 part of the user address space
3368 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3369 valid for this socket's family (a.k.a., communication domain)
3370 @throws randolf::rex::xENOBUFS Insufficient memory
3371 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3372 @throws randolf::rex::xENOTCONN Underlying socket is not connected
3373 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3374 doesn't refer to a socket
3375
3376 @returns string representation of peer name
3377 @qualifier TLS
3378 *///=========================================================================
3379 const std::string getpeername_ntop() {
3380 sockaddr_storage sa;
3381 socklen_t len = sizeof(sockaddr_storage);
3382 __rc_check(::getpeername(__socket_fd, (sockaddr*)&sa, &len));
3383 return inet_ntop(&sa);
3384 }; // -x- std::string getpeername_ntop -x-
3385
3386 /*======================================================================*//**
3387 @brief
3388 Get specified "sockaddr_storage" structure's address as a "sockaddr"
3389 structure, for sockets in one of the supported families:
3390
3391 - AF_INET (IPv4)
3392 - AF_INET6 (IPv6)
3393 - AF_UNIX (Domain socket path)
3394 - AF_PACKET (Ethernet node/mac. address)
3395
3396 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3397 (e.g., because the @c family doesn't utilize or support an address
3398 {or the format isn't known}
3399
3400 @returns pointer to sockaddr structure within provided sockaddr_storage
3401 @see bind
3402 @see mk_sockaddr_storage
3403 @see recvfrom
3404 @see sendto
3405 @see sendzto
3406 @qualifier POSIX
3407 @qualifier TLS
3408 *///=========================================================================
3409 static sockaddr* getsockaddr(
3410 /// The @c sockaddr_storage structure wherein @c sockaddr will be referenced from
3411 const sockaddr_storage* sa) {
3412 switch (sa->ss_family) {
3413 case AF_INET: // IPv4 address
3414 return (sockaddr*)&(((struct sockaddr_in*)sa)->sin_addr);//->sin_addr);
3415 case AF_INET6: // IPv6 address
3416 return (sockaddr*)&(((struct sockaddr_in6*)sa)->sin6_addr);//->sin6_addr);
3417 case AF_UNIX: // UNIX (path) domain socket
3418 return (sockaddr*)&(((struct sockaddr_un*)sa)->sun_path);//->sun_path);
3419 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
3420 // case /*AF_LINK* /18: // Link layer interface (arp)
3421 // break;
3422 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3423 return (sockaddr*)&(((struct sockaddr_ll*)sa)->sll_addr);//->sll_addr);
3424 } // -x- switch family -x-
3425 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY"); // Everything else
3426 }; // -x- sockaddr* getsockaddr -x-
3427
3428 /*======================================================================*//**
3429 @brief
3430 Get socket name returns the address of the socket as a "sockaddr_storage"
3431 structure.
3432
3433 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
3434 that aids in the prevention of resource leaks).
3435
3436 @throws randolf::rex::xEBADF The underlying socket is not open
3437 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3438 part of the user address space
3439 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3440 valid for this socket's family (a.k.a., communication domain)
3441 @throws randolf::rex::xENOBUFS Insufficient memory
3442 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3443 doesn't refer to a socket
3444
3445 @returns sockaddr_storage structure
3446 @qualifier POSIX
3447 @qualifier TLS
3448 *///=========================================================================
3449 const std::shared_ptr<sockaddr_storage> getsockname() {
3450 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3451 socklen_t len = sizeof(sockaddr_storage);
3452 __rc_check(::getsockname(__socket_fd, (struct sockaddr*)sa.get(), &len));
3453 return sa;
3454 }; // -x- std::string getsockname -x-
3455
3456 /*======================================================================*//**
3457 @brief
3458 Get socket name returns the name of the socket as a std::string object.
3459
3460 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3461 (e.g., because the @c family doesn't utilize or support an address
3462 {or the format isn't known}
3463 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3464 @throws randolf::rex::xEBADF The underlying socket is not open
3465 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3466 part of the user address space
3467 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3468 valid for this socket's family (a.k.a., communication domain)
3469 @throws randolf::rex::xENOBUFS Insufficient memory
3470 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3471 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3472 doesn't refer to a socket
3473
3474 @returns string representation of socket name
3475 @qualifier TLS
3476 *///=========================================================================
3477 const std::string getsockname_ntop() {
3478 sockaddr_storage sa;
3479 socklen_t len = sizeof(sockaddr_storage);
3480 __rc_check(::getsockname(__socket_fd, (sockaddr*)&sa, &len));
3481 return inet_ntop(&sa);
3482 }; // -x- std::string getsockname_ntop -x-
3483
3484 /*======================================================================*//**
3485 @brief
3486 Get socket option details in the form of an integer.
3487
3488 Most options return an integer, with the remaining options returning a
3489 pointer to a structure wrapped in a std::shared_ptr (a C++ smart pointer that
3490 aids in the prevention of resource leaks); the primitive types int, u_int,
3491 and u_char are not wrapped in C++ smart pointers because returning them by
3492 value is more efficient since allocating memory for an entire structure isn't
3493 needed.
3494
3495 @post
3496 It is up to the developer to know which return type is needed according to
3497 the socket option, otherwise an exception will likely be thrown -- in some
3498 cases where the wrong type will seem to work, this is due to the wrong type
3499 providing a minimally sufficient amount of memory for the storage of the
3500 resulting structure.
3501
3502 @par Notes
3503 The returned values/structures are not marked as "const" because they may
3504 need to be modified for unforseen purposes. Modifying the returend values or
3505 structures is fine because they are intended to be independent and are
3506 expected to have no direct impact on the rsocket's internal variables and
3507 structures.
3508
3509 Templates in C++ aren't used here because they don't work properly for our
3510 needs due to neccesity to handle both fundamental types and structures; it
3511 turns out that mixing these is impossible when using the same function name,
3512 so this just doesn't work as well as we'd like it to. (We may try to work on
3513 this again in the future as time permits to provide an additional method for
3514 obtaining socket options, but with the intention of never removing this
3515 current set of methods so as to ensure backward compatibility in the future.)
3516
3517 @throws randolf::rex::xEBADF The underlying socket is not open
3518 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3519 part of the user address space
3520 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3521 valid for this socket's family (a.k.a., communication domain)
3522 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
3523 is not supported
3524 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3525 doesn't refer to a socket
3526
3527 @returns socket option value
3528 @qualifier TLS
3529 *///=========================================================================
3530 int getsockopt_int(
3531 /// The level at which the option resides; typically @c SOL_SOCKET
3532 const int level,
3533 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3534 const int option) {
3535 int value = 0;
3536 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3537 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3538 return value;
3539 }; // -x- int getsockopt_int -x-
3540
3541 /*======================================================================*//**
3542 @brief
3543 Get socket option details in the form of an unsigned integer.
3544 @copydetails getsockopt_int(const int, const int)
3545 @returns socket option value
3546 @qualifier TLS
3547 *///=========================================================================
3548 u_int getsockopt_u_int(
3549 /// The level at which the option resides; typically @c SOL_SOCKET
3550 const int level,
3551 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3552 const int option) {
3553 u_int value = 0;
3554 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3555 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3556 return value;
3557 }; // -x- u_int getsockopt_u_int -x-
3558
3559 /*======================================================================*//**
3560 @brief
3561 Get socket option details in the form of an unsigned character.
3562 @copydetails getsockopt_int(const int, const int)
3563 @returns socket option value
3564 @qualifier TLS
3565 *///=========================================================================
3566 u_char getsockopt_u_char(
3567 /// The level at which the option resides; typically @c SOL_SOCKET
3568 const int level,
3569 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3570 const int option) {
3571 u_char value = 0;
3572 __rc_check(::getsockopt(__socket_fd, level, option, &value, new socklen_t(sizeof(value))));
3573 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3574 return value;
3575 }; // -x- u_char getsockopt_u_char -x-
3576
3577 /*======================================================================*//**
3578 @brief
3579 Get socket option details in the form of a structure.
3580 @copydetails getsockopt_int(const int, const int);
3581 @returns socket option structure wrapped in std::shared_ptr
3582 @qualifier TLS
3583 *///=========================================================================
3584 std::shared_ptr<linger> getsockopt_linger(
3585 /// The level at which the option resides; typically @c SOL_SOCKET
3586 const int level,
3587 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3588 const int option) {
3589 std::shared_ptr value = std::make_shared<linger>();
3590 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3591 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3592 return value;
3593 }; // -x- linger getsockopt_linger -x-
3594
3595 /*======================================================================*//**
3596 @copydoc getsockopt_linger(const int, const int)
3597 @qualifier TLS
3598 *///=========================================================================
3599 std::shared_ptr<timeval> getsockopt_timeval(
3600 /// The level at which the option resides; typically @c SOL_SOCKET
3601 const int level,
3602 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3603 const int option) {
3604 std::shared_ptr value = std::make_shared<timeval>();
3605 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3606 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3607 return value;
3608 }; // -x- timeval getsockopt_timeval -x-
3609
3610 /*======================================================================*//**
3611 @copydoc getsockopt_linger(const int, const int)
3612 @qualifier TLS
3613 *///=========================================================================
3614 std::shared_ptr<in_addr> getsockopt_in_addr(
3615 /// The level at which the option resides; typically @c SOL_SOCKET
3616 const int level,
3617 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3618 const int option) {
3619 std::shared_ptr value = std::make_shared<in_addr>();
3620 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3621 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3622 return value;
3623 }; // -x- in_addr getsockopt_in_addr -x-
3624
3625 /*======================================================================*//**
3626 @copydoc getsockopt_linger(const int, const int)
3627 @qualifier TLS
3628 *///=========================================================================
3629 std::shared_ptr<ip_mreq> getsockopt_ip_mreq(
3630 /// The level at which the option resides; typically @c SOL_SOCKET
3631 const int level,
3632 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3633 const int option) {
3634 std::shared_ptr value = std::make_shared<ip_mreq>();
3635 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3636 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3637 return value;
3638 }; // -x- ip_mreq getsockopt_ip_mreq -x-
3639
3640 /*======================================================================*//**
3641 @copydoc getsockopt_linger(const int, const int)
3642 @qualifier TLS
3643 *///=========================================================================
3644 std::shared_ptr<ip_mreq_source> getsockopt_ip_mreq_source(
3645 /// The level at which the option resides; typically @c SOL_SOCKET
3646 const int level,
3647 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3648 const int option) {
3649 std::shared_ptr value = std::make_shared<ip_mreq_source>();
3650 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3651 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3652 return value;
3653 }; // -x- ip_mreq_source getsockopt_ip_mreq_source -x-
3654
3655 /*======================================================================*//**
3656 @copydoc getsockopt_linger(const int, const int)
3657 @qualifier TLS
3658 *///=========================================================================
3659 std::shared_ptr<icmp6_filter> getsockopt_icmp6_filter(
3660 /// The level at which the option resides; typically @c SOL_SOCKET
3661 const int level,
3662 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3663 const int option) {
3664 std::shared_ptr value = std::make_shared<icmp6_filter>();
3665 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3666 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3667 return value;
3668 }; // -x- icmp6_filter getsockopt_icmp6_filter -x-
3669
3670 /*======================================================================*//**
3671 @copydoc getsockopt_linger(const int, const int)
3672 @qualifier TLS
3673 *///=========================================================================
3674 std::shared_ptr<sockaddr_in6> getsockopt_sockaddr_in6(
3675 /// The level at which the option resides; typically @c SOL_SOCKET
3676 const int level,
3677 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3678 const int option) {
3679 std::shared_ptr value = std::make_shared<sockaddr_in6>();
3680 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3681 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3682 return value;
3683 }; // -x- sockaddr_in6 getsockopt_sockaddr_in6 -x-
3684
3685 /*======================================================================*//**
3686 @copydoc getsockopt_linger(const int, const int)
3687 @qualifier TLS
3688 *///=========================================================================
3689 std::shared_ptr<ip6_mtuinfo> getsockopt_ip6_mtuinfo(
3690 /// The level at which the option resides; typically @c SOL_SOCKET
3691 const int level,
3692 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3693 const int option) {
3694 std::shared_ptr value = std::make_shared<ip6_mtuinfo>();
3695 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3696 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3697 return value;
3698 }; // -x- ip6_mtuinfo getsockopt_ip6_mtuinfo -x-
3699
3700 /*======================================================================*//**
3701 @copydoc getsockopt_linger(const int, const int)
3702 @qualifier TLS
3703 *///=========================================================================
3704 std::shared_ptr<ipv6_mreq> getsockopt_ipv6_mreq(
3705 /// The level at which the option resides; typically @c SOL_SOCKET
3706 const int level,
3707 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3708 const int option) {
3709 std::shared_ptr value = std::make_shared<ipv6_mreq>();
3710 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3711 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3712 return value;
3713 }; // -x- ipv6_mreq getsockopt_ipv6_mreq -x-
3714
3715 /*======================================================================*//**
3716 @copydoc getsockopt_linger(const int, const int)
3717 @qualifier TLS
3718 *///=========================================================================
3719 std::shared_ptr<group_req> getsockopt_group_req(
3720 /// The level at which the option resides; typically @c SOL_SOCKET
3721 const int level,
3722 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3723 const int option) {
3724 std::shared_ptr value = std::make_shared<group_req>();
3725 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3726 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3727 return value;
3728 }; // -x- group_req getsockopt_group_req -x-
3729
3730 /*======================================================================*//**
3731 @copydoc getsockopt_linger(const int, const int)
3732 @qualifier TLS
3733 *///=========================================================================
3734 std::shared_ptr<group_source_req> getsockopt_group_source_req(
3735 /// The level at which the option resides; typically @c SOL_SOCKET
3736 const int level,
3737 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3738 const int option) {
3739 std::shared_ptr value = std::make_shared<group_source_req>();
3740 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3741 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3742 return value;
3743 }; // -x- group_source_req getsockopt_group_source_req -x-
3744
3745 /*======================================================================*//**
3746 @copydoc getsockopt_linger(const int, const int)
3747 @qualifier TLS
3748 *///=========================================================================
3749 template<class T> std::shared_ptr<T> getsockopt_other(
3750 /// The level at which the option resides; typically @c SOL_SOCKET
3751 const int level,
3752 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3753 const int option) {
3754 std::shared_ptr value = std::make_shared<T>();
3755 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), new socklen_t(sizeof(value))));
3756 if (__debug) __debug_sockopt_other("getsockopt_other(socket{0x", level, option, socklen_t(sizeof(value)));
3757 return value;
3758 }; // -x- T getsockopt_other -x-
3759
3760 /*======================================================================*//**
3761 @brief
3762 Get underlying socket's address as a std::string, for sockets in one of the
3763 supported families:
3764
3765 - AF_INET (IPv4)
3766 - AF_INET6 (IPv6)
3767 - AF_UNIX (Domain socket path)
3768 - AF_PACKET (Ethernet node/mac. address)
3769
3770 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3771 (e.g., because the @c family doesn't utilize or support an address
3772 {or the format isn't known}
3773 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3774 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3775
3776 @returns string representation of underlying socket's address
3777 @see inet_ntop(sockaddr_storage*) static method
3778 @qualifier POSIX
3779 @qualifier TLS
3780 *///=========================================================================
3781 const std::string inet_ntop() { return inet_ntop((sockaddr_storage*)&__socket_addr); } // -x- std::string inet_ntop -x-
3782
3783 /*======================================================================*//**
3784 @brief
3785 Get specified "sockaddr_storage" structure's address as a std::string, for
3786 sockets in one of the supported families:
3787
3788 - AF_INET (IPv4)
3789 - AF_INET6 (IPv6)
3790 - AF_UNIX (Domain socket path)
3791 - AF_PACKET (Ethernet node/mac. address)
3792
3793 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3794 (e.g., because the @c family doesn't utilize or support an address
3795 {or the format isn't known}
3796 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3797 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3798
3799 @returns string representation of underlying socket's address
3800 @see inet_ntop() non-static method
3801 @qualifier POSIX
3802 @qualifier TLS
3803 *///=========================================================================
3804 static const std::string inet_ntop(
3805 /// Source structure that [should] contain address data
3806 sockaddr_storage* sa) {
3807 std::string str;
3808 switch (sa->ss_family) {
3809 case AF_INET: // IPv4 address
3810 {
3811 char ntop[sizeof(sockaddr_in)]{0};
3812 const char* rc = ::inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), ntop, sizeof(ntop));
3813 str = std::string(ntop);
3814 }
3815 break;
3816 case AF_INET6: // IPv6 address
3817 { // Debug
3818 char ntop[sizeof(sockaddr_in6)]{0};
3819 const char* rc = ::inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), ntop, sizeof(ntop));
3820 str = std::string(ntop);
3821 }
3822 break;
3823 case AF_UNIX: // UNIX (path) domain socket
3824 str = std::string(((struct sockaddr_un *)sa)->sun_path);
3825 break;
3826 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
3827 // case /*AF_LINK* /18: // Link layer interface (arp)
3828 // break;
3829 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3830 str = to_mac(((struct sockaddr_ll *)sa)->sll_addr);
3831 break;
3832 default: // Everything else
3833 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY");
3834 } // -x- switch family -x-
3835 return str;
3836 }; // -x- std::string inet_ntop -x-
3837
3838 /*======================================================================*//**
3839 @brief
3840 Find out whether an internal read buffer was allocated (this is most likely
3841 triggered by an attempt to read a line of text).
3842 @note
3843 The buffer_size() methods report on how much memory was allocated for the
3844 internal read buffer or to set its size (in bytes).
3845 @returns TRUE = an internal read buffer was allocated
3846 @returns FALSE = an internal read buffer was not allocated
3847 @see buffer_size
3848 @see buffer_size(const size_t nbytes)
3849 @qualifier TLS
3850 *///=========================================================================
3851 const bool is_buffered() noexcept { return __buffer != nullptr; }; // -x- bool is_buffered -x-
3852
3853 /*======================================================================*//**
3854 @brief
3855 Find out whether the underlying socket is not open (which may not be the same
3856 as specifically "closed" since a newly instantiated empty socket begins in a
3857 "not open" state despite the underlying socket not explicitly having been
3858 closed).
3859 @returns TRUE = not open
3860 @returns FALSE = open
3861 @see is_open()
3862 @qualifier TLS
3863 *///=========================================================================
3864 const bool is_closed() noexcept { return !__socket_open; }; // -x- bool is_closed -x-
3865
3866 /*======================================================================*//**
3867 @brief
3868 Find out whether the underlying socket is connected with/to an endpoint.
3869 @returns TRUE = open
3870 @returns FALSE = not open
3871 @qualifier TLS
3872 *///=========================================================================
3873 const bool is_connected() noexcept { return __socket_connected; }; // -x- bool is_connected -x-
3874
3875 /*======================================================================*//**
3876 @brief
3877 Find out whether the default byte order for this host is LSB (small endian).
3878 @note
3879 If you're trying to choose which endian type to use when designing a new
3880 internet protocol, then big endian is normally the better option. However,
3881 if your new protocol will only be used by hardware that all share the same
3882 endianness, then that endianness is probably the more optimal option since it
3883 will translate to an overall lesser consumption of CPU cycles by reducing or
3884 eliminating endianness conversions.
3885 @returns TRUE = LSB (little endian)
3886 @returns FALSE = MSB (big endian / network byte order)
3887 @qualifier TLS
3888 *///=========================================================================
3889 const bool is_endian_lsb() noexcept { return !__endian_is_msb; }; // -x- bool is_endian_lsb -x-
3890
3891 /*======================================================================*//**
3892 @brief
3893 Find out whether the default byte order for this host is MSB (big endian).
3894 @note
3895 Big endian is the standard known as "network byte order" that's also used in
3896 various header fields in internet packets.
3897 @n@n
3898 If you're trying to choose which endian type to use when designing a new
3899 internet protocol, then big endian is normally the better option. However,
3900 if your new protocol will only be used by hardware that all share the same
3901 endianness, then that endianness is probably the more optimal option since it
3902 will translate to an overall lesser consumption of CPU cycles by reducing or
3903 eliminating endianness conversions.
3904 @returns TRUE = MSB (big endian / network byte order)
3905 @returns FALSE = LSB (little endian)
3906 @qualifier TLS
3907 *///=========================================================================
3908 const bool is_endian_msb() noexcept { return __endian_is_msb; }; // -x- bool is_endian_msb -x-
3909
3910 /*======================================================================*//**
3911 @brief
3912 Find out if the EoL adoption policy is enabled for the @ref recvline() method
3913 (see the @ref eol_adoption method to find out how the dynamically-detected
3914 EoL sequence gets adopted, and under what conditions).
3915 @returns TRUE = EoL adoption is enabled
3916 @returns FALSE = EoL adoption is disabled
3917 @see eol
3918 @see eol_adoption
3919 @see eol_index
3920 @see recvline
3921 @see sendline
3922 @see send_eol
3923 @qualifier TLS
3924 *///=========================================================================
3925 const bool is_eol_adoption() noexcept {
3926 return __eol_adoption;
3927 }; // -x- bool is_eol_adoption -x-
3928
3929 /*======================================================================*//**
3930 @brief
3931 Find out if the EoL substitution policy is enabled for the @ref printf(),
3932 @ref printfline(), @ref vprintf(), and @ref vprintfline() methods.
3933 @returns TRUE = EoL substitution is enabled
3934 @returns FALSE = EoL substitution is disabled
3935 @see eol
3936 @see eol_fix_printf
3937 @see printf
3938 @see printfline
3939 @see sendline
3940 @see send_eol
3941 @see vprintf
3942 @see vprintfline
3943 @qualifier TLS
3944 *///=========================================================================
3945 const bool is_eol_fix_printf() noexcept {
3946 return __eol_fix_printf;
3947 }; // -x- bool is_eol_fix_printf -x-
3948
3949 /*======================================================================*//**
3950 @brief
3951 Find out whether the underlying socket is open.
3952 @returns TRUE = open
3953 @returns FALSE = not open
3954 @see is_closed()
3955 @qualifier TLS
3956 *///=========================================================================
3957 const bool is_open() noexcept { return __socket_open; }; // -x- bool is_open -x-
3958
3959 /*======================================================================*//**
3960 @brief
3961 Find out whether encrypted communications is enabled or disabled.
3962 @returns TRUE = encrypted communications is enabled
3963 @returns FALSE = encrypted communications is disabled
3964 @see tls(bool, TLS_FLAGS)
3965 @qualifier TLS
3966 *///=========================================================================
3967 const bool is_tls() noexcept { return __tls; }; // -x- bool is_tls -x-
3968
3969 /*======================================================================*//**
3970 @brief
3971 Find out whether TLS context is in TLS_CLIENT mode.
3972 @returns TRUE = TLS context is in TLS_CLIENT mode
3973 @returns FALSE = TLS context is in TLS_SERVER mode
3974 @see TLS_CLIENT
3975 @see tls()
3976 @qualifier TLS
3977 *///=========================================================================
3978 const bool is_tls_client_mode() noexcept { return !__tls_server_mode; }; // -x- bool is_tls_client_mode -x-
3979
3980 /*======================================================================*//**
3981 @brief
3982 Find out whether egress from encryption (to unencrypted mode) is allowed.
3983 @returns TRUE = egress from encrypted communications is allowed
3984 @returns FALSE = egress from encrypted communications is not allowed
3985 @see TLS_NO_EGRESS
3986 @see tls()
3987 @qualifier TLS
3988 *///=========================================================================
3989 const bool is_tls_egress_okay() noexcept { return __tls_egress; }; // -x- bool is_tls_egress_okay -x-
3990
3991 /*======================================================================*//**
3992 @brief
3993 Find out whether encrypted communications is exclusive.
3994 @returns TRUE = encrypted communications is exclusive
3995 @returns FALSE = encrypted communications is not exclusive
3996 @see tls()
3997 @qualifier TLS
3998 *///=========================================================================
3999 const bool is_tls_exclusive() noexcept { return __tls_exclusive; }; // -x- bool is_tls_exclusive -x-
4000
4001 /*======================================================================*//**
4002 @brief
4003 Find out whether ingress to encryption (from unencrypted mode) is allowed.
4004 @returns TRUE = ingress to encrypted communications is allowed
4005 @returns FALSE = ingress to encrypted communications is not allowed
4006 @see TLS_NO_INGRESS
4007 @see tls()
4008 @qualifier TLS
4009 *///=========================================================================
4010 const bool is_tls_ingress_okay() noexcept { return __tls_ingress; }; // -x- bool is_tls_ingress_okay -x-
4011
4012 /*======================================================================*//**
4013 @brief
4014 Find out whether TLS context is in TLS_SERVER mode.
4015 @returns TRUE = TLS context is in TLS_SERVER mode
4016 @returns FALSE = TLS context is in TLS_CLIENT mode
4017 @see TLS_SERVER
4018 @see tls()
4019 @qualifier TLS
4020 *///=========================================================================
4021 const bool is_tls_server_mode() noexcept { return __tls_server_mode; }; // -x- bool is_tls_server_mode -x-
4022
4023 /*======================================================================*//**
4024 @brief
4025 Find out whether SNI (Server Name Identifier) is enabled (configured, which
4026 implies that an internal callback function was also set up).
4027 @returns TRUE = SNI is enabled
4028 @returns FALSE = SNI is disabled
4029 @see tls_sni()
4030 @qualifier TLS
4031 *///=========================================================================
4032 const bool is_tls_sni() noexcept { return __tls_sni != nullptr; }; // -x- bool is_tls_sni -x-
4033
4034 /*======================================================================*//**
4035 @brief
4036 Find out whether SNI (Server Name Identifier) was matched, which means that
4037 we're using one of the supplementary TLS certificates that are included in
4038 the associated @ref rsocket_sni object as separate TLS contexts.
4039
4040 When this method returns @c TRUE, it means the @c OpenSSL callback (which
4041 occurs during the TLS handshake process) completed the TLS handshake with one
4042 of the TLS certificates that's included in this @c rsocket's @ref rsocket_sni
4043 object instead of the primary TLS certificate assigned to this @c rsocket,
4044 and this @c rsocket is using the respective TLS context instead of the
4045 primary (default) one.
4046 @returns TRUE = SNI was matched
4047 @returns FALSE = SNI wasn't matched
4048 @see name_sni
4049 @see tls_sni
4050 @qualifier TLS
4051 *///=========================================================================
4052 const bool is_tls_sni_match() noexcept { return __tls_sni_match; }; // -x- bool is_tls_sni_match -x-
4053
4054 /*======================================================================*//**
4055 @brief
4056 Enable listening mode for this rsocket to prepare it to accept() new inbound
4057 connections.
4058
4059 The backlog defaults to SOMAXCONN (4096 on Linux, and 128 on older systems),
4060 which is common on most systems. If a higher value is supplied that exceeds
4061 @c kern.somaxconn, the kernel will automatically override the backlog with
4062 the value stored in @c kern.somaxconn without generating any errors or
4063 warnings.
4064
4065 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
4066 @throws randolf::rex::xEADDRINUSE No ephemeral ports are available for
4067 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
4068 but it really isn't)
4069 @throws randolf::rex::xEBADF The underlying socket is not open
4070 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4071 doesn't refer to a socket
4072 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
4073 @returns The same rsocket object so as to facilitate stacking
4074 @see accept()
4075 @see accept_sp()
4076 @see accept4()
4077 @see accept4_sp()
4078 @see backlog()
4079 @see bind()
4080 @qualifier POSIX
4081 @qualifier TLS
4082 *///=========================================================================
4083 rsocket* listen(
4084 /// Backlog queue size (0 = uses rsocket's default; see @ref backlog for more
4085 /// details about this); specifying a non-zero backlog also updates rocket's
4086 /// internal default (SOMAXCONN is 4096 on Linux, and 128 on older systems)
4087 int backlog = 0) {
4088 if (__debug) debug("listen(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4089 + ", " + std::to_string(backlog)
4090 + ");");
4091
4092 // --------------------------------------------------------------------------
4093 // Use rsocket's default backlog if not specified, otherwise update it
4094 // with the new value specified here.
4095 // --------------------------------------------------------------------------
4096 if (backlog = 0) backlog = __socket_backlog;
4097 else __socket_backlog = backlog;
4098
4099 // --------------------------------------------------------------------------
4100 // Configure underlying socket to queue up to "backlog" inbound connections.
4101 // --------------------------------------------------------------------------
4102 __rc_check(::listen(__socket_fd, backlog));
4103
4104 return this;
4105 }; // -x- rsocket* listen -x-
4106
4107 /*======================================================================*//**
4108 @brief
4109 Convert an IPv4 address, IPv6 address, ethernet packet, or UNIX domain
4110 socket to a sockaddr_storage structure.
4111
4112 If service_name is an absolute path (that begins with a "/" charcter) and the
4113 family is set to AF_UNSPEC (the default), then the resulting family will be
4114 set to AF_UNIX.
4115
4116 @par Notes
4117 This method utilizes the results of getaddrinfo().
4118
4119 Other families like AF_LINK and AF_PACKET should work, but haven't been
4120 tested thoroughly. The additional support we provide for IPv4 and IPv6
4121 addresses is to copy the port number into the resulting structure (as a
4122 convenience).
4123
4124 @post
4125 The resulting sockaddr_storage structure is wrapped in std::shared_ptr (a C++
4126 smart pointer that aids in the prevention of resource leaks).
4127
4128 @par Threads
4129 This method is thread-safe.
4130
4131 @throws randolf::rex::xEAI_ADDRFAMILY If specified network host doesn't have
4132 any addresses in the specified address family
4133 @throws randolf::rex::xEAI_AGAIN Temporary failure code from DNS server (try
4134 again later)
4135 @throws randolf::rex::xEAI_BADFLAGS Invalid flags in @c hints.ai_flags (or
4136 `hints.ai_flags` included @c AI_CANONNAME with @c nullptr as @c name)
4137 @throws randolf::rex::xEAI_FAIL Permanent failure code from DNS server
4138 @throws randolf::rex::xEAI_FAMILY The specified family is not supported
4139 @throws randolf::rex::xEAI_MEMORY Out of memory
4140 @throws randolf::rex::xEAI_NONAME If node_name is nullptr or an empty string
4141 @throws randolf::rex::xEAI_SERVICE The specified service is not available
4142 for the specified socket type
4143 @throws randolf::rex::xEAI_SOCKTYPE The specified socket type is not
4144 supported
4145 @throws randolf::rex::xEAI_SYSTEM Other system error (use errno to determine
4146 what the error is, then run use @ref randolf::rex::rex::mk_exception
4147 to throw the correct exception)
4148
4149 @returns sockaddr_storage structure
4150 @see getsockaddr
4151 *///=========================================================================
4152 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
4153 /// IP address or UNIX domain socket address to convert
4154 const char* node_name,
4155 /// Port number (or service name used by some other families)
4156 const char* service_name = nullptr,
4157 /// Optional pointer to a helpful addrinfo structure
4158 const addrinfo* hints = nullptr) {
4159
4160 // --------------------------------------------------------------------------
4161 // Initial sanity checks.
4162 // --------------------------------------------------------------------------
4163 if (node_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME); // if (node_name == nullptr && service_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME);
4164
4165 // --------------------------------------------------------------------------
4166 // Handle any special cases.
4167 // --------------------------------------------------------------------------
4168 switch (hints == nullptr ? AF_UNSPEC : hints->ai_family) { // Default to AF_UNSPEC in the absence of hints
4169 case AF_UNSPEC:
4170 if (node_name[0] != '/') break;
4171 // Next entry MUST be "case AF_UNIX" for this fall-through to work
4172 case AF_UNIX:
4173 {
4174 // For some unknown reason, clang++ can't compile this line that
4175 // g++ has absolutely no trouble with at all:
4176 // std::shared_ptr sa = std::make_shared<sockaddr_storage>(AF_UNIX);
4177 // So, after wasting more than a year trying to figure out what the
4178 // hundreds of lines of cryptic errors meant, I eventually gave up
4179 // on clang++ for being such a dumbfuck, and used this work-around:
4180 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
4181 sa->ss_family = AF_UNIX;
4182 std::strcpy(((struct sockaddr_un *)sa.get())->sun_path, node_name); // Copy path
4183 return sa;
4184 }
4185 break;
4186 } // -x- switch hints->family -x-
4187
4188 // --------------------------------------------------------------------------
4189 // Acquire addrinfo[] linked-list array.
4190 // --------------------------------------------------------------------------
4191 addrinfo* __addr_result; // This is temporary
4192//std::cout << " Making socket structure... node_name=" << node_name << " ...hints: ai_family=" << hints->ai_family << " ai_socktype=" << hints->ai_socktype << " ai_flags=" << hints->ai_flags << std::endl;
4193 randolf::rex::mk_exception("", getaddrinfo(node_name,
4194 service_name,
4195 hints,
4196 &__addr_result));
4197
4198 // --------------------------------------------------------------------------
4199 // Find first valid addrinfo[] array by trying to open a socket with it.
4200 // --------------------------------------------------------------------------
4201 for (addrinfo* ar = __addr_result; ar != nullptr; ar = ar->ai_next) {
4202//std::cout << " ... ai_family=" << ar->ai_family << " ai_socktype=" << ar->ai_socktype << " ai_flags=" << ar->ai_flags << std::endl; // *******************
4203// ***************************** TODO: Make sure this loop is working properly
4204 } // -x- for ar -x-
4205//std::cout << " !!! ai_family=" << __addr_result->ai_family << std::endl; // *******************
4206
4207 // --------------------------------------------------------------------------
4208 // Copy the address to "sa" and return it. The "addr" data for IPv4 and IPv6
4209 // addresses include the TCP/UDP port number (service) in first two bytes as
4210 // as an unsigned integer (u_int16), followed immediately by the IP address.
4211 //
4212 // We're taking the first record only. We assume that hints{} is defined on
4213 // an as-needed basis.
4214 //
4215 // Note: AF_LINK and AF_PACKET are not widely supported, but we've made an
4216 // effort to accomodate their usage.
4217 // --------------------------------------------------------------------------
4218 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
4219 switch (__addr_result->ai_family) {
4220 case AF_INET: // IPv4 address
4221 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_in));
4222 break;
4223 case AF_INET6: // IPv6 address
4224 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_in6));
4225 break;
4226 /* // We're handling this earlier as a special case: TODO: Move special handling to here
4227 case AF_UNIX: // UNIX (path) domain socket
4228 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_un));
4229 break;
4230 */
4231 case /*AF_LINK*/18: // Link layer interface (arp)
4232 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_dl));
4233 break;
4234 case AF_PACKET: // Packet (ethernet) address (packet capturing)
4235 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_ll));
4236 break;
4237 default: // Everything else
4238 std::memcpy(sa.get(), __addr_result->ai_addr, sizeof(sockaddr_storage));
4239 } // -x- switch family -x-
4240 freeaddrinfo(__addr_result); // We don't need this anymore
4241 return sa;
4242 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
4243
4244 /*======================================================================*//**
4245 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
4246 *///=========================================================================
4247 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
4248 /// IP address or UNIX domain socket address to convert
4249 const char* node_name,
4250 /// Port number
4251 const u_int16_t service_name,
4252 /// Optional pointer to a helpful addrinfo structure
4253 const addrinfo* hints = nullptr) {
4254 std::string port = std::to_string(service_name);
4255 return mk_sockaddr_storage(node_name, port.c_str(), hints);
4256 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
4257
4258 /*======================================================================*//**
4259 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
4260 *///=========================================================================
4261 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
4262 /// IP address or UNIX domain socket address to convert
4263 const std::string node_name,
4264 /// Port number
4265 const u_int16_t service_name,
4266 /// Optional pointer to a helpful addrinfo structure
4267 const addrinfo* hints = nullptr) {
4268 std::string port = std::to_string(service_name);
4269 return mk_sockaddr_storage(node_name.c_str(), port.c_str(), hints);
4270 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
4271
4272 /*======================================================================*//**
4273 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
4274 *///=========================================================================
4275 static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
4276 /// IP address or UNIX domain socket address to convert
4277 const std::string node_name,
4278 /// Port number (or server name used by some other families)
4279 const std::string service_name,
4280 /// Optional pointer to a helpful addrinfo structure
4281 const addrinfo* hints = nullptr) {
4282 return mk_sockaddr_storage(node_name.c_str(), service_name.c_str(), hints);
4283 }; // -x- sockaddr_storage* mk_sockaddr_storage -x-
4284
4285 /*======================================================================*//**
4286 @brief
4287 Specify a name for this rsocket.
4288
4289 This is an arbitrary name that is entirely optional, and should be regarded
4290 as similar to the naming of threads.
4291 @returns The same rsocket object so as to facilitate stacking
4292 *///=========================================================================
4293 rsocket* name(
4294 /// Name to assign to this @c rsocket
4295 const std::string name) noexcept {
4296 // const std::lock_guard<std::mutex> lock(__name);
4297 __name = name;
4298 return this;
4299 }; // -x- rsocket* name -x-
4300
4301 /*======================================================================*//**
4302 @brief
4303 Find out what this rsocket's name is.
4304
4305 The built-in SNI mechanism will overwrite this data to indicate the hostname
4306 that was specified by the TLS-encrypted endpoint that triggered an internal
4307 SNI callback -- use the @ref name_sni() method to find out which hostname is
4308 actually being used by TLS.
4309 @returns The name of this rsocket (or an empty @c std::string if this rsocket
4310 doesn't have a name)
4311 @see name_sni
4312 @see tls_sni
4313 *///=========================================================================
4314 std::string name() noexcept {
4315 // const std::lock_guard<std::mutex> lock(__name);
4316 return __name;
4317 }; // -x- std::string name -x-
4318
4319 /*======================================================================*//**
4320 @brief
4321 Find out what this rsocket's actual TLS SNI hostname is.
4322
4323 This is the exact - or closest-matching (in the case of wildcards) - hostname
4324 associated with an actual TLS certificate that was provided in the configured
4325 @ref rsocket_sni object.
4326 @returns The hostname associated with the TLS certificate selected by SNI
4327 @see name
4328 @see tls_sni
4329 @qualifier TLS
4330 *///=========================================================================
4331 std::string name_sni() noexcept {
4332 return __name_sni;
4333 }; // -x- std::string name_sni -x-
4334
4335 /*======================================================================*//**
4336 @brief
4337 Get socket I/O statistics from internally-tracked socket I/O counters.
4338
4339 The number of bytes transmitted and received is tracked internally, so that
4340 the information can be used later in logging. These statistics are available
4341 at all times, but for logging purposes it makes the most sense to copy this
4342 information after the rsocket is closed, at which time the final statistics
4343 will continue to be available until the rsocket's destructor takes over.
4344
4345 @par Threads
4346 This method is threadsafe.
4347 @returns rsocket_io wrapped in a std::shared_ptr object to help ease memory
4348 management efforts
4349 @see net_io_final
4350 @see net_io_update
4351 @see printf
4352 @qualifier TLS
4353 *///=========================================================================
4354 std::shared_ptr<rsocket_io> net_io() noexcept {
4355 std::shared_ptr stats = std::make_shared<rsocket_io>();
4356 stats->bytes_rx = __bytes_rx;
4357 stats->bytes_tx = __bytes_tx;
4358 stats->crypt_rx = __crypt_rx;
4359 stats->crypt_tx = __crypt_tx;
4360 stats->is_final = false;
4361 return stats;
4362 }; // -x- std::shared_ptr<rsocket_io> net_io -x-
4363
4364 /*======================================================================*//**
4365 @brief
4366 Get socket I/O statistics from internally-tracked socket I/O counters as a
4367 pre-formatted std::string object.
4368
4369 The number of bytes transmitted and received is tracked internally, so that
4370 the information can be used later in logging. These statistics are available
4371 at all times, but for logging purposes it makes the most sense to copy this
4372 information after the rsocket is closed, at which time the final statistics
4373 will continue to be available until the rsocket's destructor takes over.
4374
4375 @par Formatting
4376 The format string may contain any characters, with only instances of the
4377 following case-sensitive command sequences to be interpolated accordingly
4378 (invalid commands will be ignored and will remain in place, unmodified):
4379
4380 <table cellpadding=8 cellspacing=0 border=1>
4381 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
4382 <tr><td>$$</td><td>One dollar sign ("$")</td><td>@e N/A</td></tr>
4383 <tr><td>$aR</td><td>Total (all) bytes received</td><td>bytes_rx @c + crypt_rx</td></tr>
4384 <tr><td>$aT</td><td>Total (all) bytes transmitted</td><td>bytes_tx @c + crypt_tx</td></tr>
4385 <tr><td>$bR</td><td>Unencrypted bytes received</td><td>bytes_rx</td></tr>
4386 <tr><td>$bT</td><td>Unencrypted bytes transmitted</td><td>bytes_tx</td></tr>
4387 <tr><td>$cR</td><td>Encrypted bytes recevied</td><td>crypt_rx</td></tr>
4388 <tr><td>$cT</td><td>Encrypted bytes transmitted</td><td>crypt_tx</td></tr>
4389 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
4390 </table>
4391
4392 Why do we use dollar signs instead of percent symbols, like other formatting
4393 functions like printf() does? So that the format string won't be in conflict
4394 with any percent-prefixed commands in printf() and similar funcions. This
4395 means that a printf() format string can be put through a first pass here to
4396 get the needed statistics interpolated into the needed positions.
4397
4398 @par Threads
4399 This method is threadsafe.
4400 @returns An interpolated format string as an std::string object.
4401 @see net_io_final
4402 @see net_io_update
4403 @see printf
4404 @qualifier TLS
4405 *///=========================================================================
4406 std::string net_io(
4407 /// Format string
4408 const char* format,
4409 /// Length of format string (in bytes), or 0 to auto-detect length if format string is an ASCIIZ string
4410 size_t len = 0,
4411 /// Pointer to an @ref rsocket_io structure to read the statistics from (nullptr = use internal copy of current statistics)
4412 // TODO: Convert this to a static method and create a new method without the "len" parameter that just calls the static method with the pointer to the internal "addr" buffer (this will make it easier to call this method in a static fashion later without incurring any of the overhead of instantiation)
4413 rsocket_io* addr = nullptr) noexcept {
4414
4415 // --------------------------------------------------------------------------
4416 // Measure size of format string if an ASCIIZ string was indicated.
4417 // --------------------------------------------------------------------------
4418 if (len == 0) len = std::strlen(format);
4419
4420 // --------------------------------------------------------------------------
4421 // Internal variables.
4422 // --------------------------------------------------------------------------
4423 rsocket_io* data = addr == nullptr ? net_io().get() : addr; // Copy current statistics so we have a snapshot instead of a set of possibly-changing statistics to work with
4424 std::string stats; // Formatted result
4425 ulong val; // Value (to be inserted)
4426 char c; // Current character
4427 std::string cmd; // Accumulated command (may be subsituted)
4428 bool flag_commas = false; // Thousands separators flag
4429 bool flag_right = false; // Flush-right flag
4430
4431 // --------------------------------------------------------------------------
4432 // Process format string and build resulting formatted stats string.
4433 //
4434 // This is designed to be fast, and I'm taking huge shortcuts here since the
4435 // commands are all the same size (except for the first one, which is only
4436 // two dollar sign characters). Processing in this loop should be quite fast
4437 // compared to using library functions to search for dollar sign characters
4438 // since data needs to be copied anyway -- why run a loop over the text twice
4439 // when can get away with doing it faster by doing it only once? This is an
4440 // optimized approach, although it probably could be even faster by not using
4441 // std::string for temporary command storage (there are other ways to do this
4442 // but I think this should suffice for now since it isn't expected to be used
4443 // very often).
4444 // --------------------------------------------------------------------------
4445 for (int i = 0; i < len; i++) {
4446
4447 // --------------------------------------------------------------------------
4448 // First character (potentially).
4449 // --------------------------------------------------------------------------
4450 if ((c = format[i]) != '$') { // Not a dollar sign -- save it and move on
4451 stats.push_back(c);
4452 continue;
4453 } // -x- if !$ -x-
4454 cmd = c; // Start building up the command string
4455
4456 // --------------------------------------------------------------------------
4457 // Second character: Part 1 of 2
4458 //
4459 // TODO: Add support for "," commas, and "r" right-alignment.
4460 // --------------------------------------------------------------------------
4461 if (++i == len) continue; // End of format string, so we're done
4462 cmd.push_back(c = format[i]);
4463 if (c == '$') { // Consume the previous dollar sign so that $$ turns into $
4464 stats.push_back(c); // Add $ to stats string (we're only keeping one dollar sign)
4465 continue;
4466 } // -x- if $ -x-
4467 flag_commas = flag_right = false; // Reset these flags for a clean start
4468
4469 // --------------------------------------------------------------------------
4470 // Flag checks:
4471 // , = Include thousands separators (commas)
4472 // r = Flush right (leading spaces will be added)
4473 // --------------------------------------------------------------------------
4474 net_io_flags_loop:
4475 if (c == ',') {
4476 if (++i == len) continue; // End of format string, so we're done
4477 cmd.push_back(c = format[i]);
4478 flag_commas = true;
4479 goto net_io_flags_loop;
4480 } else if (c == 'r') {
4481 if (++i == len) continue; // End of format string, so we're done
4482 cmd.push_back(c = format[i]);
4483 flag_right = true;
4484 goto net_io_flags_loop;
4485 } // -x- if [,r] -x-
4486 // TODO: Do more
4487
4488 // --------------------------------------------------------------------------
4489 // Second character: Part 1 of 2
4490 // --------------------------------------------------------------------------
4491 if (c < 'a' || c > 'c') { // Not a, b, or c, so treat it as a regular character
4492 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4493 continue;
4494 } // -x- if !~[abc] -x-
4495
4496 // --------------------------------------------------------------------------
4497 // Third character.
4498 // --------------------------------------------------------------------------
4499 if (++i == len) continue; // End of format string, so we're done
4500 cmd.push_back(c = format[i]);
4501 if (c != 'R' && c != 'T') { // Not R or T, so treat it as a regular character
4502 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4503 continue;
4504 } // -x- if !~[RT] -x-
4505
4506 // --------------------------------------------------------------------------
4507 // Command processing. If the command is valid, then it will be interpolated
4508 // with its expected result by replacing it with the resulting value.
4509 // --------------------------------------------------------------------------
4510 //std::cout << "[" << cmd << "]"; // Debug
4511 if (cmd.ends_with("aR")) val = data->bytes_rx + data->crypt_rx;
4512 else if (cmd.ends_with("aT")) val = data->bytes_tx + data->crypt_tx;
4513 else if (cmd.ends_with("bR")) val = data->bytes_rx ;
4514 else if (cmd.ends_with("bT")) val = data->bytes_tx ;
4515 else if (cmd.ends_with("cR")) val = data->crypt_rx;
4516 else if (cmd.ends_with("cT")) val = data->crypt_tx;
4517 else { stats.append(cmd); continue; } // This is wrong, so ignore it
4518
4519 // --------------------------------------------------------------------------
4520 // Re-use cmd to generate formatted value.
4521 // --------------------------------------------------------------------------
4522 cmd.clear();
4523 cmd.resize(21); // Maximum length without commas (plus character zero): 18446744073709551615
4524 cmd.resize(sprintf(cmd.data(), flag_right ? "%20lu" : "%lu", val)); // The ::sprintf function can't use cmd.c_str() here
4525 if (flag_commas) cmd = randolf::rtools::insert_commas(cmd.c_str()); // Insert commas (this makes the string longer)
4526
4527 // --------------------------------------------------------------------------
4528 // Convert to std::string and add to the stats string.
4529 // --------------------------------------------------------------------------
4530 stats.append(cmd);
4531
4532 }; // -x- for i -x-
4533
4534 return stats;
4535 }; // -x- std::string net_io -x-
4536
4537 /*======================================================================*//**
4538 @brief
4539 Where the destructor should save final I/O statistics before this rsocket's
4540 resources are completely freed/deallocated.
4541 @attention
4542 Developers should take care to check that the @ref rsocket_io::is_final flag
4543 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
4544 since there's no guarantee that the destructor will necessarily be executed
4545 in a timely manner (this flag is set last, after all other statistics are
4546 copied into the structure while in a mutex-locked state).
4547 @returns The same rsocket object so as to facilitate stacking
4548 @see ~rsocket
4549 @see net_io
4550 @see net_io_update
4551 @see printf
4552 @qualifier TLS
4553 *///=========================================================================
4554 rsocket* net_io_final(
4555 /// Pointer to @ref rsocket_io structure (nullptr = disabled)
4556 rsocket_io* addr) noexcept {
4557 __io_final_addr = addr;
4558 return this;
4559 }; // -x- rsocket* net_io_final -x-
4560
4561 /*======================================================================*//**
4562 @brief
4563 Where the destructor should save current I/O statistics.
4564 @attention
4565 Statistics are copied into the structure while in a mutex-locked state, but
4566 the copy itself is not an overall atomic snapshot of the I/O statistics even
4567 though each statistic is copied atomically. This means that the actual
4568 statistics could be slightly different if updates occur independently (e.g.,
4569 due to activities in a different thread), but this likely won't matter since
4570 the anticipated use for these statistics is to display or otherwise present
4571 general statistical information to a user at regular intervals.
4572 @returns The same rsocket object so as to facilitate stacking
4573 @see ~rsocket
4574 @see net_io
4575 @see net_io_final
4576 @see printf
4577 @qualifier TLS
4578 *///=========================================================================
4579 rsocket* net_io_update(
4580 /// Pointer to @ref rsocket_io structure (nullptr = do nothing)
4581 rsocket_io* addr) noexcept {
4582
4583 // --------------------------------------------------------------------------
4584 // Copy statistics to final location. (We do this last in case there's an
4585 // issue with locking; there shouldn't be, but if there is then at least we
4586 // already we're not inadvertently hanging on to resources at this point that
4587 // need to be freed/closed anyway.)
4588 // --------------------------------------------------------------------------
4589 if (addr != nullptr) {
4590 addr->lock();
4591 addr->bytes_rx = __bytes_rx;
4592 addr->bytes_tx = __bytes_tx;
4593 addr->crypt_rx = __crypt_rx;
4594 addr->crypt_tx = __crypt_tx;
4595 addr->is_final = false;
4596 addr->unlock();
4597 } // -x- if addr -x-
4598
4599 return this;
4600 }; // -x- rsocket* net_io_update -x-
4601
4602 /*======================================================================*//**
4603 @brief
4604 Return the number of bytes pending to be received, without actually receiving
4605 any data.
4606
4607 @note
4608 When using TLS, OpenSSL may not report all the pending data, but this is
4609 resolved here by also adding the total amount of pending raw data that is not
4610 currently being processed in the current record by OpenSSL, even when "read
4611 ahead" is enabled.
4612
4613 @throws randolf::rex::xEBADF The underlying socket is not open
4614 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4615 part of the user address space
4616 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
4617 @throws randolf::rex::xENOTTY The underlying socket is not associated with a
4618 character special device, or the specified operation does not apply
4619 to the kind of object that the file descriptor fd references (this
4620 exception will probably never occur unless the underlying socket
4621 handle was arbitrarily replaced with the type of handle that can
4622 cause this error to occur)
4623
4624 @returns Total number of bytes pending (0 = none)
4625 @see eos
4626 @see recv
4627 @qualifier TLS
4628 *///=========================================================================
4629 ulong pending() {
4630 if (__debug) debug("pending(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4631 + ");");
4632
4633 // --------------------------------------------------------------------------
4634 // Get pending bytes from raw socket.
4635 // --------------------------------------------------------------------------
4636 ulong pending_bytes = 0; // BSD, glibc, etc., use ulong; older UNIX systems use int // TODO: Figure out how to detect what ioctl needs
4637 __rc_check(ioctl(__socket_fd, FIONREAD, &pending_bytes));
4638
4639 // --------------------------------------------------------------------------
4640 // Add pending bytes from OpenSSL if TLS is active, because this total isn't
4641 // included in the raw socket count since OpenSSL received these bytes.
4642 // --------------------------------------------------------------------------
4643 return __tls ? pending_bytes + SSL_pending(__tls_fd) : pending_bytes;
4644
4645 }; // -x- ulong pending -x-
4646
4647 /*======================================================================*//**
4648 @brief
4649 Poll the underlying socket using the poll() method for data that's ready for
4650 receiving (default), etc.
4651
4652 @note
4653 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4654 select(), and accept()/accept4() when using multiple sockets.
4655
4656 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4657 part of the user address space
4658 @throws randolf::rex::xEINTR Interrupted by a signal
4659 @throws randolf::rex::xENOMEM Insufficient memory
4660 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4661 doesn't refer to a socket
4662 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4663 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4664 is a highly improbable chance that a timeout could still occur if the
4665 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4666 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4667 there's no new data
4668
4669 @returns The resulting (short)pollfd.revents bitfield
4670 @returns 0 if @c timeout_behaviour is set to TIMEOUT_ZERO
4671 @see ppoll
4672 @see rsocket_mux
4673 @qualifier POSIX
4674 @qualifier TLS
4675 *///=========================================================================
4676 // TODO: Integrate SSL_poll (requires OpenSSL 3.3 or newer) with SSL_POLL_EVENT_R, SSL_POLL_EVENT_W, and SSL_POLL_EVENT_E conversion
4677 short poll(
4678 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4679 const short events = POLLIN,
4680 /// Number of milliseconds to wait
4681 const int timeout = 0,
4682 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4683 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4684 if (__debug) debug("poll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4685 + ", pollfd.events=" + std::to_string(events)
4686 + ", timeout=" + std::to_string(timeout)
4687 + ");");
4688 struct pollfd fds{__socket_fd, events, 0};
4689 if (__rc_check(::poll(&fds, 1, timeout)) == 0) {
4690 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4691 else return 0;
4692 } // -x- if 0 -x-
4693 return fds.revents;
4694 }; // -x- short poll -x-
4695
4696 /*======================================================================*//**
4697 @brief
4698 Get port number associated with underlying socket descriptor/handle.
4699
4700 Returns 0 if:
4701 - an emphemeral port number assignment (dynamic) is intended
4702 - the underlying socket details are not defined (e.g., in the case of an
4703 empty rsocket instantiation)
4704 - port numbers are not supported/utilized by the current family (e.g., not
4705 AF_INET {IPv4} or AF_INET6 {IPv6})
4706
4707 The port number can be set in most constructors, or via one of the socket()
4708 or bind() methods.
4709 @returns Port number (typically TCP and UDP, although some other families
4710 such as SCTP and DCCP also utilize port numbers)
4711 @see socket_family()
4712 @see socket_protocol()
4713 @see socket_type()
4714 @qualifier TLS
4715 *///=========================================================================
4716 const uint16_t port() noexcept {
4717 switch (__socket_addr.ss_family) {
4718 case AF_INET: // IPv4
4719 return ntohs(((sockaddr_in*)&__socket_addr)->sin_port);
4720 case AF_INET6: // IPv6
4721 return ntohs(((sockaddr_in6*)&__socket_addr)->sin6_port);
4722 default: // Everything else
4723 return 0;
4724 } // -x- switch __socket_addr.ss_family -x-
4725 }; // -x- uint16_t port -x-
4726
4727 /*======================================================================*//**
4728 @brief
4729 Poll the underlying socket using the ppoll() method for data that's ready for
4730 receiving (default), etc.
4731
4732 @note
4733 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4734 select(), and accept()/accept4() when using multiple sockets.
4735
4736 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4737 part of the user address space
4738 @throws randolf::rex::xEINTR Interrupted by a signal
4739 @throws randolf::rex::xENOMEM Insufficient memory
4740 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4741 doesn't refer to a socket
4742 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4743 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4744 is a highly improbable chance that a timeout could still occur if the
4745 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4746 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4747 there's no new data
4748
4749 @returns The resulting (short)pollfd.revents bitfield
4750 @see poll
4751 @see rsocket_mux
4752 @qualifier POSIX
4753 @qualifier TLS
4754 *///=========================================================================
4755 // TODO: Integrate SSL_poll (requires OpenSSL 3.3 or newer) with SSL_POLL_EVENT_R, SSL_POLL_EVENT_W, and SSL_POLL_EVENT_E conversion
4756 short ppoll(
4757 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4758 const short events = POLLIN,
4759 /// Timeout
4760 const struct timespec* tmo_p = nullptr,
4761 /// Signal mask
4762 const sigset_t* sigmask = nullptr,
4763 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4764 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4765 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4766 + ", pollfd.events=" + std::to_string(events)
4767 + ", timespec{tv_sec=" + std::to_string(tmo_p->tv_sec)
4768 + ", tv_nsec=" + std::to_string(tmo_p->tv_nsec) + "}"
4769 + ", sigset_t"
4770 + ");");
4771 struct pollfd fds{__socket_fd, events, 0};
4772 if (__rc_check(::ppoll(&fds, 1, tmo_p, sigmask)) == 0) {
4773 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4774 else return 0;
4775 } // -x- if 0 -x-
4776 return fds.revents;
4777 }; // -x- short ppoll -x-
4778
4779 /*======================================================================*//**
4780 @brief
4781 Poll the underlying socket using the ppoll() method for data that's ready for
4782 receiving (default), etc.
4783
4784 @note
4785 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4786 select(), and accept()/accept4() when using multiple sockets.
4787
4788 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4789 part of the user address space
4790 @throws randolf::rex::xEINTR Interrupted by a signal
4791 @throws randolf::rex::xENOMEM Insufficient memory
4792 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4793 doesn't refer to a socket
4794 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4795 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4796 is a highly improbable chance that a timeout could still occur if the
4797 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4798 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4799 there's no new data
4800
4801 @returns The resulting (short)pollfd.revents bitfield
4802 @see ppoll
4803 @see rsocket_mux
4804 @qualifier TLS
4805 *///=========================================================================
4806 // TODO: Integrate SSL_poll (requires OpenSSL 3.3 or newer) with SSL_POLL_EVENT_R, SSL_POLL_EVENT_W, and SSL_POLL_EVENT_E conversion
4807 short ppoll(
4808 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
4809 const short events = POLLIN,
4810 /// Timeout in seconds
4811 const int tv_sec = 0,
4812 /// Timeout in nanoseconds
4813 const long tv_nsec = 0,
4814 /// Signal mask
4815 const sigset_t* sigmask = nullptr,
4816 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
4817 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
4818 struct timespec tmo_p{tv_sec, tv_nsec};
4819 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4820 + ", pollfd.events=" + std::to_string(events)
4821 + ", timespec{tv_sec=" + std::to_string(tmo_p.tv_sec)
4822 + ", tv_nsec=" + std::to_string(tmo_p.tv_nsec) + "}"
4823 + ", sigset_t"
4824 + ");");
4825 struct pollfd fds{__socket_fd, events, 0};
4826 if (__rc_check(::ppoll(&fds, 1, &tmo_p, sigmask)) == 0) {
4827 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
4828 else return 0;
4829 } // -x- if 0 -x-
4830 return fds.revents;
4831 }; // -x- short ppoll -x-
4832
4833 /*======================================================================*//**
4834 @brief
4835 Send a formatted string to the @ref rsocket endpoint.
4836
4837 The @c format is described in the documentation for the POSIX or Standard C
4838 Library @c printf() function.
4839 @throws randolf::rex::xEBADF The underlying socket is not open
4840 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
4841 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
4842 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
4843 @throws randolf::rex::xENOMEM Insufficient memory
4844 @returns The same rsocket object so as to facilitate stacking
4845 @see eol_fix_printf
4846 @see is_eol_fix_printf
4847 @see net_io
4848 @see printfline
4849 @see vprintf
4850 @see vprintfline
4851 @qualifier POSIX
4852 @qualifier TLS
4853 *///=========================================================================
4854 rsocket* printf(
4855 /// Format string to use
4856 const char* format,
4857 /// Variadic arguments
4858 ...) {
4859 char* buf;
4860 ::va_list args;
4861 ::va_start(args, format);
4862 int rc = ::vasprintf(&buf, format, args);
4863 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
4864 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
4865 if (__eol_fix_printf && !__eol.empty()) {
4866 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
4867 ::free(buf);
4868 __send(str.c_str(), str.length());
4869 } else {
4870 try {
4871 __send(buf, rc);
4872 } catch (std::exception& e) { // Free buf then re-throw the exception
4873 ::free(buf); // Prevent memory leak when an exception is thrown
4874 throw;
4875 }
4876 } // -x- if __eol_fix_printf -x-
4877 return this;
4878 }; // -x- rsocket* printf -x-
4879
4880 /*======================================================================*//**
4881 @brief
4882 Send a formatted string to the @ref rsocket endpoint, and append an EoL
4883 sequence.
4884
4885 The @c format is described in the documentation for the POSIX or Standard C
4886 Library @c printf() function.
4887 @throws randolf::rex::xEBADF The underlying socket is not open
4888 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
4889 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
4890 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
4891 @throws randolf::rex::xENOMEM Insufficient memory
4892 @returns The same rsocket object so as to facilitate stacking
4893 @see eol
4894 @see eol_fix_printf
4895 @see is_eol_fix_printf
4896 @see net_io
4897 @see printf
4898 @see sendline
4899 @see vprintf
4900 @see vprintfline
4901 @qualifier TLS
4902 *///=========================================================================
4903 rsocket* printfline(
4904 /// Format string to use
4905 const char* format,
4906 /// Variadic arguments
4907 ...) {
4908 char* buf;
4909 ::va_list args;
4910 ::va_start(args, format);
4911 int rc = ::vasprintf(&buf, format, args);
4912 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
4913 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
4914 if (__eol_fix_printf && !__eol.empty()) {
4915 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
4916 .append(__eol);
4917 ::free(buf);
4918 __send(str.c_str(), str.length());
4919 } else {
4920 try {
4921 __sendline(buf, rc);
4922 } catch (std::exception& e) { // Free buf then re-throw the exception
4923 ::free(buf); // Prevent memory leak when an exception is thrown
4924 throw;
4925 }
4926 } // -x- if __eol_fix_printf -x-
4927 return this;
4928 }; // -x- rsocket* printfline -x-
4929
4930 /*======================================================================*//**
4931 @brief
4932 Receive data from the endpoint into a @c std::vector<char> that is allocated
4933 on-the-fly.
4934
4935 If nbytes is 0, then the internal @ref buffer_size() will be used as the
4936 default, which can also be changed from its compiled-in default of 1024 by
4937 using one of the buffer_size() methods.
4938
4939 @par Notes
4940 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4941 reception, but it's important to note that it does implement temporary
4942 blocking while waiting for data.
4943
4944 @throws randolf::rex::xEBADF The underlying socket is not open
4945 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
4946 connections
4947 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4948 part of the user address space
4949 @throws randolf::rex::xEINTR Interrupted by a signal
4950 @throws randolf::rex::xEINVAL Invalid argument passed
4951 @throws randolf::rex::xENOMEM Insufficient memory
4952 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4953 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4954 doesn't refer to a socket
4955 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4956 there's no new data
4957
4958 @returns appropriately-sized vector of characters
4959 @see recv(std::vector<char>, const int)
4960 @see recvz(const size_t, const int)
4961 @qualifier TLS
4962 *///=========================================================================
4963 std::vector<char> recv(
4964 /// Maximum number of bytes to receive
4965 const size_t nbytes = 0,
4966 /// MSG_OOB@n
4967 /// MSG_PEEK@n
4968 /// MSG_WAITALL@n
4969 /// MSG_DONTWAIT@n
4970 /// MSG_CMSG_CLOEXEC
4971 const int posix_flags = 0) {
4972 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
4973 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4974 + ", <buf>"
4975 + ", " + std::to_string(buf_size)
4976 + ", " + std::to_string(posix_flags)
4977 + ");");
4978 std::vector<char> buf(buf_size);
4979 buf.resize(__recv(buf.data(), buf.size(), posix_flags));
4980 return buf;
4981 }; // -x- std::vector<char> recv -x-
4982
4983 /*======================================================================*//**
4984 @brief
4985 Receive data from the endpoint into the @c std::vector object supplied in the
4986 @c buf parameter.
4987
4988 The maximum number of bytes that can be received won't exceed the number of
4989 bytes that the supplied @c std::vector<char> was initialized or resized to.
4990
4991 @pre
4992 For @c std::vector it's important that the @c resize() method is used to
4993 pre-allocate the underlying char[] array, instead of the unfortunately-named
4994 @c reserve() method that doesn't pre-allocate, to avoid causing segmentation
4995 faults or other undefined behaviours.
4996
4997 @par Notes
4998 The @c MSG_WAITALL flag is particularly useful for preventing premature data
4999 reception, but it's important to note that it does implement temporary
5000 blocking while waiting for data.
5001
5002 @throws randolf::rex::xEBADF The underlying socket is not open
5003 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5004 connections
5005 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5006 part of the user address space
5007 @throws randolf::rex::xEINTR Interrupted by a signal
5008 @throws randolf::rex::xEINVAL Invalid argument passed
5009 @throws randolf::rex::xENOMEM Insufficient memory
5010 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5011 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5012 doesn't refer to a socket
5013 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5014 there's no new data
5015
5016 @returns The same array that was specified in the @c buf parameter
5017 @see recv(const size_t, const int)
5018 @see recvz(const size_t, const int)
5019 @qualifier POSIX
5020 @qualifier TLS
5021 *///=========================================================================
5022 std::vector<char> recv(
5023 /// Target std::vector<char> to receive data into
5024 std::vector<char> buf,
5025 /// MSG_OOB@n
5026 /// MSG_PEEK@n
5027 /// MSG_WAITALL@n
5028 /// MSG_DONTWAIT@n
5029 /// MSG_CMSG_CLOEXEC
5030 const int posix_flags = 0) {
5031 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5032 + ", <buf>"
5033 + ", " + std::to_string(buf.size())
5034 + ", " + std::to_string(posix_flags)
5035 + ");");
5036 buf.resize(__recv(buf.data(), buf.size(), posix_flags));
5037 return buf;
5038 }; // -x- std::vector<char> recv -x-
5039
5040 /*======================================================================*//**
5041 @brief
5042 Receive data from the endpoint into a @c std::string object that is allocated
5043 on-the-fly.
5044
5045 If nbytes is 0, then the internal @ref buffer_size() will be used as the
5046 default, which can also be changed from its compiled-in default of 1024 by
5047 using one of the buffer_size() methods.
5048
5049 @par Notes
5050 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5051 reception, but it's important to note that it does implement temporary
5052 blocking while waiting for data.
5053
5054 @throws randolf::rex::xEBADF The underlying socket is not open
5055 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5056 connections
5057 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5058 part of the user address space
5059 @throws randolf::rex::xEINTR Interrupted by a signal
5060 @throws randolf::rex::xEINVAL Invalid argument passed
5061 @throws randolf::rex::xENOMEM Insufficient memory
5062 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5063 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5064 doesn't refer to a socket
5065 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5066 there's no new data
5067
5068 @returns appropriately-sized vector of characters
5069 @see recv
5070 @see recv(std::vector<char>, const int)
5071 @see recvz(const size_t, const int)
5072 @qualifier TLS
5073 *///=========================================================================
5074 std::string recv_as_string(
5075 /// Maximum number of bytes to receive
5076 const size_t nbytes = 0,
5077 /// MSG_OOB@n
5078 /// MSG_PEEK@n
5079 /// MSG_WAITALL@n
5080 /// MSG_DONTWAIT@n
5081 /// MSG_CMSG_CLOEXEC
5082 const int posix_flags = 0) {
5083 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5084 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5085 + ", <buf>"
5086 + ", " + std::to_string(buf_size)
5087 + ", " + std::to_string(posix_flags)
5088 + ");");
5089 std::string buf;
5090 buf.resize(buf_size); // Pre-fill anticipated string size
5091 buf.resize(__recv(buf.data(), buf.size(), posix_flags)); // Shorten string
5092 return buf;
5093 }; // -x- std::string recv -x-
5094
5095 /*======================================================================*//**
5096 @brief
5097 Receive an ASCIIZ string from the endpoint, including the NULL terminator.
5098
5099 @throws randolf::rex::xEBADF The underlying socket is not open
5100 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5101 connections
5102 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5103 part of the user address space
5104 @throws randolf::rex::xEINTR Interrupted by a signal
5105 @throws randolf::rex::xEINVAL Invalid argument passed
5106 @throws randolf::rex::xENOMEM Insufficient memory
5107 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5108 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5109 doesn't refer to a socket
5110 @throws randolf::rex::xERANGE if no NULL terminator is detected (this may
5111 occur before the underlying ASCIIZ string char* array is allocated)
5112 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5113 there's no new data
5114
5115 @returns Pointer to ASCIIZ string (will need to be deleted by caller)
5116 @see send_asciiz(const char*, const int)
5117 @qualifier TLS
5118 *///=========================================================================
5119 char* recv_asciiz(
5120 /// Maximum number of bytes to receive
5121 const size_t nbytes = 0,
5122 /// MSG_OOB@n
5123 /// MSG_PEEK@n
5124 /// MSG_WAITALL@n
5125 /// MSG_DONTWAIT@n
5126 /// MSG_CMSG_CLOEXEC
5127 const int posix_flags = 0) {
5128 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5129 if (__debug) debug("recv_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5130 + ", " + std::to_string(buf_size)
5131 + ", " + std::to_string(posix_flags)
5132 + ");");
5133
5134 // --------------------------------------------------------------------------
5135 // Calculate size of buffer (includes NULL terminator since we'll also be
5136 // receiving this from the endpoint).
5137 // --------------------------------------------------------------------------
5138 char buf[buf_size];
5139
5140 // --------------------------------------------------------------------------
5141 // Reduce buffer size to what is actually read (remember: we don't actually
5142 // know where the EoL sequence is yet, or if there even is one).
5143 // --------------------------------------------------------------------------
5144 int max = __recv(&buf, buf_size, posix_flags | MSG_PEEK); // Look-ahead at socket stream (buffer was inflated to include EoL sequence)
5145 int len = -1;
5146 for (int i = 0; i < max; i++) {
5147 if (buf[i] == 0) {
5148 len = i;
5149 break;
5150 } // -x- if v[i] -x-
5151 } // -x- for i -x-
5152 if (len < 0) throw randolf::rex::xERANGE("ERANGE: NULL terminator not found");
5153
5154 // --------------------------------------------------------------------------
5155 // I'd love to use std::shared_ptr<char*> for this next part, but it leads
5156 // to intermittent segmentation faults and/or "malloc(): corrupted top size
5157 // occurs" errors outside of this method. So, my conclusion is that char*
5158 // (and char[], as I've tried with this too) are not properly supported by:
5159 // std::shared_ptr<char*> v = std::make_shared<char*>(new char[len + 1]);
5160 //
5161 // Using std::string() is really not what we want because there is confusion
5162 // concerning the NULL terminator -- with char* strings, it's crystal clear
5163 // that there must be a NULL terminator (since a length isn't tracked). Now,
5164 // if you want an std::string, just initialize as follows:
5165 //
5166 // char* temp_string = r.recv_asciiz();
5167 // std::string mystr = std::string(temp_string);
5168 // delete temp_string;
5169 // --------------------------------------------------------------------------
5170 char* v = new char[len + 1];
5171 int rc = __recv(v, len + 1, posix_flags); // Consume with NULL terminator from socket stream
5172// TODO: Free "v" in a catch-and-rethrow block (check other calls to __recv and __send too)
5173 return v;
5174
5175 }; // -x- char* recv_asciiz -x-
5176
5177 /*======================================================================*//**
5178 @brief
5179 Receive one byte (unsigned 8-bit byte) of data from the endpoint.
5180 @par Notes
5181 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5182 reception, but it's important to note that it does implement temporary
5183 blocking while waiting for data.
5184
5185 @throws randolf::rex::xEBADF The underlying socket is not open
5186 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5187 connections
5188 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5189 part of the user address space
5190 @throws randolf::rex::xEINTR Interrupted by a signal
5191 @throws randolf::rex::xEINVAL Invalid argument passed
5192 @throws randolf::rex::xENOMEM Insufficient memory
5193 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5194 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5195 doesn't refer to a socket
5196 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5197 there's no new data
5198
5199 @returns one unsigned character
5200 @see recv(std::vector<char>, const int)
5201 @see recvz(const size_t, const int)
5202 @see recv_char
5203 @see send_byte
5204 @see send_char
5205 @qualifier TLS
5206 *///=========================================================================
5207 u_char recv_byte(
5208 /// MSG_OOB@n
5209 /// MSG_PEEK@n
5210 /// MSG_WAITALL@n
5211 /// MSG_DONTWAIT@n
5212 /// MSG_CMSG_CLOEXEC
5213 const int posix_flags = 0) {
5214 char buf(0);
5215 if (__debug) debug("recv_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5216 + ", <buf>"
5217 + ", " + std::to_string(sizeof(buf))
5218 + ", " + std::to_string(posix_flags)
5219 + ");");
5220 int rc = __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5221 return buf;
5222 }; // -x- byte recv_byte -x-
5223
5224 /*======================================================================*//**
5225 @brief
5226 Receive one character (signed 8-bit byte) of data from the endpoint.
5227 @par Notes
5228 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5229 reception, but it's important to note that it does implement temporary
5230 blocking while waiting for data.
5231
5232 @throws randolf::rex::xEBADF The underlying socket is not open
5233 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5234 connections
5235 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5236 part of the user address space
5237 @throws randolf::rex::xEINTR Interrupted by a signal
5238 @throws randolf::rex::xEINVAL Invalid argument passed
5239 @throws randolf::rex::xENOMEM Insufficient memory
5240 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5241 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5242 doesn't refer to a socket
5243 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5244 there's no new data
5245
5246 @returns one signed character
5247 @see recv(std::vector<char>, const int)
5248 @see recvz(const size_t, const int)
5249 @see recv_byte
5250 @see send_byte
5251 @see send_char
5252 @qualifier TLS
5253 *///=========================================================================
5254 char recv_char(
5255 /// MSG_OOB@n
5256 /// MSG_PEEK@n
5257 /// MSG_WAITALL@n
5258 /// MSG_DONTWAIT@n
5259 /// MSG_CMSG_CLOEXEC
5260 const int posix_flags = 0) {
5261 char buf(0);
5262 if (__debug) debug("recv_char(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5263 + ", <buf>"
5264 + ", " + std::to_string(sizeof(buf))
5265 + ", " + std::to_string(posix_flags)
5266 + ");");
5267 int rc = __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5268 // How to detect disconnected stream when telnet user presses CTRL-C?
5269 //std::cout << "rc=" << std::to_string(rc) << std::endl; // Debug (to be removed)
5270// TODO: Investigate checking platform's char size and moving signing bit accordingly
5271 return buf;
5272 }; // -x- char recv_char -x-
5273
5274 /*======================================================================*//**
5275 @brief
5276 Receive a line of data from the endpoint, into an @ref randolf::rline object
5277 with the EoL character(s) isolated. While this is meant for ASCII and UTF-8
5278 text, it will also work with binary data that doesn't include EoL character
5279 sequences as non-line-ending data (when receiving binary data,
5280 the @ref recv() and @ref recvz() methods tend to be better-suited).
5281
5282 This is essentially a wrapper around what recvline() does, but returns both
5283 the line of text and the EoL sequence together in an @ref randolf::rline
5284 object.
5285 @note
5286 For additional details on the other parameters, please see the @ref recvline
5287 method's documentation for the remaining details.
5288 @warning
5289 If you're using a customzied EoL sequence, then it's important to note that
5290 it probably won't be recognized by the @ref randolf::rline class, but do also
5291 check that documentation to confirm this.
5292
5293 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
5294 @throws randolf::rex::xEBADF The underlying socket is not open
5295 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5296 connections
5297 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5298 part of the user address space
5299 @throws randolf::rex::xEINTR Interrupted by a signal
5300 @throws randolf::rex::xEINVAL Invalid argument passed
5301 @throws randolf::rex::xENOMEM Insufficient memory
5302 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5303 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5304 doesn't refer to a socket
5305 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
5306 no EoL character sequence is detected after recvline's buffer became
5307 full (whatever data is waiting may still be received using any of the
5308 recv_ methods {with or without the @c MSG_PEEK flag set}, including
5309 @c recvline() with a larger buffer {see the @c nbytes parameter})
5310 @throws randolf::rex::xERANGE if the timeout parameter is below 0
5311 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5312 there's no new data
5313
5314 @returns One line of text as a randolf::rline object.
5315 @see discard
5316 @see eol()
5317 @see eol_consumed_seq()
5318 @see randolf::rline
5319 @see recvline
5320 @see sendline(const std::string, const int)
5321 @see timeout
5322 @see timeout_recvline
5323 @see timeout_recvline(long)
5324 @qualifier TLS
5325 *///=========================================================================
5326 randolf::rline recv_rline(
5327 /// Maximum number of bytes to receive (including EoL character sequence)
5328 const size_t nbytes = 0,
5329 /// MSG_OOB@n
5330 /// MSG_PEEK@n
5331 /// MSG_WAITALL@n
5332 /// MSG_DONTWAIT@n
5333 /// MSG_CMSG_CLOEXEC
5334 const int posix_flags = 0,
5335 /// Line timeout (in seconds)@n
5336 /// 0 = no timeout (default), unless it was configured by way of the
5337 /// @ref timeout_recvline(long) method
5338 long timeout = 0,
5339 /// Configuration parameters
5340 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
5341 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
5342 // + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF; TODO: Add an option for this
5343 // Internally, the length of the @ref eol() sequence is added to the buffer size
5344 // so that the maximum line length without EoL characters matches the maximum
5345 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
5346 if (__debug) debug("recv_rline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5347 + ", " + std::to_string(buf_size)
5348 + ", " + std::to_string(posix_flags)
5349 + ");");
5350 std::string line = __recvline(buf_size, posix_flags, timeout, recvline_flags);
5351 return randolf::rline(line.append(eol_consumed_seq()));
5352 }; // -x- randolf::rline recv_rline -x-
5353
5354 /*======================================================================*//**
5355 @brief
5356 Receive a data structure from the endpoint.
5357 @post
5358 MSB/LSB considerations are important for any integers within your structure,
5359 so be sure to sanitize them with htons(), ntohs(), and related methods prior
5360 to sending, and then after receiving. This way, your data will be protected
5361 against corruption resulting from byte order differences when communicating
5362 between hardware architectures that differ in MSB and LSB byte ordering.
5363 @par Notes
5364 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5365 reception, but it's important to note that it does implement temporary
5366 blocking while waiting for data.
5367
5368 @throws randolf::rex::xEBADF The underlying socket is not open
5369 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5370 connections
5371 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5372 part of the user address space
5373 @throws randolf::rex::xEINTR Interrupted by a signal
5374 @throws randolf::rex::xEINVAL Invalid argument passed
5375 @throws randolf::rex::xENOMEM Insufficient memory
5376 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5377 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5378 doesn't refer to a socket
5379 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5380 there's no new data
5381
5382 @returns Data structure
5383 @see send_struct
5384 @qualifier TLS
5385 *///=========================================================================
5386 template <typename T> T recv_struct(
5387 /// MSG_OOB@n
5388 /// MSG_PEEK@n
5389 /// MSG_WAITALL@n
5390 /// MSG_DONTWAIT@n
5391 /// MSG_CMSG_CLOEXEC
5392 const int posix_flags = 0) {
5393 T buf{0};
5394 if (__debug) debug("recv_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5395 + ", <buf>"
5396 + ", " + std::to_string(sizeof(buf))
5397 + ", " + std::to_string(posix_flags)
5398 + ");");
5399 __recv(&buf, sizeof(buf), posix_flags);
5400 return buf;
5401 }; // -x- T recv_struct -x-
5402
5403 /*======================================================================*//**
5404 @brief
5405 Receive one 16-bit unsigned integer of data in LSB (little endian) order from
5406 the endpoint.
5407 @par Notes
5408 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5409 reception, which implements temporary blocking while waiting for data.
5410
5411 @throws randolf::rex::xEBADF The underlying socket is not open
5412 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5413 connections
5414 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5415 part of the user address space
5416 @throws randolf::rex::xEINTR Interrupted by a signal
5417 @throws randolf::rex::xEINVAL Invalid argument passed
5418 @throws randolf::rex::xENOMEM Insufficient memory
5419 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5420 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5421 doesn't refer to a socket
5422 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5423 there's no new data
5424
5425 @returns uint16_t (converted to local endianness)
5426 @qualifier TLS
5427 *///=========================================================================
5428 uint16_t recv_uint16_lsb(
5429 /// MSG_OOB@n
5430 /// MSG_PEEK@n
5431 /// MSG_WAITALL@n
5432 /// MSG_DONTWAIT@n
5433 /// MSG_CMSG_CLOEXEC
5434 const int posix_flags = 0) {
5435 uint16_t buf(0);
5436 if (__debug) debug("recv_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5437 + ", <buf>"
5438 + ", " + std::to_string(sizeof(buf))
5439 + ", " + std::to_string(posix_flags)
5440 + ");");
5441 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5442 return !__endian_is_msb ? buf : ntohs(buf);
5443 }; // -x- uint16_t recv_uint16_lsb -x-
5444
5445 /*======================================================================*//**
5446 @brief
5447 Receive one 16-bit unsigned integer of data in MSB (big endian) order from
5448 the endpoint.
5449 @par Notes
5450 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5451 reception, which implements temporary blocking while waiting for data.
5452
5453 @throws randolf::rex::xEBADF The underlying socket is not open
5454 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5455 connections
5456 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5457 part of the user address space
5458 @throws randolf::rex::xEINTR Interrupted by a signal
5459 @throws randolf::rex::xEINVAL Invalid argument passed
5460 @throws randolf::rex::xENOMEM Insufficient memory
5461 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5462 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5463 doesn't refer to a socket
5464 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5465 there's no new data
5466
5467 @returns uint16_t (converted to local endianness)
5468 @qualifier TLS
5469 *///=========================================================================
5470 uint16_t recv_uint16_msb(
5471 /// MSG_OOB@n
5472 /// MSG_PEEK@n
5473 /// MSG_WAITALL@n
5474 /// MSG_DONTWAIT@n
5475 /// MSG_CMSG_CLOEXEC
5476 const int posix_flags = 0) {
5477 uint16_t buf(0);
5478 if (__debug) debug("recv_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5479 + ", <buf>"
5480 + ", " + std::to_string(sizeof(buf))
5481 + ", " + std::to_string(posix_flags)
5482 + ");");
5483 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5484 return __endian_is_msb ? buf : htons(buf);
5485 }; // -x- uint16_t recv_uint16_msb -x-
5486
5487 /*======================================================================*//**
5488 @brief
5489 Receive one 32-bit unsigned integer of data in LSB (little endian) order from
5490 the endpoint.
5491 @par Notes
5492 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5493 reception, which implements temporary blocking while waiting for data.
5494
5495 @throws randolf::rex::xEBADF The underlying socket is not open
5496 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5497 connections
5498 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5499 part of the user address space
5500 @throws randolf::rex::xEINTR Interrupted by a signal
5501 @throws randolf::rex::xEINVAL Invalid argument passed
5502 @throws randolf::rex::xENOMEM Insufficient memory
5503 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5504 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5505 doesn't refer to a socket
5506 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5507 there's no new data
5508
5509 @returns uint32_t (converted to local endianness)
5510 @qualifier TLS
5511 *///=========================================================================
5512 uint32_t recv_uint32_lsb(
5513 /// MSG_OOB@n
5514 /// MSG_PEEK@n
5515 /// MSG_WAITALL@n
5516 /// MSG_DONTWAIT@n
5517 /// MSG_CMSG_CLOEXEC
5518 const int posix_flags = 0) {
5519 uint32_t buf(0);
5520 if (__debug) debug("recv_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5521 + ", <buf>"
5522 + ", " + std::to_string(sizeof(buf))
5523 + ", " + std::to_string(posix_flags)
5524 + ");");
5525 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5526 return !__endian_is_msb ? buf : htonl(buf);
5527 }; // -x- uint32_t recv_uint32_lsb -x-
5528
5529 /*======================================================================*//**
5530 @brief
5531 Receive one 32-bit unsigned integer of data in MSB (big endian) order from
5532 the endpoint.
5533 @par Notes
5534 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5535 reception, which implements temporary blocking while waiting for data.
5536
5537 @throws randolf::rex::xEBADF The underlying socket is not open
5538 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5539 connections
5540 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5541 part of the user address space
5542 @throws randolf::rex::xEINTR Interrupted by a signal
5543 @throws randolf::rex::xEINVAL Invalid argument passed
5544 @throws randolf::rex::xENOMEM Insufficient memory
5545 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5546 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5547 doesn't refer to a socket
5548 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5549 there's no new data
5550
5551 @returns uint32_t (converted to local endianness)
5552 @qualifier TLS
5553 *///=========================================================================
5554 uint32_t recv_uint32_msb(
5555 /// MSG_OOB@n
5556 /// MSG_PEEK@n
5557 /// MSG_WAITALL@n
5558 /// MSG_DONTWAIT@n
5559 /// MSG_CMSG_CLOEXEC
5560 const int posix_flags = 0) {
5561 uint32_t buf(0);
5562 if (__debug) debug("recv_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5563 + ", <buf>"
5564 + ", " + std::to_string(sizeof(buf))
5565 + ", " + std::to_string(posix_flags)
5566 + ");");
5567 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5568 return __endian_is_msb ? buf : ntohl(buf);
5569 }; // -x- uint32_t recv_uint32_msb -x-
5570
5571 /*======================================================================*//**
5572 @brief
5573 Receive one 64-bit unsigned integer of data in LSB (little endian) order from
5574 the endpoint.
5575 @par Notes
5576 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5577 reception, which implements temporary blocking while waiting for data.
5578
5579 @throws randolf::rex::xEBADF The underlying socket is not open
5580 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5581 connections
5582 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5583 part of the user address space
5584 @throws randolf::rex::xEINTR Interrupted by a signal
5585 @throws randolf::rex::xEINVAL Invalid argument passed
5586 @throws randolf::rex::xENOMEM Insufficient memory
5587 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5588 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5589 doesn't refer to a socket
5590 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5591 there's no new data
5592
5593 @returns uint64_t (converted to local endianness)
5594 @qualifier TLS
5595 *///=========================================================================
5596 uint64_t recv_uint64_lsb(
5597 /// MSG_OOB@n
5598 /// MSG_PEEK@n
5599 /// MSG_WAITALL@n
5600 /// MSG_DONTWAIT@n
5601 /// MSG_CMSG_CLOEXEC
5602 const int posix_flags = 0) {
5603 uint64_t buf(0);
5604 if (__debug) debug("recv_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5605 + ", <buf>"
5606 + ", " + std::to_string(sizeof(buf))
5607 + ", " + std::to_string(posix_flags)
5608 + ");");
5609 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5610 return !__endian_is_msb ? buf : ((((uint64_t)ntohl((buf) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((buf) >> 32)));
5611 }; // -x- uint64_t recv_uint64_lsb -x-
5612
5613 /*======================================================================*//**
5614 @brief
5615 Receive one 64-bit unsigned integer of data in MSB (big endian) order from
5616 the endpoint.
5617 @par Notes
5618 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5619 reception, which implements temporary blocking while waiting for data.
5620
5621 @throws randolf::rex::xEBADF The underlying socket is not open
5622 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5623 connections
5624 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5625 part of the user address space
5626 @throws randolf::rex::xEINTR Interrupted by a signal
5627 @throws randolf::rex::xEINVAL Invalid argument passed
5628 @throws randolf::rex::xENOMEM Insufficient memory
5629 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5630 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5631 doesn't refer to a socket
5632 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5633 there's no new data
5634
5635 @returns uint64_t (converted to local endianness)
5636 @qualifier TLS
5637 *///=========================================================================
5638 uint64_t recv_uint64_msb(
5639 /// MSG_OOB@n
5640 /// MSG_PEEK@n
5641 /// MSG_WAITALL@n
5642 /// MSG_DONTWAIT@n
5643 /// MSG_CMSG_CLOEXEC
5644 const int posix_flags = 0) {
5645 uint64_t buf(0);
5646 if (__debug) debug("recv_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5647 + ", <buf>"
5648 + ", " + std::to_string(sizeof(buf))
5649 + ", " + std::to_string(posix_flags)
5650 + ");");
5651 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5652 return __endian_is_msb ? buf : ((((uint64_t)htonl((buf) & 0xffffffffUL)) << 32) | htonl((uint32_t)((buf) >> 32)));
5653 }; // -x- uint64_t recv_uint64_msb -x-
5654
5655 /*======================================================================*//**
5656 @brief
5657 Receive data from a specific endpoint.
5658
5659 @par Notes
5660 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5661 reception, but it's important to note that it does implement temporary
5662 blocking while waiting for data.
5663
5664 @warning
5665 This method is not compatible with TLS.
5666
5667 @throws randolf::rex::xEBADF The underlying socket is not open
5668 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5669 connections
5670 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5671 part of the user address space
5672 @throws randolf::rex::xEINTR Interrupted by a signal
5673 @throws randolf::rex::xEINVAL Invalid argument passed
5674 @throws randolf::rex::xENOMEM Insufficient memory
5675 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5676 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5677 doesn't refer to a socket
5678 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5679 there's no new data
5680
5681 @returns Appropriately-sized vector of characters
5682 @qualifier POSIX
5683 *///=========================================================================
5684 std::vector<char> recvfrom(
5685 /// Maximum number of bytes to receive
5686 const size_t nbytes,
5687 /// MSG_OOB@n
5688 /// MSG_PEEK@n
5689 /// MSG_WAITALL@n
5690 /// MSG_DONTWAIT@n
5691 /// MSG_CMSG_CLOEXEC
5692 const int posix_flags,
5693 /// Target endpoint address structure
5694 struct sockaddr *from,
5695 /// Size of target endpoint structure
5696 socklen_t fromlen = sizeof(sockaddr)) {
5697 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5698 if (__debug) debug("recvfrom(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5699 + ", <buf>"
5700 + ", " + std::to_string(buf_size)
5701 + ", " + std::to_string(posix_flags)
5702 + ", <sockaddr>"
5703 + ", " + std::to_string(fromlen)
5704 + ");");
5705 std::vector<char> v(buf_size);
5706 v.resize(__track_bytes_rx(__rc_check(::recvfrom(__socket_fd, v.data(), v.size(), posix_flags, from, &fromlen))));
5707 return v;
5708 }; // -x- std::vector<char> recvfrom -x-
5709
5710 private:
5711 /*======================================================================*//**
5712 @brief
5713 This is an internal function that:
5714 1. receives data from the endpoint:
5715 - via underlying socket when TLS is not enabled
5716 - via the OpenSSL socket API when TLS is enabled
5717 2. checks for a socket I/O error (and throws an exception)
5718 3. tracks number of bytes received (if there were no errors)
5719
5720 @note
5721 The ring buffer will only be used if there is one. The @ref __recvline
5722 method is one method that instantiates the internal ring buffer, and there
5723 could be others in the future (although this is not strongly anticipated
5724 aside from a method that allows the software developer to explicitly choose
5725 to have a buffer active for all socket-read operations).
5726
5727 @throws randolf::rex::xEAGAIN The underlying socket timed out
5728 @throws randolf::rex::xEBADF The underlying socket is not open
5729 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5730 connections
5731 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5732 part of the user address space
5733 @throws randolf::rex::xEINTR Interrupted by a signal
5734 @throws randolf::rex::xEINVAL Invalid argument passed
5735 @throws randolf::rex::xENOMEM Insufficient memory
5736 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5737 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5738 doesn't refer to a socket
5739 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5740 there's no new data
5741
5742 @returns Number of bytes that were successfully received
5743 @qualifier TLS
5744 *///=========================================================================
5745 int __recv(
5746 /// Pointer to data
5747 void* data,
5748 /// Length of message data
5749 const int len,
5750 /// Flags (ignored with encrypted streams, except for the MSG_PEEK flag)
5751 const int posix_flags = 0) {
5752
5753 // --------------------------------------------------------------------------
5754 // When internal buffering is not set up (which is the default operation), we
5755 // simply pass data directly.
5756 // --------------------------------------------------------------------------
5757 if (__buffer == nullptr) {
5758 return __tls ? (posix_flags & MSG_PEEK ? __rc_check_tls(SSL_peek(__tls_fd, data, len))
5759 : __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, data, len))))
5760 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, data, len, posix_flags)));
5761 } // -x- if !__buffer -x-
5762
5763 // --------------------------------------------------------------------------
5764 // If the amount of data contained in the internal buffer is sufficient to
5765 // satisfy the amount of data that "len" represents, then all that's needed
5766 // is to return "len" bytes from the ring buffer without having to receive
5767 // more data from the socket.
5768 // --------------------------------------------------------------------------
5769 if (len <= __buffer->get_utilized()) {
5770 if (posix_flags & MSG_PEEK) __buffer->peek_array( (size_t)len, (char*)data); // Copy character(s) from ring buffer because MSG_PEEK flag is set
5771 else __buffer->remove_array((size_t)len, (char*)data); // Consume character(s) from ring buffer
5772 return len;
5773 } // -x- if len -x-
5774
5775 // --------------------------------------------------------------------------
5776 // Consume data from socket to make up for OpenSSL's inability to read beyond
5777 // one packet of data in its SSL_peek() and SSL_read() methods, and also some
5778 // inconsistencies with certain raw (unencrypted) socket implementations that
5779 // occasionally refuse to read beyond one packet of data (at least on a first
5780 // attempt to read with or without the MSG_PEEK flag).
5781 //
5782 // Fill internal buffer. It may be more than len is set to, but this is okay
5783 // because we're committed to using internal buffering now anyway (it's far
5784 // more likely that a protocol that requires the use of recvline() even once
5785 // is going to be using it repeatedly, but as long as the ring buffer is
5786 // large enough to cover the maximum length of a line of text, including its
5787 // EoL sequence, then this will always succeed).
5788 // --------------------------------------------------------------------------
5789 __buffer_bam = __buffer->data_bam(__buffer_bam); // Update ring buffer setup (will be allocated automatically if "nullptr" default)
5790 if (__buffer_bam->avail1_size == 0) return 0; // Buffer is full, so return 0
5791 int n = __tls ? __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, __buffer_bam->avail1, __buffer_bam->avail1_size))) // Not using SSL_peek() at all here because we need to consume this data as we stuff it into the internal buffer
5792 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, __buffer_bam->avail1, __buffer_bam->avail1_size, posix_flags & (~MSG_PEEK)))); // Mask the MSG_PEEK flag because we need to consume this data as we stuff it into the internal buffer
5793 if (n == 0) return 0;
5794 __buffer->set_head(n, false); // Add number of bytes received to the ring buffer after writing to the available portion of the ring buffer
5795
5796 // --------------------------------------------------------------------------
5797 // Fill in the remaining (unused) portion of the internal buffer (if it's
5798 // available).
5799 //
5800 // Note: FIONREAD instructs ioctl() to acquire the number of bytes that are
5801 // immediately available for reading. Returns 0 when successful.
5802 // --------------------------------------------------------------------------
5803 if (n == __buffer_bam->avail1_size // First available block got filled
5804 && __buffer_bam->avail2_size > 0 // Second available block is not 0 (which means that more data can be stored there)
5805 && !eos()) { // More data is in stream, ready to be copied without any delay
5806 int n2 = __tls ? __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, __buffer_bam->avail2, __buffer_bam->avail2_size))) // Not using SSL_peek() at all here because we need to consume this data as we stuff it into the internal buffer
5807 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, __buffer_bam->avail2, __buffer_bam->avail2_size, posix_flags & (~MSG_PEEK)))); // Mask the MSG_PEEK flag because we need to consume this data as we stuff it into the internal buffer
5808 __buffer->set_head(n2, false); // Add number of bytes received to the ring buffer after writing to the available portion of the ring buffer
5809 n += n2; // Update n
5810 } // -x- if n -x-
5811
5812 // --------------------------------------------------------------------------
5813 // Copy or move data into destination.
5814 // --------------------------------------------------------------------------
5815 size_t utilized = std::min((size_t)len, __buffer->get_utilized()); // Specified length or utilized data in ring buffer, whichever is less
5816 if (posix_flags & MSG_PEEK) __buffer->peek_array( utilized, (char*)data); // Copy character(s) from ring buffer because MSG_PEEK flag is set
5817 else __buffer->remove_array(utilized, (char*)data); // Consume character(s) from ring buffer
5818 return n;
5819
5820 }; // -x- int __recv -x-
5821
5822 /*======================================================================*//**
5823 @brief
5824 This is an internal function that:
5825 1. receives a line of data from the endpoint:
5826 - via underlying socket when TLS is not enabled
5827 - via the OpenSSL socket API when TLS is enabled
5828 2. checks for a socket I/O error (and throws an exception)
5829 3. tracks number of bytes received (if there were no errors)
5830 4. isolates the EoL sequence from the line of data, and records it for
5831 later reference (for those cases where it's needed)
5832
5833 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
5834 @throws randolf::rex::xEBADF The underlying socket is not open
5835 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5836 connections
5837 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5838 part of the user address space
5839 @throws randolf::rex::xEINTR Interrupted by a signal
5840 @throws randolf::rex::xEINVAL Invalid argument passed
5841 @throws randolf::rex::xENOMEM Insufficient memory
5842 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5843 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5844 doesn't refer to a socket
5845 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
5846 no EoL character sequence is detected after recvline's buffer became
5847 full (whatever data is waiting may still be received using any of the
5848 recv_ methods {with or without the @c MSG_PEEK flag set}, including
5849 @c recvline() with a larger buffer {see the @c nbytes parameter})
5850 @throws randolf::rex::xERANGE if the timeout parameter is below 0
5851 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5852 there's no new data
5853
5854 @qualifier TLS
5855 *///=========================================================================
5856 std::string __recvline(
5857 /// Maximum number of bytes to receive (including EoL character sequence)
5858 const int buf_size,
5859 /// MSG_OOB@n
5860 /// MSG_PEEK (with some limitations)@n
5861 /// MSG_WAITALL@n
5862 /// MSG_DONTWAIT (ineffective when @c timeout is not 0)@n
5863 /// MSG_CMSG_CLOEXEC
5864 const int posix_flags,
5865 /// Line timeout (in seconds)@n
5866 /// 0 = no timeout (default), unless it was configured by way of the
5867 /// @ref timeout_recvline(long) method
5868 const ulong timeout,
5869 /// Configuration parameters
5870 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
5871
5872 // --------------------------------------------------------------------------
5873 // Syntax checks as part of preparation of timeout variable for faster
5874 // comparisons within line-reading loop. To avoid problems when wrapping
5875 // past ULONG_MAX, we mask out the most-significant bit by multiplying by two
5876 // and then dividing by two.
5877 // --------------------------------------------------------------------------
5878 //if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
5879 //if (timeout == 0) timeout = (__recvline_timeout * 2) / 2; // Convert 0 to
5880//std::cout << "timeout=" << timeout << std::endl;
5881 ulong timeout_target = timeout == 0 ? ULONG_MAX : timeout + time(0);
5882//std::cout << "timeout_target=" << timeout_target << std::endl;
5883
5884 // --------------------------------------------------------------------------
5885 // Allocate line buffer.
5886 // --------------------------------------------------------------------------
5887 std::string v; v.resize(buf_size);
5888
5889 // --------------------------------------------------------------------------
5890 // If an internal buffer, which is used by __recv, hasn't been instantiated,
5891 // then instantiate it and save the pointer to it for future use.
5892 //
5893 // Buffering becomes necessary with TLS connection because SSL_peek fails to
5894 // return data that spans multiple packets. What triggers this behaviour is
5895 // the command "stty -icanon && openssl s_client host_name tcp_port" followed
5896 // by manual interactive typing some text then pressing the "[Enter]" key.
5897 //
5898 // The ::malloc(__buffer_size) won't be leaked because it will be freed in
5899 // in this rsocket's destructor (if it isn't freed elsewhere before this).
5900 // --------------------------------------------------------------------------
5901 if (__buffer == nullptr) __buffer = new rring(__buffer_size);
5902
5903 int br = 0; // Number of Bytes Received
5904 int len_with_eol = 0;
5905 int len = -1;
5906
5907 // --------------------------------------------------------------------------
5908 // Line-reading loop.
5909 // --------------------------------------------------------------------------
5910 do {
5911
5912 // --------------------------------------------------------------------------
5913 // Attempt to read from the stream.
5914 // --------------------------------------------------------------------------
5915 try {
5916 br = __recv(v.data(), buf_size, posix_flags | MSG_PEEK); // Look-ahead at socket stream (with buffer size that was inflated earlier to include EoL sequence)
5917 } catch (const randolf::rex::xEAGAIN e) {
5918 // Socket timeout occurred, but ignore it so we can handle it separately
5919 }
5920std::cout << "br=" << br << std::endl;
5921 len = eol_index(v, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
5922 if (len >= 0) break;
5923 if (br == buf_size && len < 0) break;
5924
5925//std::cout << "br=" << br << " buf_size=" << buf_size << " v.size()=" << v.size() << " len=" << len << " len_with_eol=" << len_with_eol << " v[]=" << randolf::rtools::to_hex(v.substr(0, len < 0 ? 0 : 20), ":") << " t=" << timeout_target - time(0) << std::endl;
5926 // --------------------------------------------------------------------------
5927 // Wait for pending data for a duration no longer than the remaining timeout.
5928 // --------------------------------------------------------------------------
5929 if (eos(timeout == 0 ? timeout_target : timeout_target - time(0))) break;
5930
5931 // --------------------------------------------------------------------------
5932 // Keep going if we haven't timed out yet.
5933 // --------------------------------------------------------------------------
5934 } while (timeout == 0 || time(0) < timeout_target);
5935
5936std::cout << "break -- br=" << br << std::endl;
5937//std::cout << "br=" << br << " buf_size=" << buf_size << " v.size()=" << v.size() << " len=" << len << " len_with_eol=" << len_with_eol << " v[]=" << randolf::rtools::to_hex(v.substr(0, len < 0 ? 0 : 20), ":") << std::endl;
5938 // --------------------------------------------------------------------------
5939 // EoL sequence not found. Either the buffer is exhausted, or we're at the
5940 // end of the stream, with no EoL anywhere in sight.
5941 // --------------------------------------------------------------------------
5942 if (len < 0) {
5943 //std::cout << "overflow br=" << br << " buf_size=" << buf_size << std::endl; // Debug
5944
5945 // --------------------------------------------------------------------------
5946 // Caller wants partial data instead of throwing an exception, even if it has
5947 // no EoL sequence.
5948 // --------------------------------------------------------------------------
5949 if (recvline_flags && RECVLINE_PARTIAL) {
5950 __eol_consumed_seq = ""; // Indicate that there's no EoL sequence
5951 if (!(posix_flags & MSG_PEEK)) __recv(v.data(), br, posix_flags & (~MSG_PEEK)); // Consume data if not in MSG_PEEK mode / __recv will throw exception if something goes wrong so there's no need for an additional __rc_check call from here
5952 v.resize(br); // Truncate string to "br" (bytes received)
5953 return v;
5954 } // -x- if RECVLINE_PARTIAL -x-
5955
5956 // --------------------------------------------------------------------------
5957 // Generate xEOVERFLOW exception.
5958 //
5959 // TODO: Generate a different exception for no EoL when the buffer isn't
5960 // full.
5961 // --------------------------------------------------------------------------
5962 if (!(recvline_flags && RECVLINE_KEEP_ON_OVERFLOW) && !(posix_flags & MSG_PEEK)) {
5963 discard_line(); // Consume all that remains, or stop after first EoL sequence encountered
5964std::cout << "discarded" << std::endl;
5965 }
5966 throw randolf::rex::xEOVERFLOW("recvline overflow (line too long or missing EoL sequence)");
5967
5968 } // -x- if !len -x-
5969
5970 // --------------------------------------------------------------------------
5971 // Save the EoL sequence that was detected following a successful recvline()
5972 // (this must be completed prior to "final clean-up" {which is next}, because
5973 // we need to copy these characters before the truncation occurs).
5974 // --------------------------------------------------------------------------
5975//std::cout << "len=" << len << " len_with_eol=" << len_with_eol << " __buffer.get_utilized()=" << __buffer->get_utilized() << std::endl;
5976 __eol_consumed_seq = v.substr(len, len_with_eol - len);
5977
5978 // --------------------------------------------------------------------------
5979 // Final clean-up.
5980 // --------------------------------------------------------------------------
5981 if (!(posix_flags & MSG_PEEK)) __recv(v.data(), len_with_eol, posix_flags & (~MSG_PEEK)); // Consume data if not in MSG_PEEK mode / __recv will throw exception if something goes wrong so there's no need for an additional __rc_check call from here
5982 //if ((posix_flags & MSG_PEEK) == 0) __recv(v.data(), len_with_eol, posix_flags); // Consume data if not in MSG_PEEK mode / __recv will throw exception if something goes wrong so there's no need for an additional __rc_check call from here
5983 v.resize(len); // Truncate string to exclude unused portion (and we're also not including the EoL sequence)
5984 return v;
5985
5986 }; // -x- std::string __recvline -x-
5987
5988 public:
5989 /*======================================================================*//**
5990 @brief
5991 Receive a line of data from the endpoint, with the EoL character(s) removed.
5992 While this is meant for ASCII and UTF-8 text, it will also work with binary
5993 data that doesn't include EoL character sequences as non-line-ending data
5994 (when receiving binary data, the @ref recv() and @ref recvz() methods tend to
5995 be better-suited).
5996
5997 If @c nbytes is 0, then the internal buffer_size will be used as the default,
5998 which can also be changed from its compiled-in default of 1024 by using one
5999 of the buffer_size() methods. The total number of bytes received, including
6000 the EoL sequence, will not exceed the total number of byte specified with the
6001 @c nbytes parameter.
6002 @warning
6003 For @c nbytes the EoL character sequence size should be included, especially
6004 for shorter lines -- if the EoL character sequence is detected automatically
6005 on-the-fly (which is the default), then provisioning 2 characters is needed
6006 since an EoL sequence could be 1 or 2 characters long (line lengths shorter
6007 than 3 characters could be particularly problematic when detecting on-the-fly
6008 EoL character sequences). In such a case, a better approach is to allow for
6009 2 additional characters and then test the length of the returned string to
6010 ensure it doesn't exceed whatever the required maximum is (and throw the same
6011 @ref randolf::rex::xEOVERFLOW exception if the length is exceeded).
6012
6013 For more information about which characters an EoL sequence is comprised of,
6014 and how our specialized support for multiple EoL sequences works, see the
6015 documentation for the various @ref eol methods.
6016
6017 @pre
6018 When setting the recvline timeout (either with the @c timeout parameter with
6019 this method, or by using the @ref timeout_recvline(long) method), you may
6020 also need to set the socket timeout beforehand using the @ref timeout()
6021 method, because @c recvline doesn't do this...
6022 @n
6023 <blockquote>
6024 @ref timeout_recvline sets the overall total timeout for an entire line to
6025 be entered
6026 @n
6027 @ref timeout sets the timeout between individual characters received (such
6028 as keystrokes from a live end-user)
6029 </blockquote>
6030 @n
6031 If your socket timeout is longer than then recvline timeout, this will have
6032 the effect of rendering the recvling timeout as ineffective.
6033
6034 The @c timeout parameter can be used to override the total overall timeout
6035 for receiving a line, which is useful for specific situations where a
6036 specific timeout is desired for particular prompts, during certain data entry
6037 stages, etc.
6038
6039 @post
6040 When a line is not found (@c randolf::rex::xEAGAIN), or a line is too long
6041 because there's too much data (longer than a line) without an EoL sequence
6042 (@c randolf::rex::xOVERFLOW), then that data will not be discarded or
6043 consumed, and will remain ready for receiving (@ref recv) or for discarding
6044 @ref discard).
6045
6046 @par Notes
6047 If you're searching for a readline() method for socket I/O, then this is most
6048 likely what you're wanting/needing.
6049
6050 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
6051 @throws randolf::rex::xEBADF The underlying socket is not open
6052 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6053 connections
6054 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6055 part of the user address space
6056 @throws randolf::rex::xEINTR Interrupted by a signal
6057 @throws randolf::rex::xEINVAL Invalid argument passed
6058 @throws randolf::rex::xENOMEM Insufficient memory
6059 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6060 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6061 doesn't refer to a socket
6062 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
6063 no EoL character sequence is detected after recvline's buffer became
6064 full (whatever data is waiting may still be received using any of the
6065 recv_ methods {with or without the @c MSG_PEEK flag set}, including
6066 @c recvline() with a larger buffer {see the @c nbytes parameter})
6067 @throws randolf::rex::xERANGE if the timeout parameter is below 0
6068 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6069 there's no new data
6070
6071 @returns One line of text as a std::string object.
6072 @see discard
6073 @see eol()
6074 @see eol_consumed_seq()
6075 @see recv_rline
6076 @see sendline(const std::string, const int)
6077 @see timeout
6078 @see timeout_recvline
6079 @see timeout_recvline(long)
6080 @qualifier TLS
6081 *///=========================================================================
6082 std::string recvline(
6083 /// Maximum number of bytes to receive (including EoL character sequence)@n
6084 /// 0 = use internal @ref buffer_size()
6085 const size_t nbytes = 0,
6086 /// MSG_OOB@n
6087 /// MSG_PEEK@n
6088 /// MSG_WAITALL@n
6089 /// MSG_DONTWAIT@n
6090 /// MSG_CMSG_CLOEXEC
6091 const int posix_flags = 0,
6092 /// Line timeout (in seconds)@n
6093 /// 0 = no timeout (default), unless it was configured by way of the
6094 /// @ref timeout_recvline(long) method
6095 long timeout = 0,
6096 /// Configuration parameters
6097 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
6098 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
6099 // + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF; TODO: Add an option for this
6100 // Internally, the length of the @ref eol() sequence is added to the buffer size
6101 // so that the maximum line length without EoL characters matches the maximum
6102 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
6103 if (__debug) debug("recvline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6104 + ", " + std::to_string(buf_size)
6105 + ", " + std::to_string(posix_flags)
6106 + ");");
6107 return __recvline(buf_size, posix_flags, timeout, recvline_flags);
6108 }; // -x- std::string recvline -x-
6109
6110 /*======================================================================*//**
6111 @brief
6112 Receive data in the form of a "msghdr" structure.
6113 @warning
6114 This method is not compatible with TLS.
6115
6116 @throws randolf::rex::xEBADF The underlying socket is not open
6117 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6118 connections
6119 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6120 part of the user address space
6121 @throws randolf::rex::xEINTR Interrupted by a signal
6122 @throws randolf::rex::xEINVAL Invalid argument passed
6123 @throws randolf::rex::xENOMEM Insufficient memory
6124 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6125 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6126 doesn't refer to a socket
6127 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6128 there's no new data
6129
6130 @returns pointer to "msghdr" structure
6131 @qualifier POSIX
6132 *///=========================================================================
6133 msghdr* recvmsg(
6134 /// Pointer to "msghdr" structure (or nullptr for automatic allocation)
6135 msghdr* msg,
6136 /// MSG_OOB@n
6137 /// MSG_PEEK@n
6138 /// MSG_WAITALL@n
6139 /// MSG_DONTWAIT@n
6140 /// MSG_CMSG_CLOEXEC
6141 const int posix_flags = 0) {
6142 if (__debug) debug("recvmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6143 + ", <msghdr>"
6144 + ", " + std::to_string(posix_flags)
6145 + ");");
6146 if (msg == nullptr) msg = new msghdr{0};
6147 __track_bytes_rx(__rc_check(::recvmsg(__socket_fd, msg, posix_flags)));
6148 return msg;
6149 }; // -x- msghdr* recvmsg -x-
6150
6151 /*======================================================================*//**
6152 @brief
6153 Receive data in the form of an "mmsghdr" structure.
6154 @warning
6155 This method is not compatible with TLS.
6156
6157 @throws randolf::rex::xEBADF The underlying socket is not open
6158 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6159 connections
6160 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6161 part of the user address space
6162 @throws randolf::rex::xEINTR Interrupted by a signal
6163 @throws randolf::rex::xEINVAL Invalid argument passed
6164 @throws randolf::rex::xENOMEM Insufficient memory
6165 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6166 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6167 doesn't refer to a socket
6168 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6169 there's no new data
6170
6171 @returns pointer to "mmsghdr" structure
6172 @qualifier POSIX
6173 *///=========================================================================
6174 mmsghdr* recvmmsg(
6175 /// Pointer to "mmsghdr" structure (or nullptr for automatic allocation)
6176 struct mmsghdr* mmsg,
6177 /// Size of target endpoint structure
6178 const unsigned int vlen = sizeof(mmsghdr),
6179 /// MSG_DONTROUTE@n
6180 /// MSG_DONTWAIT@n
6181 /// MSG_EOR@n
6182 /// MSG_NOSIGNAL@n
6183 /// MSG_OOB
6184 const int posix_flags = 0,
6185 /// Timeout
6186 struct timespec* timeout = {0}) {
6187 if (__debug) debug("recvmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6188 + ", <mmsghdr>"
6189 + ", " + std::to_string(vlen)
6190 + ", " + std::to_string(posix_flags)
6191 + ", timeout{tv_sec=" + std::to_string(timeout->tv_sec)
6192 + ", tv_nsec=" + std::to_string(timeout->tv_nsec) + "}"
6193 + ");");
6194 __track_bytes_rx(__rc_check(::recvmmsg(__socket_fd, mmsg, vlen, posix_flags, timeout)));
6195 return mmsg;
6196 }; // -x- mmsghdr* recvmmsg -x-
6197
6198 /*======================================================================*//**
6199 @brief
6200 Receive data from the endpoint, and add a 0 (null) onto the end. This is
6201 useful when using the resulting std::vector<char> as an ASCIIZ string.
6202
6203 If nbytes is 0, then the internal buffer_size will be used as the default,
6204 which can also be changed from its compiled-in default of 1024 by using one
6205 of the buffer_size() methods.
6206
6207 @post
6208 The resulting std::vector size is always inflated by 1. This means that
6209 relying on a comparison against 0 will result in an infinite loop:
6210 @code{.cpp}
6211 for (std::vector<char> v = r.recvz(); v.size > 0; v = recvz()) { ... }
6212 @endcode
6213 So, you'll need to compare against 1 instead of 0 to compensate fot the
6214 inflated size due to the addition of null character 0:
6215 @code{.cpp}
6216 for (std::vector<char> v = r.recvz(); v.size > 1; v = recvz()) { ... }
6217 @endcode
6218
6219 @warning
6220 The resulting size of std::vector<char> will be <tt>nbytes + 1</tt> which
6221 ensures that any string processing functions or presentation libraries - such
6222 as @c printf() - expecting an ASCIIZ string buffer will stop at null (0), and
6223 will reliably output no more than the maximum size specified.
6224 @n@n
6225 The way to think of this is that @c nbytes specifies the maximum number of
6226 bytes (a.k.a., characters) to recieve over the underlying socket, and the
6227 final 0 (null) being added is not included in the maximum specified by the
6228 @c nbytes parameter.
6229
6230 @par Notes
6231 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6232 reception, but it's important to note that it does implement temporary
6233 blocking while waiting for data.
6234
6235 @throws randolf::rex::xEBADF The underlying socket is not open
6236 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6237 connections
6238 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6239 part of the user address space
6240 @throws randolf::rex::xEINTR Interrupted by a signal
6241 @throws randolf::rex::xEINVAL Invalid argument passed
6242 @throws randolf::rex::xENOMEM Insufficient memory
6243 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6244 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6245 doesn't refer to a socket
6246 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6247 there's no new data
6248
6249 @returns appropriately-sized vector of characters + 1 (includes additional
6250 null terminator character 0)
6251 @see recv(const size_t, const int)
6252 @see recv(std::vector<char>, const int)
6253 @qualifier TLS
6254 *///=========================================================================
6255 std::vector<char> recvz(
6256 /// Maximum number of bytes to receive
6257 const size_t nbytes = 0,
6258 /// MSG_OOB@n
6259 /// MSG_PEEK@n
6260 /// MSG_WAITALL@n
6261 /// MSG_DONTWAIT@n
6262 /// MSG_CMSG_CLOEXEC
6263 const int posix_flags = 0) {
6264 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6265 if (__debug) debug("recvz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6266 + ", <buf>"
6267 + ", " + std::to_string(buf_size)
6268 + ", " + std::to_string(posix_flags)
6269 + ");");
6270 std::vector<char> v(buf_size);
6271 v.resize(__recv(v.data(), v.size(), posix_flags) + 1); // Add 1 to include room for final null character 0
6272 v[v.size() - 1] = (char)0; // Set final element to 0 (null)
6273 return v;
6274 }; // -x- std::vector<char> recvz -x-
6275
6276 private:
6277 /*======================================================================*//**
6278 @brief
6279 This is an internal function that:
6280 1. sends data to the endpoint:
6281 - via underlying socket when TLS is not enabled
6282 - via the OpenSSL socket API when TLS is enabled
6283 2. checks for a socket I/O error (and throws an exception)
6284 3. tracks number of bytes transmitted (if there were no errors)
6285
6286 @par Threads
6287 This method is threadsafe because the underlying POSIX send() and OpenSSL's
6288 SSL_write() functions are threadsafe.
6289
6290 @throws randolf::rex::xEBADF The underlying socket is not open
6291 @throws randolf::rex::xECONNRESET Connect reset by peer
6292 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6293 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6294 part of the user address space
6295 @throws randolf::rex::xEINTR Interrupted by a signal
6296 @throws randolf::rex::xEINVAL Invalid argument passed
6297 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6298 occur, but the POSIX sockets documentation lists it as one of the
6299 errors that can be returned, perhaps because some incorrectly
6300 implemented TCP/IP stacks return this error?)
6301 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6302 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6303 and 65,527 bytes for IPv6)
6304 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6305 network congestion (or, less commonly, insufficient memory)
6306 @throws randolf::rex::xENOMEM Insufficient memory
6307 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6308 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6309 doesn't refer to a socket
6310 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6311 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6312 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6313 isn't set)
6314 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6315 there's no new data
6316 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6317 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6318 but it really isn't)
6319
6320 @returns Number of bytes that were successfully transmitted
6321 @qualifier TLS
6322 *///=========================================================================
6323 int __send(
6324 /// Pointer to data
6325 const void* data,
6326 /// Length of message data
6327 const size_t len,
6328 /// Flags (ignored with encrypted streams)
6329 const int posix_flags = 0) {
6330 if (__tls) return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data, len)));
6331 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data, len, posix_flags)));
6332 }; // -x- int __send -x-
6333
6334 /*======================================================================*//**
6335 @brief
6336 This is an internal function that:
6337 1. sends data to the endpoint:
6338 - via underlying socket when TLS is not enabled
6339 - via the OpenSSL socket API when TLS is enabled
6340 2. checks for a socket I/O error (and throws an exception)
6341 3. tracks number of bytes transmitted (if there were no errors)
6342
6343 @par Threads
6344 This method is threadsafe because the underlying POSIX send() and OpenSSL's
6345 SSL_write() functions are threadsafe.
6346
6347 @throws randolf::rex::xEBADF The underlying socket is not open
6348 @throws randolf::rex::xECONNRESET Connect reset by peer
6349 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6350 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6351 part of the user address space
6352 @throws randolf::rex::xEINTR Interrupted by a signal
6353 @throws randolf::rex::xEINVAL Invalid argument passed
6354 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6355 occur, but the POSIX sockets documentation lists it as one of the
6356 errors that can be returned, perhaps because some incorrectly
6357 implemented TCP/IP stacks return this error?)
6358 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6359 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6360 and 65,527 bytes for IPv6)
6361 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6362 network congestion (or, less commonly, insufficient memory)
6363 @throws randolf::rex::xENOMEM Insufficient memory
6364 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6365 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6366 doesn't refer to a socket
6367 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6368 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6369 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6370 isn't set)
6371 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6372 there's no new data
6373 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6374 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6375 but it really isn't)
6376
6377 @returns Number of bytes that were successfully transmitted
6378 @qualifier TLS
6379 *///=========================================================================
6380 int __sendline(
6381 /// Pointer to data
6382 const void* data,
6383 /// Number of bytes to send
6384 const size_t len,
6385 /// Flags (ignored with encrypted streams)
6386 const int posix_flags = 0) {
6387 if (__tls) { // Encrypted
6388 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data , len )))
6389 + __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.c_str(), __eol_out.length())));
6390 } else { // Not encrypted
6391 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data , len , posix_flags | MSG_MORE))) // MSG_MORE prevents extra packets from being sent
6392 + __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.c_str(), __eol_out.length(), posix_flags )));
6393 } // -x- if __tls -x-
6394 }; // -x- int __sendline -x-
6395
6396 /*======================================================================*//**
6397 @brief
6398 This is an internal function that:
6399 1. sends data to the endpoint:
6400 - via underlying socket when TLS is not enabled
6401 - via the OpenSSL socket API when TLS is enabled
6402 2. checks for a socket I/O error (and throws an exception)
6403 3. tracks number of bytes transmitted (if there were no errors)
6404
6405 @par Threads
6406 This method is threadsafe because the underlying POSIX send() and OpenSSL's
6407 SSL_write() functions are threadsafe.
6408
6409 @throws randolf::rex::xEBADF The underlying socket is not open
6410 @throws randolf::rex::xECONNRESET Connect reset by peer
6411 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6412 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6413 part of the user address space
6414 @throws randolf::rex::xEINTR Interrupted by a signal
6415 @throws randolf::rex::xEINVAL Invalid argument passed
6416 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6417 occur, but the POSIX sockets documentation lists it as one of the
6418 errors that can be returned, perhaps because some incorrectly
6419 implemented TCP/IP stacks return this error?)
6420 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6421 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6422 and 65,527 bytes for IPv6)
6423 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6424 network congestion (or, less commonly, insufficient memory)
6425 @throws randolf::rex::xENOMEM Insufficient memory
6426 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6427 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6428 doesn't refer to a socket
6429 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6430 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6431 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6432 isn't set)
6433 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6434 there's no new data
6435 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6436 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6437 but it really isn't)
6438
6439 @returns Number of bytes that were successfully transmitted
6440 @qualifier TLS
6441 *///=========================================================================
6442 int __send_eol(
6443 /// Flags (ignored with encrypted streams)
6444 const int posix_flags = 0) {
6445 if (__tls) { // Encrypted
6446 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.c_str(), __eol_out.length())));
6447 } else { // Not encrypted
6448 return __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.c_str(), __eol_out.length(), posix_flags)));
6449 } // -x- if __tls -x-
6450 }; // -x- int __send_eol -x-
6451 // TODO: Create a recv_eol() method that some people will probably consider to be evil (ha ha!)
6452 // recv_eol() will need to be able to auto-detect the CRLF sequence on-the-fly (if necessary) just like recvline() does
6453
6454 public:
6455 /*======================================================================*//**
6456 @brief
6457 Send data in the form of a std::string to the endpoint.
6458 @par Threads
6459 This method is threadsafe.
6460
6461 @throws randolf::rex::xEBADF The underlying socket is not open
6462 @throws randolf::rex::xECONNRESET Connect reset by peer
6463 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6464 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6465 part of the user address space
6466 @throws randolf::rex::xEINTR Interrupted by a signal
6467 @throws randolf::rex::xEINVAL Invalid argument passed
6468 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6469 occur, but the POSIX sockets documentation lists it as one of the
6470 errors that can be returned, perhaps because some incorrectly
6471 implemented TCP/IP stacks return this error?)
6472 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6473 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6474 and 65,527 bytes for IPv6)
6475 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6476 network congestion (or, less commonly, insufficient memory)
6477 @throws randolf::rex::xENOMEM Insufficient memory
6478 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6479 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6480 doesn't refer to a socket
6481 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6482 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6483 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6484 isn't set)
6485 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6486 there's no new data
6487 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6488 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6489 but it really isn't)
6490
6491 @returns The same rsocket object so as to facilitate stacking
6492 @qualifier TLS
6493 *///=========================================================================
6494 rsocket* send(
6495 /// Data to send
6496 const std::string msg,
6497 /// MSG_DONTROUTE@n
6498 /// MSG_DONTWAIT@n
6499 /// MSG_EOR@n
6500 /// MSG_NOSIGNAL@n
6501 /// MSG_OOB
6502 const int posix_flags = 0) {
6503 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6504 + ", <std::string>"
6505 + ", " + std::to_string(msg.length())
6506 + ", " + std::to_string(posix_flags)
6507 + ");");
6508 __send(msg.c_str(), msg.length(), posix_flags);
6509 return this;
6510 }; // -x- rsocket* send -x-
6511
6512 /*======================================================================*//**
6513 @brief
6514 Send data in the form of a std::vector<char> to the endpoint.
6515 @par Threads
6516 This method is threadsafe.
6517
6518 @throws randolf::rex::xEBADF The underlying socket is not open
6519 @throws randolf::rex::xECONNRESET Connect reset by peer
6520 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6521 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6522 part of the user address space
6523 @throws randolf::rex::xEINTR Interrupted by a signal
6524 @throws randolf::rex::xEINVAL Invalid argument passed
6525 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6526 occur, but the POSIX sockets documentation lists it as one of the
6527 errors that can be returned, perhaps because some incorrectly
6528 implemented TCP/IP stacks return this error?)
6529 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6530 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6531 and 65,527 bytes for IPv6)
6532 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6533 network congestion (or, less commonly, insufficient memory)
6534 @throws randolf::rex::xENOMEM Insufficient memory
6535 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6536 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6537 doesn't refer to a socket
6538 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6539 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6540 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6541 isn't set)
6542 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6543 there's no new data
6544 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6545 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6546 but it really isn't)
6547
6548 @returns The same rsocket object so as to facilitate stacking
6549 @qualifier TLS
6550 *///=========================================================================
6551 rsocket* send(
6552 /// Data to send
6553 const std::vector<char> msg,
6554 /// MSG_DONTROUTE@n
6555 /// MSG_DONTWAIT@n
6556 /// MSG_EOR@n
6557 /// MSG_NOSIGNAL@n
6558 /// MSG_OOB
6559 const int posix_flags = 0) {
6560 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6561 + ", <std::vector>"
6562 + ", " + std::to_string(msg.size())
6563 + ", " + std::to_string(posix_flags)
6564 + ");");
6565 __send(msg.data(), msg.size(), posix_flags);
6566 return this;
6567 }; // -x- rsocket* send -x-
6568
6569 /*======================================================================*//**
6570 @brief
6571 Send data in the form of a C-string to the endpoint.
6572 @par Threads
6573 This method is threadsafe.
6574
6575 @throws randolf::rex::xEBADF The underlying socket is not open
6576 @throws randolf::rex::xECONNRESET Connect reset by peer
6577 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6578 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6579 part of the user address space
6580 @throws randolf::rex::xEINTR Interrupted by a signal
6581 @throws randolf::rex::xEINVAL Invalid argument passed
6582 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6583 occur, but the POSIX sockets documentation lists it as one of the
6584 errors that can be returned, perhaps because some incorrectly
6585 implemented TCP/IP stacks return this error?)
6586 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6587 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6588 and 65,527 bytes for IPv6)
6589 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6590 network congestion (or, less commonly, insufficient memory)
6591 @throws randolf::rex::xENOMEM Insufficient memory
6592 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6593 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6594 doesn't refer to a socket
6595 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6596 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6597 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6598 isn't set)
6599 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6600 there's no new data
6601 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6602 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6603 but it really isn't)
6604
6605 @returns The same rsocket object so as to facilitate stacking
6606 @qualifier POSIX
6607 @qualifier TLS
6608 *///=========================================================================
6609 rsocket* send(
6610 /// Pointer to data to send
6611 const char* msg,
6612 /// Number of bytes to send, or 0 to auto-detect length if message is an ASCIIZ string
6613 size_t len = 0,
6614 /// MSG_DONTROUTE@n
6615 /// MSG_DONTWAIT@n
6616 /// MSG_EOR@n
6617 /// MSG_NOSIGNAL@n
6618 /// MSG_OOB
6619 const int posix_flags = 0) {
6620 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6621 + ", <msg>"
6622 + ", " + std::to_string(len)
6623 + ", " + std::to_string(posix_flags)
6624 + ");");
6625
6626 // --------------------------------------------------------------------------
6627 // Measure size of format string if an ASCIIZ string was indicated.
6628 // --------------------------------------------------------------------------
6629 if (len == 0) len = std::strlen(msg);
6630
6631 // --------------------------------------------------------------------------
6632 // Send string.
6633 // --------------------------------------------------------------------------
6634 __send(msg, len, posix_flags);
6635 return this;
6636 }; // -x- rsocket* send -x-
6637
6638 /*======================================================================*//**
6639 @brief
6640 Send data in the form of an ASCIIZ string to the endpoint, including the
6641 terminating NULL character.
6642 @par Threads
6643 This method is threadsafe.
6644
6645 @throws randolf::rex::xEBADF The underlying socket is not open
6646 @throws randolf::rex::xECONNRESET Connect reset by peer
6647 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6648 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6649 part of the user address space
6650 @throws randolf::rex::xEINTR Interrupted by a signal
6651 @throws randolf::rex::xEINVAL Invalid argument passed
6652 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6653 occur, but the POSIX sockets documentation lists it as one of the
6654 errors that can be returned, perhaps because some incorrectly
6655 implemented TCP/IP stacks return this error?)
6656 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6657 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6658 and 65,527 bytes for IPv6)
6659 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6660 network congestion (or, less commonly, insufficient memory)
6661 @throws randolf::rex::xENOMEM Insufficient memory
6662 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6663 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6664 doesn't refer to a socket
6665 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6666 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6667 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6668 isn't set)
6669 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6670 there's no new data
6671 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6672 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6673 but it really isn't)
6674
6675 @returns The same rsocket object so as to facilitate stacking
6676 @see recv_asciiz(const size_t, const int)
6677 @see sendz(const char*, const int) which doesn't transmit the terminating
6678 NULL character
6679 @qualifier TLS
6680 *///=========================================================================
6681 rsocket* send_asciiz(
6682 /// Pointer to data to send
6683 const char* msg,
6684 /// MSG_DONTROUTE@n
6685 /// MSG_DONTWAIT@n
6686 /// MSG_EOR@n
6687 /// MSG_NOSIGNAL@n
6688 /// MSG_OOB
6689 const int posix_flags = 0) {
6690 if (__debug) debug("send_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6691 + ", <msg>"
6692 + ", " + std::to_string(std::strlen(msg) + 1)
6693 + ", " + std::to_string(posix_flags)
6694 + ");");
6695 __send(msg, std::strlen(msg) + 1, posix_flags);
6696 return this;
6697 }; // -x- rsocket* send_asciiz -x-
6698
6699 /*======================================================================*//**
6700 @brief
6701 Send one 8-bit byte (one unsigned character) of data to the endpoint.
6702 @par Threads
6703 This method is threadsafe.
6704
6705 @throws randolf::rex::xEBADF The underlying socket is not open
6706 @throws randolf::rex::xECONNRESET Connect reset by peer
6707 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6708 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6709 part of the user address space
6710 @throws randolf::rex::xEINTR Interrupted by a signal
6711 @throws randolf::rex::xEINVAL Invalid argument passed
6712 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6713 occur, but the POSIX sockets documentation lists it as one of the
6714 errors that can be returned, perhaps because some incorrectly
6715 implemented TCP/IP stacks return this error?)
6716 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6717 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6718 and 65,527 bytes for IPv6)
6719 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6720 network congestion (or, less commonly, insufficient memory)
6721 @throws randolf::rex::xENOMEM Insufficient memory
6722 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6723 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6724 doesn't refer to a socket
6725 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6726 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6727 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6728 isn't set)
6729 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6730 there's no new data
6731 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6732 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6733 but it really isn't)
6734
6735 @returns The same rsocket object so as to facilitate stacking
6736 @see recv_byte
6737 @see recv_char
6738 @see send_char
6739 @qualifier TLS
6740 *///=========================================================================
6741 rsocket* send_byte(
6742 /// Data to send
6743 const u_char value,
6744 /// MSG_DONTROUTE@n
6745 /// MSG_DONTWAIT@n
6746 /// MSG_EOR@n
6747 /// MSG_NOSIGNAL@n
6748 /// MSG_OOB
6749 const int posix_flags = 0) {
6750 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6751 + ", <value>"
6752 + ", " + std::to_string(sizeof(value))
6753 + ", " + std::to_string(posix_flags)
6754 + ");");
6755 __send(&value, sizeof(value), posix_flags);
6756 return this;
6757 }; // -x- rsocket* send_byte -x-
6758
6759 /*======================================================================*//**
6760 @brief
6761 Send one signed character (one 8-bit byte) of data to the endpoint.
6762 @copydoc send_byte(char, int)
6763 @returns The same rsocket object so as to facilitate stacking
6764 @see recv_byte
6765 @see recv_char
6766 @see send_byte
6767 @qualifier TLS
6768 *///=========================================================================
6769 rsocket* send_char(
6770 /// Data to send
6771 const char value,
6772 /// MSG_DONTROUTE@n
6773 /// MSG_DONTWAIT@n
6774 /// MSG_EOR@n
6775 /// MSG_NOSIGNAL@n
6776 /// MSG_OOB
6777 const int posix_flags = 0) {
6778 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6779 + ", <value>"
6780 + ", " + std::to_string(sizeof(value))
6781 + ", " + std::to_string(posix_flags)
6782 + ");");
6783 __send(&value, sizeof(value), posix_flags);
6784 return this;
6785 }; // -x- rsocket* send_char -x-
6786
6787 /*======================================================================*//**
6788 @brief
6789 Send the EoL sequence to the endpoint.
6790 @par Threads
6791 This method is threadsafe.
6792
6793 @throws randolf::rex::xEBADF The underlying socket is not open
6794 @throws randolf::rex::xECONNRESET Connect reset by peer
6795 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6796 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6797 part of the user address space
6798 @throws randolf::rex::xEINTR Interrupted by a signal
6799 @throws randolf::rex::xEINVAL Invalid argument passed
6800 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6801 occur, but the POSIX sockets documentation lists it as one of the
6802 errors that can be returned, perhaps because some incorrectly
6803 implemented TCP/IP stacks return this error?)
6804 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6805 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6806 and 65,527 bytes for IPv6)
6807 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6808 network congestion (or, less commonly, insufficient memory)
6809 @throws randolf::rex::xENOMEM Insufficient memory
6810 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6811 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6812 doesn't refer to a socket
6813 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6814 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6815 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6816 isn't set)
6817 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6818 there's no new data
6819 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6820 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6821 but it really isn't)
6822
6823 @returns The same rsocket object so as to facilitate stacking
6824 @qualifier TLS
6825 *///=========================================================================
6826 rsocket* send_eol(
6827 /// MSG_DONTROUTE@n
6828 /// MSG_DONTWAIT@n
6829 /// MSG_EOR@n
6830 /// MSG_NOSIGNAL@n
6831 /// MSG_OOB
6832 const int posix_flags = 0) {
6833 if (__debug) debug("send_eol(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6834 + ", " + std::to_string(posix_flags)
6835 + ");");
6836 __send_eol(posix_flags);
6837 return this;
6838 }; // -x- rsocket* send_eol -x-
6839
6840 /*======================================================================*//**
6841 @brief
6842 Send a data structure to the endpoint.
6843 @pre
6844 MSB/LSB considerations are important for any integers within your structure,
6845 so be sure to sanitize them with htons(), ntohs(), and related methods prior
6846 to sending, and then after receiving. This way, your data will be protected
6847 against corruption resulting from byte order differences when communicating
6848 between hardware architectures that differ in MSB and LSB byte ordering.
6849 @par Threads
6850 This method is threadsafe.
6851
6852 @throws randolf::rex::xEBADF The underlying socket is not open
6853 @throws randolf::rex::xECONNRESET Connect reset by peer
6854 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6855 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6856 part of the user address space
6857 @throws randolf::rex::xEINTR Interrupted by a signal
6858 @throws randolf::rex::xEINVAL Invalid argument passed
6859 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6860 occur, but the POSIX sockets documentation lists it as one of the
6861 errors that can be returned, perhaps because some incorrectly
6862 implemented TCP/IP stacks return this error?)
6863 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6864 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6865 and 65,527 bytes for IPv6)
6866 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6867 network congestion (or, less commonly, insufficient memory)
6868 @throws randolf::rex::xENOMEM Insufficient memory
6869 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6870 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6871 doesn't refer to a socket
6872 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6873 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6874 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6875 isn't set)
6876 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6877 there's no new data
6878 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6879 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6880 but it really isn't)
6881
6882 @returns The same rsocket object so as to facilitate stacking
6883 @see recv_struct
6884 @qualifier TLS
6885 *///=========================================================================
6886 template <typename T> rsocket* send_struct(
6887 /// Data to send
6888 const T value,
6889 /// MSG_DONTROUTE@n
6890 /// MSG_DONTWAIT@n
6891 /// MSG_EOR@n
6892 /// MSG_NOSIGNAL@n
6893 /// MSG_OOB
6894 const int posix_flags = 0) {
6895 if (__debug) debug("send_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6896 + ", <value>"
6897 + ", " + std::to_string(sizeof(value))
6898 + ", " + std::to_string(posix_flags)
6899 + ");");
6900 __send(&value, sizeof(value), posix_flags);
6901 return this;
6902 }; // -x- rsocket* send_struct -x-
6903
6904 /*======================================================================*//**
6905 @brief
6906 Send one 16-bit unsigned integer of data in LSB (little endian) order to the
6907 endpoint.
6908 @par Threads
6909 This method is threadsafe.
6910
6911 @throws randolf::rex::xEBADF The underlying socket is not open
6912 @throws randolf::rex::xECONNRESET Connect reset by peer
6913 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6914 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6915 part of the user address space
6916 @throws randolf::rex::xEINTR Interrupted by a signal
6917 @throws randolf::rex::xEINVAL Invalid argument passed
6918 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6919 occur, but the POSIX sockets documentation lists it as one of the
6920 errors that can be returned, perhaps because some incorrectly
6921 implemented TCP/IP stacks return this error?)
6922 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6923 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6924 and 65,527 bytes for IPv6)
6925 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6926 network congestion (or, less commonly, insufficient memory)
6927 @throws randolf::rex::xENOMEM Insufficient memory
6928 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6929 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6930 doesn't refer to a socket
6931 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6932 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6933 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6934 isn't set)
6935 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6936 there's no new data
6937 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6938 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6939 but it really isn't)
6940
6941 @returns The same rsocket object so as to facilitate stacking
6942 @qualifier TLS
6943 *///=========================================================================
6944 rsocket* send_uint16_lsb(
6945 /// Data to send
6946 const uint16_t value,
6947 /// MSG_DONTROUTE@n
6948 /// MSG_DONTWAIT@n
6949 /// MSG_EOR@n
6950 /// MSG_NOSIGNAL@n
6951 /// MSG_OOB
6952 const int posix_flags = 0) {
6953 uint16_t buf = !__endian_is_msb ? value : ntohs(value);
6954 if (__debug) debug("send_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6955 + ", " + std::to_string(value)
6956 + ", " + std::to_string(sizeof(buf))
6957 + ", " + std::to_string(posix_flags)
6958 + ");");
6959 __send(&buf, sizeof(buf), posix_flags);
6960 return this;
6961 }; // -x- rsocket* send_uint16_lsb -x-
6962
6963 /*======================================================================*//**
6964 @brief
6965 Send one 16-bit integer of data in MSB (big endian) order to the endpoint.
6966 @par Threads
6967 This method is threadsafe.
6968
6969 @throws randolf::rex::xEBADF The underlying socket is not open
6970 @throws randolf::rex::xECONNRESET Connect reset by peer
6971 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6972 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6973 part of the user address space
6974 @throws randolf::rex::xEINTR Interrupted by a signal
6975 @throws randolf::rex::xEINVAL Invalid argument passed
6976 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6977 occur, but the POSIX sockets documentation lists it as one of the
6978 errors that can be returned, perhaps because some incorrectly
6979 implemented TCP/IP stacks return this error?)
6980 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6981 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6982 and 65,527 bytes for IPv6)
6983 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6984 network congestion (or, less commonly, insufficient memory)
6985 @throws randolf::rex::xENOMEM Insufficient memory
6986 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6987 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6988 doesn't refer to a socket
6989 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6990 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6991 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6992 isn't set)
6993 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6994 there's no new data
6995 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6996 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6997 but it really isn't)
6998
6999 @returns The same rsocket object so as to facilitate stacking
7000 @qualifier TLS
7001 *///=========================================================================
7002 rsocket* send_uint16_msb(
7003 /// Data to send
7004 const uint16_t value,
7005 /// MSG_DONTROUTE@n
7006 /// MSG_DONTWAIT@n
7007 /// MSG_EOR@n
7008 /// MSG_NOSIGNAL@n
7009 /// MSG_OOB
7010 const int posix_flags = 0) {
7011 int16_t buf = __endian_is_msb ? value : htons(value);
7012 if (__debug) debug("send_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7013 + ", " + std::to_string(value)
7014 + ", " + std::to_string(sizeof(buf))
7015 + ", " + std::to_string(posix_flags)
7016 + ");");
7017 __send(&buf, sizeof(buf), posix_flags);
7018 return this;
7019 }; // -x- rsocket* send_int16_msb -x-
7020
7021 /*======================================================================*//**
7022 @brief
7023 Send one 32-bit unsigned integer of data in LSB (little endian) order to the
7024 endpoint.
7025 @par Threads
7026 This method is threadsafe.
7027
7028 @throws randolf::rex::xEBADF The underlying socket is not open
7029 @throws randolf::rex::xECONNRESET Connect reset by peer
7030 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7031 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7032 part of the user address space
7033 @throws randolf::rex::xEINTR Interrupted by a signal
7034 @throws randolf::rex::xEINVAL Invalid argument passed
7035 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7036 occur, but the POSIX sockets documentation lists it as one of the
7037 errors that can be returned, perhaps because some incorrectly
7038 implemented TCP/IP stacks return this error?)
7039 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7040 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7041 and 65,527 bytes for IPv6)
7042 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7043 network congestion (or, less commonly, insufficient memory)
7044 @throws randolf::rex::xENOMEM Insufficient memory
7045 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7046 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7047 doesn't refer to a socket
7048 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7049 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7050 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7051 isn't set)
7052 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7053 there's no new data
7054 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7055 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7056 but it really isn't)
7057
7058 @returns The same rsocket object so as to facilitate stacking
7059 @qualifier TLS
7060 *///=========================================================================
7061 rsocket* send_uint32_lsb(
7062 /// Data to send
7063 const uint32_t value,
7064 /// MSG_DONTROUTE@n
7065 /// MSG_DONTWAIT@n
7066 /// MSG_EOR@n
7067 /// MSG_NOSIGNAL@n
7068 /// MSG_OOB
7069 const int posix_flags = 0) {
7070 uint32_t buf = !__endian_is_msb ? value : ntohl(value);
7071 if (__debug) debug("send_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7072 + ", " + std::to_string(value)
7073 + ", " + std::to_string(sizeof(buf))
7074 + ", " + std::to_string(posix_flags)
7075 + ");");
7076 __send(&buf, sizeof(buf), posix_flags);
7077 return this;
7078 }; // -x- rsocket* send_uint32_lsb -x-
7079
7080 /*======================================================================*//**
7081 @brief
7082 Send one 32-bit unsigned integer of data in MSB (big endian) order to the
7083 endpoint.
7084 @par Threads
7085 This method is threadsafe.
7086
7087 @throws randolf::rex::xEBADF The underlying socket is not open
7088 @throws randolf::rex::xECONNRESET Connect reset by peer
7089 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7090 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7091 part of the user address space
7092 @throws randolf::rex::xEINTR Interrupted by a signal
7093 @throws randolf::rex::xEINVAL Invalid argument passed
7094 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7095 occur, but the POSIX sockets documentation lists it as one of the
7096 errors that can be returned, perhaps because some incorrectly
7097 implemented TCP/IP stacks return this error?)
7098 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7099 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7100 and 65,527 bytes for IPv6)
7101 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7102 network congestion (or, less commonly, insufficient memory)
7103 @throws randolf::rex::xENOMEM Insufficient memory
7104 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7105 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7106 doesn't refer to a socket
7107 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7108 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7109 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7110 isn't set)
7111 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7112 there's no new data
7113 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7114 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7115 but it really isn't)
7116
7117 @returns The same rsocket object so as to facilitate stacking
7118 @qualifier TLS
7119 *///=========================================================================
7120 rsocket* send_uint32_msb(
7121 /// Data to send
7122 const uint32_t value,
7123 /// MSG_DONTROUTE@n
7124 /// MSG_DONTWAIT@n
7125 /// MSG_EOR@n
7126 /// MSG_NOSIGNAL@n
7127 /// MSG_OOB
7128 const int posix_flags = 0) {
7129 uint32_t buf = __endian_is_msb ? value : htonl(value);
7130 if (__debug) debug("send_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7131 + ", " + std::to_string(value)
7132 + ", " + std::to_string(sizeof(buf))
7133 + ", " + std::to_string(posix_flags)
7134 + ");");
7135 __send(&buf, sizeof(buf), posix_flags);
7136 return this;
7137 }; // -x- rsocket* send_uint32_msb -x-
7138
7139 /*======================================================================*//**
7140 @brief
7141 Send one 64-bit unsigned integer of data in LSB (little endian) order to the
7142 endpoint.
7143 @par Threads
7144 This method is threadsafe.
7145
7146 @throws randolf::rex::xEBADF The underlying socket is not open
7147 @throws randolf::rex::xECONNRESET Connect reset by peer
7148 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7149 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7150 part of the user address space
7151 @throws randolf::rex::xEINTR Interrupted by a signal
7152 @throws randolf::rex::xEINVAL Invalid argument passed
7153 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7154 occur, but the POSIX sockets documentation lists it as one of the
7155 errors that can be returned, perhaps because some incorrectly
7156 implemented TCP/IP stacks return this error?)
7157 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7158 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7159 and 65,527 bytes for IPv6)
7160 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7161 network congestion (or, less commonly, insufficient memory)
7162 @throws randolf::rex::xENOMEM Insufficient memory
7163 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7164 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7165 doesn't refer to a socket
7166 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7167 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7168 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7169 isn't set)
7170 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7171 there's no new data
7172 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7173 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7174 but it really isn't)
7175
7176 @returns The same rsocket object so as to facilitate stacking
7177 @qualifier TLS
7178 *///=========================================================================
7179 rsocket* send_uint64_lsb(
7180 /// Data to send
7181 const uint64_t value,
7182 /// MSG_DONTROUTE@n
7183 /// MSG_DONTWAIT@n
7184 /// MSG_EOR@n
7185 /// MSG_NOSIGNAL@n
7186 /// MSG_OOB
7187 const int posix_flags = 0) {
7188 uint64_t buf = !__endian_is_msb ? value : ((((uint64_t)ntohl((value) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((value) >> 32)));
7189 if (__debug) debug("send_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7190 + ", " + std::to_string(value)
7191 + ", " + std::to_string(sizeof(buf))
7192 + ", " + std::to_string(posix_flags)
7193 + ");");
7194 __send(&buf, sizeof(buf), posix_flags);
7195 return this;
7196 }; // -x- rsocket* send_uint64_lsb -x-
7197
7198 /*======================================================================*//**
7199 @brief
7200 Send one 64-bit unsigned integer of data in MSB (big endian) order to the
7201 endpoint.
7202 @par Threads
7203 This method is threadsafe.
7204
7205 @throws randolf::rex::xEBADF The underlying socket is not open
7206 @throws randolf::rex::xECONNRESET Connect reset by peer
7207 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7208 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7209 part of the user address space
7210 @throws randolf::rex::xEINTR Interrupted by a signal
7211 @throws randolf::rex::xEINVAL Invalid argument passed
7212 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7213 occur, but the POSIX sockets documentation lists it as one of the
7214 errors that can be returned, perhaps because some incorrectly
7215 implemented TCP/IP stacks return this error?)
7216 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7217 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7218 and 65,527 bytes for IPv6)
7219 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7220 network congestion (or, less commonly, insufficient memory)
7221 @throws randolf::rex::xENOMEM Insufficient memory
7222 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7223 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7224 doesn't refer to a socket
7225 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7226 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7227 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7228 isn't set)
7229 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7230 there's no new data
7231 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7232 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7233 but it really isn't)
7234
7235 @returns The same rsocket object so as to facilitate stacking
7236 @qualifier TLS
7237 *///=========================================================================
7238 rsocket* send_uint64_msb(
7239 /// Data to send
7240 const uint64_t value,
7241 /// MSG_DONTROUTE@n
7242 /// MSG_DONTWAIT@n
7243 /// MSG_EOR@n
7244 /// MSG_NOSIGNAL@n
7245 /// MSG_OOB
7246 const int posix_flags = 0) {
7247 uint64_t buf = __endian_is_msb ? value : ((((uint64_t)htonl((value) & 0xffffffffUL)) << 32) | htonl((uint32_t)((value) >> 32)));
7248 if (__debug) debug("send_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7249 + ", " + std::to_string(value)
7250 + ", " + std::to_string(sizeof(buf))
7251 + ", " + std::to_string(posix_flags)
7252 + ");");
7253 __send(&buf, sizeof(buf), posix_flags);
7254 return this;
7255 }; // -x- rsocket* send_uint64_msb -x-
7256
7257 /*======================================================================*//**
7258 @brief
7259 Send data in the form of a std::string to the endpoint, with an EoL sequence
7260 appended.
7261 @par Threads
7262 This method is threadsafe.
7263
7264 @throws randolf::rex::xEBADF The underlying socket is not open
7265 @throws randolf::rex::xECONNRESET Connect reset by peer
7266 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7267 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7268 part of the user address space
7269 @throws randolf::rex::xEINTR Interrupted by a signal
7270 @throws randolf::rex::xEINVAL Invalid argument passed
7271 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7272 occur, but the POSIX sockets documentation lists it as one of the
7273 errors that can be returned, perhaps because some incorrectly
7274 implemented TCP/IP stacks return this error?)
7275 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7276 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7277 and 65,527 bytes for IPv6)
7278 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7279 network congestion (or, less commonly, insufficient memory)
7280 @throws randolf::rex::xENOMEM Insufficient memory
7281 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7282 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7283 doesn't refer to a socket
7284 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7285 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7286 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7287 isn't set)
7288 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7289 there's no new data
7290 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7291 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7292 but it really isn't)
7293
7294 @returns The same rsocket object so as to facilitate stacking
7295 @see eol
7296 @see printfline
7297 @see recvline(const size_t, const int)
7298 @see vprintfline
7299 @qualifier TLS
7300 *///=========================================================================
7301 rsocket* sendline(
7302 /// Data to send
7303 const std::string msg = std::string(),
7304 /// MSG_DONTROUTE@n
7305 /// MSG_DONTWAIT@n
7306 /// MSG_EOR@n
7307 /// MSG_NOSIGNAL@n
7308 /// MSG_OOB
7309 const int posix_flags = 0) {
7310 if (__debug) debug("sendline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7311 + ", <std::string>"
7312 + ", " + std::to_string(msg.length())
7313 + "+" + std::to_string(__eol_out.length())
7314 + ", " + std::to_string(posix_flags)
7315 + ");");
7316 __sendline(msg.c_str(), msg.length(), posix_flags);
7317 return this;
7318 }; // -x- rsocket* sendline -x-
7319
7320 /*======================================================================*//**
7321 @brief
7322 Send data in the form of a "msghdr" structure to a specific endpoint.
7323 @warning
7324 This method is not compatible with TLS.
7325 @par Threads
7326 This method is threadsafe.
7327
7328 @throws randolf::rex::xEBADF The underlying socket is not open
7329 @throws randolf::rex::xECONNRESET Connect reset by peer
7330 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7331 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7332 part of the user address space
7333 @throws randolf::rex::xEINTR Interrupted by a signal
7334 @throws randolf::rex::xEINVAL Invalid argument passed
7335 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7336 occur, but the POSIX sockets documentation lists it as one of the
7337 errors that can be returned, perhaps because some incorrectly
7338 implemented TCP/IP stacks return this error?)
7339 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7340 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7341 and 65,527 bytes for IPv6)
7342 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7343 network congestion (or, less commonly, insufficient memory)
7344 @throws randolf::rex::xENOMEM Insufficient memory
7345 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7346 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7347 doesn't refer to a socket
7348 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7349 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7350 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7351 isn't set)
7352 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7353 there's no new data
7354 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7355 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7356 but it really isn't)
7357
7358 @returns The same rsocket object so as to facilitate stacking
7359 @qualifier POSIX
7360 *///=========================================================================
7361 rsocket* sendmsg(
7362 /// Pointer to data to send
7363 const struct msghdr* msg,
7364 /// MSG_DONTROUTE@n
7365 /// MSG_DONTWAIT@n
7366 /// MSG_EOR@n
7367 /// MSG_NOSIGNAL@n
7368 /// MSG_OOB
7369 const int posix_flags = 0) {
7370 if (__debug) debug("sendmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7371 + ", <msghdr>"
7372 + ", " + std::to_string(posix_flags)
7373 + ");");
7374 __track_bytes_tx(__rc_check(::sendmsg(__socket_fd, msg, posix_flags)));
7375 return this;
7376 }; // -x- rsocket* sendmsg -x-
7377
7378 /*======================================================================*//**
7379 @brief
7380 Send data in the form of a "mmsghdr" structure to a specific endpoint.
7381 @warning
7382 This method is not compatible with TLS.
7383 @par Threads
7384 This method is threadsafe.
7385
7386 @throws randolf::rex::xEBADF The underlying socket is not open
7387 @throws randolf::rex::xECONNRESET Connect reset by peer
7388 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7389 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7390 part of the user address space
7391 @throws randolf::rex::xEINTR Interrupted by a signal
7392 @throws randolf::rex::xEINVAL Invalid argument passed
7393 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7394 occur, but the POSIX sockets documentation lists it as one of the
7395 errors that can be returned, perhaps because some incorrectly
7396 implemented TCP/IP stacks return this error?)
7397 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7398 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7399 and 65,527 bytes for IPv6)
7400 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7401 network congestion (or, less commonly, insufficient memory)
7402 @throws randolf::rex::xENOMEM Insufficient memory
7403 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7404 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7405 doesn't refer to a socket
7406 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7407 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7408 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7409 isn't set)
7410 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7411 there's no new data
7412 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7413 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7414 but it really isn't)
7415
7416 @returns The same rsocket object so as to facilitate stacking
7417 @qualifier POSIX
7418 *///=========================================================================
7419 rsocket* sendmmsg(
7420 /// Pointer to data to send
7421 struct mmsghdr* mmsg,
7422 /// Size of target endpoint structure
7423 const unsigned int vlen = sizeof(mmsghdr),
7424 /// MSG_DONTROUTE@n
7425 /// MSG_DONTWAIT@n
7426 /// MSG_EOR@n
7427 /// MSG_NOSIGNAL@n
7428 /// MSG_OOB
7429 const int posix_flags = 0) {
7430 if (__debug) debug("sendmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7431 + ", <mmsghdr>"
7432 + ", " + std::to_string(vlen)
7433 + ", " + std::to_string(posix_flags)
7434 + ");");
7435 __track_bytes_tx(__rc_check(::sendmmsg(__socket_fd, mmsg, vlen, posix_flags)));
7436 return this;
7437 }; // -x- rsocket* sendmsg -x-
7438
7439 /*======================================================================*//**
7440 @brief
7441 Send data in the form of a std::string to a specific endpoint.
7442 @warning
7443 This method is not compatible with TLS.
7444 @par Threads
7445 This method is threadsafe.
7446
7447 @throws randolf::rex::xEBADF The underlying socket is not open
7448 @throws randolf::rex::xECONNRESET Connect reset by peer
7449 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7450 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7451 part of the user address space
7452 @throws randolf::rex::xEINTR Interrupted by a signal
7453 @throws randolf::rex::xEINVAL Invalid argument passed
7454 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7455 occur, but the POSIX sockets documentation lists it as one of the
7456 errors that can be returned, perhaps because some incorrectly
7457 implemented TCP/IP stacks return this error?)
7458 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7459 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7460 and 65,527 bytes for IPv6)
7461 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7462 network congestion (or, less commonly, insufficient memory)
7463 @throws randolf::rex::xENOMEM Insufficient memory
7464 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7465 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7466 doesn't refer to a socket
7467 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7468 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7469 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7470 isn't set)
7471 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7472 there's no new data
7473 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7474 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7475 but it really isn't)
7476
7477 @returns The same rsocket object so as to facilitate stacking
7478 @qualifier POSIX
7479 *///=========================================================================
7480 rsocket* sendto(
7481 /// Data to send
7482 const std::string msg,
7483 /// MSG_DONTROUTE@n
7484 /// MSG_DONTWAIT@n
7485 /// MSG_EOR@n
7486 /// MSG_NOSIGNAL@n
7487 /// MSG_OOB
7488 const int posix_flags,
7489 /// Target endpoint address structure
7490 const struct sockaddr *to,
7491 /// Size of target endpoint structure
7492 socklen_t tolen = sizeof(sockaddr)) {
7493 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7494 + ", <std::string>"
7495 + ", " + std::to_string(posix_flags)
7496 + ", <sockaddr>"
7497 + ", " + std::to_string(tolen)
7498 + ");");
7499 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg.c_str(), msg.length(), posix_flags, to, tolen)));
7500 return this;
7501 }; // -x- rsocket* sendto -x- // TODO: Create easier-to-use variants
7502
7503 /*======================================================================*//**
7504 @brief
7505 Send data in the form of a C-string to a specific endpoint.
7506 @warning
7507 This method is not compatible with TLS.
7508 @par Threads
7509 This method is threadsafe.
7510
7511 @throws randolf::rex::xEBADF The underlying socket is not open
7512 @throws randolf::rex::xECONNRESET Connect reset by peer
7513 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7514 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7515 part of the user address space
7516 @throws randolf::rex::xEINTR Interrupted by a signal
7517 @throws randolf::rex::xEINVAL Invalid argument passed
7518 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7519 occur, but the POSIX sockets documentation lists it as one of the
7520 errors that can be returned, perhaps because some incorrectly
7521 implemented TCP/IP stacks return this error?)
7522 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7523 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7524 and 65,527 bytes for IPv6)
7525 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7526 network congestion (or, less commonly, insufficient memory)
7527 @throws randolf::rex::xENOMEM Insufficient memory
7528 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7529 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7530 doesn't refer to a socket
7531 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7532 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7533 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7534 isn't set)
7535 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7536 there's no new data
7537 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7538 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7539 but it really isn't)
7540
7541 @returns The same rsocket object so as to facilitate stacking
7542 @qualifier POSIX
7543 *///=========================================================================
7544 rsocket* sendto(
7545 /// Pointer to data to send
7546 const char* msg,
7547 /// Number of bytes to send
7548 const size_t len,
7549 /// MSG_DONTROUTE@n
7550 /// MSG_DONTWAIT@n
7551 /// MSG_EOR@n
7552 /// MSG_NOSIGNAL@n
7553 /// MSG_OOB
7554 const int posix_flags,
7555 /// Target endpoint address structure
7556 const struct sockaddr *to,
7557 /// Size of target endpoint structure
7558 socklen_t tolen = sizeof(sockaddr)) {
7559 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7560 + ", <msg>"
7561 + ", " + std::to_string(std::strlen(msg))
7562 + ", " + std::to_string(posix_flags)
7563 + ", <sockaddr>"
7564 + ", " + std::to_string(tolen)
7565 + ");");
7566 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, len, posix_flags, to, tolen)));
7567 return this;
7568 }; // -x- rsocket* sendto -x-
7569
7570 /*======================================================================*//**
7571 @brief
7572 Send data in the form of an ASCIIZ string to the endpoint. The terminating
7573 NULL character won't be transmitted.
7574 @par Threads
7575 This method is threadsafe.
7576
7577 @throws randolf::rex::xEBADF The underlying socket is not open
7578 @throws randolf::rex::xECONNRESET Connect reset by peer
7579 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7580 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7581 part of the user address space
7582 @throws randolf::rex::xEINTR Interrupted by a signal
7583 @throws randolf::rex::xEINVAL Invalid argument passed
7584 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7585 occur, but the POSIX sockets documentation lists it as one of the
7586 errors that can be returned, perhaps because some incorrectly
7587 implemented TCP/IP stacks return this error?)
7588 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7589 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7590 and 65,527 bytes for IPv6)
7591 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7592 network congestion (or, less commonly, insufficient memory)
7593 @throws randolf::rex::xENOMEM Insufficient memory
7594 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7595 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7596 doesn't refer to a socket
7597 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7598 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7599 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7600 isn't set)
7601 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7602 there's no new data
7603 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7604 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7605 but it really isn't)
7606
7607 @returns The same rsocket object so as to facilitate stacking
7608 @see recvz(const size_t, const int)
7609 @see send_asciiz(const char*, const int) which also transmits the terminating
7610 NULL character
7611 @qualifier TLS
7612 *///=========================================================================
7613 rsocket* sendz(
7614 /// Pointer to data to send
7615 const char* msg,
7616 /// MSG_DONTROUTE@n
7617 /// MSG_DONTWAIT@n
7618 /// MSG_EOR@n
7619 /// MSG_NOSIGNAL@n
7620 /// MSG_OOB
7621 const int posix_flags = 0) {
7622 if (__debug) debug("sendz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7623 + ", <msg>"
7624 + ", " + std::to_string(std::strlen(msg))
7625 + ", " + std::to_string(posix_flags)
7626 + ");");
7627 __send(msg, std::strlen(msg), posix_flags);
7628 return this;
7629 }; // -x- rsocket* sendz -x-
7630
7631 /*======================================================================*//**
7632 @brief
7633 Send data in the form of an ASCIIZ string to a specific endpoint. The
7634 terminating NULL character won't be transmitted.
7635 @warning
7636 This method is not compatible with TLS.
7637 @par Threads
7638 This method is threadsafe.
7639
7640 @throws randolf::rex::xEBADF The underlying socket is not open
7641 @throws randolf::rex::xECONNRESET Connect reset by peer
7642 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7643 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7644 part of the user address space
7645 @throws randolf::rex::xEINTR Interrupted by a signal
7646 @throws randolf::rex::xEINVAL Invalid argument passed
7647 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7648 occur, but the POSIX sockets documentation lists it as one of the
7649 errors that can be returned, perhaps because some incorrectly
7650 implemented TCP/IP stacks return this error?)
7651 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7652 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7653 and 65,527 bytes for IPv6)
7654 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7655 network congestion (or, less commonly, insufficient memory)
7656 @throws randolf::rex::xENOMEM Insufficient memory
7657 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7658 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7659 doesn't refer to a socket
7660 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7661 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7662 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7663 isn't set)
7664 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7665 there's no new data
7666 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7667 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7668 but it really isn't)
7669
7670 @returns The same rsocket object so as to facilitate stacking
7671 *///=========================================================================
7672 rsocket* sendzto(
7673 /// Pointer to data to send
7674 const char* msg,
7675 /// MSG_DONTROUTE@n
7676 /// MSG_DONTWAIT@n
7677 /// MSG_EOR@n
7678 /// MSG_NOSIGNAL@n
7679 /// MSG_OOB
7680 const int posix_flags,
7681 /// Target endpoint address structure
7682 const struct sockaddr *to,
7683 /// Size of target endpoint structure
7684 socklen_t tolen = sizeof(sockaddr)) {
7685 if (__debug) debug("sendzto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7686 + ", <msg>"
7687 + ", " + std::to_string(std::strlen(msg))
7688 + ", " + std::to_string(posix_flags)
7689 + ", <sockaddr>"
7690 + ", " + std::to_string(tolen)
7691 + ");");
7692 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, std::strlen(msg), posix_flags, to, tolen)));
7693 return this;
7694 }; // -x- rsocket* sendzto -x-
7695
7696 /*======================================================================*//**
7697 @brief
7698 Set socket option to the specific integer.
7699
7700 @par Notes
7701 These setsockopt() methods take an integer or character value directly, or a
7702 pointer to a structure, and then rsocket handles the remaining tedious
7703 technical details behind-the-scenes for you when calling the underlying
7704 socket's setsockopt() function.
7705
7706 @throws randolf::rex::xEBADF The underlying socket is not open
7707 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7708 part of the user address space
7709 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7710 valid for this socket's family (a.k.a., communication domain)
7711 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7712 is not supported
7713 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7714 doesn't refer to a socket
7715
7716 @returns The same rsocket object so as to facilitate stacking
7717 @qualifier POSIX
7718 @qualifier TLS
7719 *///=========================================================================
7720 rsocket* setsockopt(
7721 /// The level at which the option resides; typically @c SOL_SOCKET
7722 const int level,
7723 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7724 const int option,
7725 /// The value that this socket option will be set to
7726 const int value) {
7727 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
7728 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7729 return this;
7730 }; // -x- rsocket* setsockopt -x-
7731
7732 /*======================================================================*//**
7733 @brief
7734 Set socket option to the specific unsigned integer.
7735 @copydetails setsockopt(const int, const int, const int)
7736
7737 @pre
7738 For any values that require a u_int, you'll need to explicitly cast this type
7739 when specifying the value directly; for example: (u_int)32768
7740
7741 @throws randolf::rex::xEBADF The underlying socket is not open
7742 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7743 part of the user address space
7744 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
7745 valid for this socket's family (a.k.a., communication domain)
7746 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
7747 is not supported
7748 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7749 doesn't refer to a socket
7750
7751 @returns The same rsocket object so as to facilitate stacking
7752 @qualifier TLS
7753 *///=========================================================================
7754 rsocket* setsockopt(
7755 /// The level at which the option resides; typically @c SOL_SOCKET
7756 const int level,
7757 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7758 const int option,
7759 /// The value that this socket option will be set to
7760 const u_int value) {
7761 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
7762 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7763 return this;
7764 }; // -x- rsocket* setsockopt -x-
7765
7766 /*======================================================================*//**
7767 @brief
7768 Set socket option to the specific unsigned character.
7769 @copydetails setsockopt(const int, const int, const int)
7770 @qualifier TLS
7771 *///=========================================================================
7772 rsocket* setsockopt(
7773 /// The level at which the option resides; typically @c SOL_SOCKET
7774 const int level,
7775 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7776 const int option,
7777 /// The value that this socket option will be set to
7778 const u_char value) {
7779 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
7780 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7781 return this;
7782 }; // -x- rsocket* setsockopt -x-
7783
7784 /*======================================================================*//**
7785 @brief
7786 Set socket option to the specific structure.
7787 @copydetails setsockopt(const int, const int, const int)
7788 @qualifier TLS
7789 *///=========================================================================
7790 rsocket* setsockopt(
7791 /// The level at which the option resides; typically @c SOL_SOCKET
7792 const int level,
7793 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7794 const int option,
7795 /// The structure that this socket option will be set to
7796 const linger& value) {
7797 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7798 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7799 return this;
7800 }; // -x- rsocket* setsockopt -x-
7801
7802 /*======================================================================*//**
7803 @brief
7804 Set socket option to the specific structure.
7805 @copydetails setsockopt(const int, const int, const linger&)
7806 @qualifier TLS
7807 *///=========================================================================
7808 rsocket* setsockopt(
7809 /// The level at which the option resides; typically @c SOL_SOCKET
7810 const int level,
7811 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7812 const int option,
7813 /// The structure that this socket option will be set to
7814 const timeval& value) {
7815 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7816 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7817 return this;
7818 }; // -x- rsocket* setsockopt -x-
7819
7820 /*======================================================================*//**
7821 @brief
7822 Set socket option to the specific structure.
7823 @copydetails setsockopt(const int, const int, const linger&)
7824 @qualifier TLS
7825 *///=========================================================================
7826 rsocket* setsockopt(
7827 /// The level at which the option resides; typically @c SOL_SOCKET
7828 const int level,
7829 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7830 const int option,
7831 /// The structure that this socket option will be set to
7832 const in_addr& value) {
7833 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7834 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7835 return this;
7836 }; // -x- rsocket* setsockopt -x-
7837
7838 /*======================================================================*//**
7839 @brief
7840 Set socket option to the specific structure.
7841 @copydetails setsockopt(const int, const int, const linger&)
7842 @qualifier TLS
7843 *///=========================================================================
7844 rsocket* setsockopt(
7845 /// The level at which the option resides; typically @c SOL_SOCKET
7846 const int level,
7847 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7848 const int option,
7849 /// The structure that this socket option will be set to
7850 const ip_mreq& value) {
7851 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7852 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7853 return this;
7854 }; // -x- rsocket* setsockopt -x-
7855
7856 /*======================================================================*//**
7857 @brief
7858 Set socket option to the specific structure.
7859 @copydetails setsockopt(const int, const int, const linger&)
7860 @qualifier TLS
7861 *///=========================================================================
7862 rsocket* setsockopt(
7863 /// The level at which the option resides; typically @c SOL_SOCKET
7864 const int level,
7865 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7866 const int option,
7867 /// The structure that this socket option will be set to
7868 const ip_mreq_source& value) {
7869 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7870 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7871 return this;
7872 }; // -x- rsocket* setsockopt -x-
7873
7874 /*======================================================================*//**
7875 @brief
7876 Set socket option to the specific structure.
7877 @copydetails setsockopt(const int, const int, const linger&)
7878 @qualifier TLS
7879 *///=========================================================================
7880 rsocket* setsockopt(
7881 /// The level at which the option resides; typically @c SOL_SOCKET
7882 const int level,
7883 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7884 const int option,
7885 /// The structure that this socket option will be set to
7886 const icmp6_filter& value) {
7887 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7888 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7889 return this;
7890 }; // -x- rsocket* setsockopt -x-
7891
7892 /*======================================================================*//**
7893 @brief
7894 Set socket option to the specific structure.
7895 @copydetails setsockopt(const int, const int, const linger&)
7896 @qualifier TLS
7897 *///=========================================================================
7898 rsocket* setsockopt(
7899 /// The level at which the option resides; typically @c SOL_SOCKET
7900 const int level,
7901 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7902 const int option,
7903 /// The structure that this socket option will be set to
7904 const sockaddr_in6& value) {
7905 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7906 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7907 return this;
7908 }; // -x- rsocket* setsockopt -x-
7909
7910 /*======================================================================*//**
7911 @brief
7912 Set socket option to the specific structure.
7913 @copydetails setsockopt(const int, const int, const linger&)
7914 @qualifier TLS
7915 *///=========================================================================
7916 rsocket* setsockopt(
7917 /// The level at which the option resides; typically @c SOL_SOCKET
7918 const int level,
7919 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7920 const int option,
7921 /// The structure that this socket option will be set to
7922 const ip6_mtuinfo& value) {
7923 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7924 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7925 return this;
7926 }; // -x- rsocket* setsockopt -x-
7927
7928 /*======================================================================*//**
7929 @brief
7930 Set socket option to the specific structure.
7931 @copydetails setsockopt(const int, const int, const linger&)
7932 @qualifier TLS
7933 *///=========================================================================
7934 rsocket* setsockopt(
7935 /// The level at which the option resides; typically @c SOL_SOCKET
7936 const int level,
7937 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7938 const int option,
7939 /// The structure that this socket option will be set to
7940 const ipv6_mreq& value) {
7941 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7942 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7943 return this;
7944 }; // -x- rsocket* setsockopt -x-
7945
7946 /*======================================================================*//**
7947 @brief
7948 Set socket option to the specific structure.
7949 @copydetails setsockopt(const int, const int, const linger&)
7950 @qualifier TLS
7951 *///=========================================================================
7952 rsocket* setsockopt(
7953 /// The level at which the option resides; typically @c SOL_SOCKET
7954 const int level,
7955 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7956 const int option,
7957 /// The structure that this socket option will be set to
7958 const group_req& value) {
7959 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7960 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7961 return this;
7962 }; // -x- rsocket* setsockopt -x-
7963
7964 /*======================================================================*//**
7965 @brief
7966 Set socket option to the specific structure.
7967 @copydetails setsockopt(const int, const int, const linger&)
7968 @qualifier TLS
7969 *///=========================================================================
7970 rsocket* setsockopt(
7971 /// The level at which the option resides; typically @c SOL_SOCKET
7972 const int level,
7973 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
7974 const int option,
7975 /// The structure that this socket option will be set to
7976 const group_source_req& value) {
7977 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
7978 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
7979 return this;
7980 }; // -x- rsocket* setsockopt -x-
7981
7982 /*======================================================================*//**
7983 @brief
7984 Shut down the underlying socket, partially or fully.
7985
7986 <div style=padding-left:32px;>
7987 <table>
7988 <tr>
7989 <td valign=top>SHUT_RD:</td>
7990 <td>Further receives will be disallowed.</td>
7991 </tr>
7992 <tr>
7993 <td valign=top>SHUT_WR:</td>
7994 <td>Further sends will be disallowed (this may cause actions specific
7995 to the protocol family of the socket to occur).</td>
7996 </tr>
7997 <tr>
7998 <td valign=top>SHUT_RDWR:</td>
7999 <td>Further sends and receives will be disallowed (default).</td>
8000 </tr>
8001 </table>
8002 </div>
8003
8004 @throws randolf::rex::xEBADF The underlying socket is not open
8005 @throws randolf::rex::xEINVAL Invalid argument passed
8006 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8007 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8008 doesn't refer to a socket
8009
8010 @returns The same rsocket object so as to facilitate stacking
8011 @qualifier POSIX
8012 @qualifier TLS
8013 *///=========================================================================
8014 rsocket* shutdown(
8015 /// SHUT_RD@n
8016 /// SHUT_RW@n
8017 /// SHUT_RDWR (default)
8018 const int how = SHUT_RDWR,
8019 /// Shutdown the TLS connection too (but only if it's alrady enabled), which
8020 /// is the default (disabling this will usually result in errors for the
8021 /// endpoint upon raw socket shutdown)
8022 const bool tls_shutdown = true) {
8023 if (__debug) debug("shutdown(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8024 + ", " + std::to_string(how)
8025 + ");");
8026
8027 // --------------------------------------------------------------------------
8028 // Shut down TLS connections first, if TLS is enabled.
8029 //
8030 // If SSL_shutdown returns a 0, then it means a "bidirectional shutdown" is
8031 // needed which entails calling SSL_shutdown a second time. The error codes
8032 // returned by SSL_shutdown are as follows:
8033 //
8034 // 1 = Shutdown completed normally
8035 //
8036 // 0 = Shutdown is not yet finished, and a second SSL_shutdown() is needed
8037 //
8038 // -1 = Critical error, probably due to a connection problem, or because
8039 // the endpoint already initiated a shutdown, etc.
8040 //
8041 // We don't really care what the second SSL_shutdown's return code is because
8042 // there's nothing we can do about it whatever it is. This situation is rare
8043 // though, and what follows next closes the raw socket, which is OpenSSL's
8044 // lifeline to communicating with the endpoint.
8045 // --------------------------------------------------------------------------
8046 if (__tls && tls_shutdown) {
8047 if (SSL_shutdown(__tls_fd) == 0) {
8048 SSL_shutdown(__tls_fd);
8049 } // -x- if SSL_shutdown -x-
8050 } // -x- if __tls -x-
8051
8052 // --------------------------------------------------------------------------
8053 // Shut down the raw socket.
8054 // --------------------------------------------------------------------------
8055 __rc_check(::shutdown(__socket_fd, how));
8056 //__socket_connected = false; // TODO: Figure out when to change this to false
8057 return this;
8058 }; // -x- rsocket* shutdown -x-
8059
8060 /*======================================================================*//**
8061 @brief
8062 Complete the configuration of an rsocket that was previously initialized
8063 without any parameters (a.k.a., an "empty rsocket").
8064 @copydetails rsocket()
8065 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
8066 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
8067 @throws randolf::rex::xEALREADY If this socket() method was already used, or
8068 it was used after rsocket() initialized with at least one parameter
8069 @throws randolf::rex::xEINVAL Protocal family invalid or not available
8070 @throws randolf::rex::xEINVAL Invalid flags in type
8071 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
8072 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
8073 @throws randolf::rex::xENOBUFS Insufficient memory
8074 @throws randolf::rex::xENOMEM Insufficient memory
8075 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
8076 supported within the specified family (a.k.a., communication domain)
8077 @returns The same rsocket object so as to facilitate stacking
8078 @see rsocket()
8079 @see socket_family()
8080 @see socket_fd()
8081 @see socket_protocol()
8082 @see socket_type()
8083 @qualifier POSIX
8084 @qualifier TLS
8085 *///=========================================================================
8086 rsocket* socket(
8087 /// Communication domain; usually one of:@n
8088 /// AF_INET (IPv4)@n
8089 /// AF_INET6 (IPv6)@n
8090 /// AF_UNIX (UNIX domain sockets)
8091 const int family,
8092 /// Communication semantics; usually one of:@n
8093 /// SOCK_STREAM (common for TCP)@n
8094 /// SOCK_DGRAM (common for UDP)
8095 const int type = SOCK_STREAM,
8096 /// Network protocol; usually one of:@n
8097 /// IPPROTO_TCP@n
8098 /// IPPROTO_UDP@n
8099 /// IPPROTO_IP@n
8100 /// PF_UNSPEC (auto-detect)
8101 const int protocol = PF_UNSPEC) {
8102 __socket(family, type, protocol);
8103 return this;
8104 }; // -x- rsocket* socket -x-
8105
8106 /*======================================================================*//**
8107 @brief
8108 Get underlying socket family/domain constant (SO_DOMAIN).
8109 @returns socket family/domain constant
8110 @see port()
8111 @see socket()
8112 @see socket_fd()
8113 @see socket_protocol()
8114 @see socket_type()
8115 @qualifier TLS
8116 *///=========================================================================
8117 const int socket_family() noexcept { return __socket_addr.ss_family; }; // -x- int socket_family -x-
8118
8119 /*======================================================================*//**
8120 @brief
8121 Get underlying socket descriptor/handle.
8122 @returns socket descriptor/handle
8123 @returns 0 = socket not yet allocated
8124 @see port()
8125 @see socket()
8126 @see socket_family()
8127 @see socket_protocol()
8128 @see socket_type()
8129 @qualifier TLS
8130 *///=========================================================================
8131 const int socket_fd() noexcept { return __socket_fd; }; // -x- int socket_fd -x-
8132
8133 /*======================================================================*//**
8134 @brief
8135 Set underlying socket descriptor/handle (to one that is presumed to be open).
8136 @note
8137 This method is only available while an underlying socket has not been created
8138 or previously assigned, such as after an empty @ref rsocket instantiation.
8139 @throws randolf::rex::xEALREADY If this socket_fd() method was already used,
8140 or it was used after socket() initialized it, or if rsocket() had
8141 initialized with at least one parameter that resulted in the creation
8142 of an underlying socket
8143 @returns The same rsocket object so as to facilitate stacking
8144 @see socket()
8145 @see socket_family()
8146 @see socket_protocol()
8147 @see socket_type()
8148 @qualifier TLS
8149 *///=========================================================================
8150 rsocket* socket_fd(
8151 /// New socket descriptor/handle
8152 const int new_socket_fd) {
8153 if (__debug) debug("socket_fd(socket{0x" + randolf::rtools::to_hex(new_socket_fd) + "}"
8154 + ");");
8155 if (__socket_fd != 0) throw randolf::rex::xEALREADY("EALREADY");
8156 __socket_fd = new_socket_fd;
8157 __socket_addr.ss_family = getsockopt_int(SOL_SOCKET, SO_DOMAIN);
8158 __socket_type = getsockopt_int(SOL_SOCKET, SO_TYPE);
8159 __socket_protocol = getsockopt_int(SOL_SOCKET, SO_PROTOCOL);
8160 __socket_open = true;
8161 return this;
8162 }; // -x- rsocket* socket_fd -x-
8163
8164 /*======================================================================*//**
8165 @brief
8166 Get underlying socket protocol constant (SO_PROTOCOL).
8167 @returns socket protocol constant
8168 @see port()
8169 @see socket()
8170 @see socket_family()
8171 @see socket_fd()
8172 @see socket_type()
8173 @qualifier TLS
8174 *///=========================================================================
8175 const int socket_protocol() noexcept { return __socket_protocol; }; // -x- int socket_protocol -x-
8176
8177 /*======================================================================*//**
8178 @brief
8179 Get underlying socket type constant (SO_TYPE).
8180 @returns socket type constant
8181 @see port()
8182 @see socket()
8183 @see socket_family()
8184 @see socket_fd()
8185 @see socket_protocol()
8186 @qualifier TLS
8187 *///=========================================================================
8188 const int socket_type() noexcept { return __socket_type; }; // -x- int socket_type -x-
8189
8190 /*======================================================================*//**
8191 @brief
8192 Find out whether the underlying socket is at the out-of-band (OOB) mark.
8193
8194 @throws randolf::rex::xEBADF The underlying socket is not open
8195 @throws randolf::rex::xEINVAL The underlying socket file descriptor is not a
8196 type to which @ref sockatmark() can be applied
8197
8198 @returns TRUE = at OOB mark
8199 @returns FALSE = not at OOB mark
8200 @qualifier POSIX
8201 @qualifier TLS
8202 *///=========================================================================
8203 const bool sockatmark() {
8204 return __rc_check(::sockatmark(__socket_fd)) ? true : false;
8205 }; // -x- bool sockatmark -x-
8206
8207 /*======================================================================*//**
8208 @brief
8209 Find out what the read timeout is set to on the current socket.
8210
8211 Since getting the read timeout is such a common operation, this specialized
8212 method was created to ease software development efforts; internally we're
8213 just calling getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO).
8214
8215 @throws randolf::rex::xEBADF The underlying socket is not open
8216 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8217 part of the user address space
8218 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
8219 valid for this socket's family (a.k.a., communication domain)
8220 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
8221 is not supported
8222 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8223 doesn't refer to a socket
8224
8225 @returns @c timeval socket option structure wrapped in std::shared_ptr
8226 @qualifier TLS
8227 *///=========================================================================
8228 std::shared_ptr<timeval> timeout() {
8229 return getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO);
8230 }; // -x- std::shared_ptr<timeval> timeout -x-
8231
8232 /*======================================================================*//**
8233 @brief
8234 Set the recv timeout on the current socket.
8235
8236 Since setting the read timeout is such a common operation, this specialized
8237 method was created to ease software development efforts; internally we're
8238 just calling setsockopt(SOL_SOCKET, SO_RCVTIMEO, timeval).
8239 @attention
8240 Although a timeout of 100,000 microseconds (1/10 of one second) may suffice
8241 in healthy and efficient networks, a more conservative setting of 1 second
8242 tends to minimally yield more reliable results. Many end-user applications
8243 use defaults of 3 to 5 seconds to cover a wider variety of unpredictable
8244 network connections (such as over shared wireless connections that are slow),
8245 and this setting should ultimately be configurable by users/administrators.
8246
8247 @note
8248 The default timeout for new sockets is normally 0 (no timeout).
8249
8250 @throws randolf::rex::xEBADF The underlying socket is not open
8251 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8252 part of the user address space
8253 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
8254 valid for this socket's family (a.k.a., communication domain)
8255 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
8256 is not supported
8257 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8258 doesn't refer to a socket
8259
8260 @returns The same rsocket object so as to facilitate stacking
8261 @qualifier TLS
8262 *///=========================================================================
8263 rsocket* timeout(
8264 /// timeval structure
8265 const struct timeval tv) {
8266 return setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
8267 }; // -x- rsocket* timeout -x-
8268
8269 /*======================================================================*//**
8270 @copydoc timeout(struct timeval)
8271 @qualifier TLS
8272 *///=========================================================================
8273 rsocket* timeout(
8274 /// Timeout in seconds
8275 const int tv_sec = 0,
8276 /// Timeout in microseconds
8277 const long tv_usec = 0) {
8278 struct timeval tv{tv_sec, tv_usec};
8279 return setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
8280 }; // -x- rsocket* timeout -x-
8281
8282 /*======================================================================*//**
8283 @brief
8284 Find out what the read timeout is set to when using the @ref recvline()
8285 method.
8286
8287 @returns @c long value (0 = no timeout)
8288 @see recvline
8289 @see timeout
8290 @see timeout_recvline(long)
8291 @qualifier TLS
8292 *///=========================================================================
8293 long timeout_recvline() {
8294 return __recvline_timeout;
8295 }; // -x- long timeout_recvline -x-
8296
8297 /*======================================================================*//**
8298 @brief
8299 Set the read timeout for the @ref recvline() method (the @ref recvline()
8300 method's @c timeout parameter can override this setting).
8301
8302 @note
8303 The default timeout for this recvline_timeout setting is 0 (no timeout).
8304
8305 @throws randolf::rex::xERANGE if the timeout parameter is below 0
8306
8307 @returns The same rsocket object so as to facilitate stacking
8308 @see recvline
8309 @see timeout
8310 @see timeout_recvline
8311 @qualifier TLS
8312 *///=========================================================================
8313 rsocket* timeout_recvline(
8314 /// timeval structure
8315 const long timeout) {
8316 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
8317 __recvline_timeout = timeout;
8318 return this;
8319 }; // -x- rsocket* timeout_recvline -x-
8320
8321 /*======================================================================*//**
8322 @brief
8323 Enable or disable encrypted communications (from the OpenSSL library).
8324
8325 @warning
8326 TLS cannot be enabled before a non-encrypted rsocket is open (an rsocket is
8327 typically opened with the @ref socket() method, the @ref connect() method, or
8328 one of the @ref accept() methods). If calling @c tls(true) on an rsocket
8329 that isn't open, an exception will be thrown.
8330
8331 If needed, a new TLS context will be instantiated and TLS will be initialized
8332 (if this hasn't already been done). TLS instantiation can be done first by
8333 calling the @ref tls_ctx() method (regardless of whether encryption is being
8334 enabled or disabled). If the default @ref TLS_FLAGS aren't sufficient for
8335 the needs of your application, then the @ref tls_ctx() method facilitates
8336 this regardless of wehther rsocket is open.
8337
8338 @note
8339 The reason a TLS context is instantiated and TLS is initialized even when the
8340 status is being set to FALSE is to facilitate TLS ingress from an unencrypted
8341 connection later in the session (see the @ref TLS_NO_INGRESS flag for more
8342 information).
8343
8344 @post
8345 If a client attempts to upgrade a TLS connection to TLS (e.g., by using a
8346 command such as @c STARTTLS, which is commonly transmitted in unencrypted
8347 form, like @c telnet-ssl does -- using `telnet-ssl -z ssl` prevents this
8348 condition), the following error that's difficult to track down may be
8349 triggered when calling any of the @c recv methods (I hope that including this
8350 information here in this documentation will be helpful):
8351 @verbatim
8352 SSL_ERROR_UNKNOWN: error:0A00010B:SSL routines::wrong version number
8353 @endverbatim
8354 This is most likely not a programming error, but rather a problem with how
8355 users may attempt to mis-use a connection based on a misunderstanding of the
8356 communications requirements (e.g., connecting unencrypted and attempting to
8357 upgrade to TLS over a connection that's expecting TLS encrypted data from the
8358 very beginning, without involving any ingress).
8359
8360 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8361 OpenSSL library doesn't document which errors may be returned)
8362
8363 @returns The same rsocket object so as to facilitate stacking
8364 @see is_tls
8365 @see tls_ctx
8366 @qualifier TLS
8367 *///=========================================================================
8368 rsocket* tls(
8369 /// TRUE = Enable encrypted communications@n FALSE = Disable encrypted communications
8370 const bool status = true,
8371 /// Configuration parameters
8372 const int flags = TLS_FLAGS::TLS_DEFAULT) { // | TLS_FLAGS::TLS_CLIENT
8373 if (__debug) debug("tls(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8374 + ", " + (status ? "true" : "false")
8375 + ");");
8376
8377 // --------------------------------------------------------------------------
8378 // Create default context (with "flags" passthrough), unless it was already
8379 // created (usually by one of the tls_ctx() methods).
8380 // --------------------------------------------------------------------------
8381 if (__tls_ctx == nullptr) tls_ctx((SSL_CTX*)nullptr, flags);
8382
8383 // --------------------------------------------------------------------------
8384 // Make sure tls_fd (ssl_fd) is allocated. If not, then it needs to be
8385 // allocated and configured.
8386 // --------------------------------------------------------------------------
8387 if (status == true && __tls_fd == nullptr) {
8388 __tls_fd = SSL_new(__tls_ctx);
8389 if (__debug) debug("SSL_set_fd(<tls_fd>, socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8390 + ");");
8391
8392 // --------------------------------------------------------------------------
8393 // Associate OpenSSL file descriptor with underlying socket file descriptor.
8394 // --------------------------------------------------------------------------
8395 __rc_check_tls(SSL_set_fd(__tls_fd, __socket_fd));
8396
8397 // --------------------------------------------------------------------------
8398 // Enable read-ahead so that SSL_peek will work properly.
8399 // --------------------------------------------------------------------------
8400 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
8401 SSL_set_read_ahead(__tls_fd, 1); // 0=disable / 1=enable
8402
8403// SSL_CTX_set_max_pipelines(__tls_ctx, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
8404// SSL_set_max_pipelines(__tls_fd, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
8405
8406// SSL_CTX_set_mode(__tls_ctx, SSL_MODE_AUTO_RETRY);
8407// SSL_set_mode(__tls_fd, SSL_MODE_AUTO_RETRY);
8408
8409 // --------------------------------------------------------------------------
8410 // We're probably not going to use BIO because we don't need it, and it adds
8411 // unnecessary overhead for what we're doing. Also, BIO doesn't provide an
8412 // alternative to the SSL_peek function, and also doesn't resolve the need
8413 // for the MSG_WAITALL flag that the ::recv() function supports.
8414 //
8415 // TODO: Remove this completely, unless is can solve the problem of reading
8416 // all incoming data (needed for readline, primarily)
8417 // --------------------------------------------------------------------------
8418 __tls_rbio = SSL_get_rbio(__tls_fd);
8419 __tls_wbio = SSL_get_wbio(__tls_fd);
8420
8421 } // -x- if !__tls_fd -x-
8422 __tls = status;
8423 return this;
8424 }; // -x- rsocket* tls -x-
8425
8426 /*======================================================================*//**
8427 @brief
8428 Return the current TLS context (multiple TLS contexts are supported, although
8429 typically needed to support SNI with inbound connections).
8430 @returns Pointer to OpenSSL's context (normally labelled @c SSL_CTX* in the
8431 documentation for OpenSSL), or nullptr if this context was never
8432 assigned to (or created by) this rsocket
8433 @see tls_ctx
8434 @qualifier TLS
8435 *///=========================================================================
8436 SSL_CTX* tls_ctx() noexcept {
8437 return __tls_ctx;
8438 }; // -x- SSL_CTX* tls_ctx -x-
8439
8440 /*======================================================================*//**
8441 @brief
8442 Copy the source rsocket's TLS context map and add it to this rsocket's
8443 collection; or, if the source doesn't have any TLS contexts and this rsocket
8444 doesn't have any TLS contexts in its collection, then initialize TLS and
8445 instantiate a new TLS context. In either scenario, the source rsocket will
8446 be treated as a template as all TLS flags duplicated to enable encrypted
8447 socket I/O for use in this rsocket().
8448
8449 @note
8450 At least one TLS context is needed to enable encrypted socket I/O for use in
8451 this rsocket().
8452
8453 @post
8454 Encrypted socket I/O is only possible after a TLS context has been
8455 initialized (this is not a global setting as it has per-rsocket specificity).
8456
8457 @note
8458 The only @ref TLS_FLAGS flag that doesn't get transferred is @ref TLS_SERVER
8459 when no flags are specified. Specifying any flag(s) will cause this method
8460 to ignore the source rsocket's TLS flags so as to defer to this override.
8461
8462 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8463 OpenSSL library doesn't document which errors may be returned)
8464
8465 @returns The same rsocket object so as to facilitate stacking
8466 @see tls_sni
8467 @qualifier TLS
8468 *///=========================================================================
8469 rsocket* tls_ctx(
8470 /// OpenSSL's TLS context to use (if not provided, a new context will be
8471 /// created automatically using OpenSSL's defaults)
8472 rsocket* rtemplate,
8473 /// Configuration parameters
8474 const int flags = TLS_FLAGS::TLS_DEFAULT) {
8475 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8476 + ", <rsocket>"
8477 + ", " + std::to_string(flags)
8478 + ");");
8479
8480 // --------------------------------------------------------------------------
8481 // TLS-related variables (OpenSSL).
8482 //
8483 // Note: The __tls_fd variable cannot be allocated until after __socket_fd
8484 // has been, hence the "post" note in the documentation.
8485 // --------------------------------------------------------------------------
8486 __tls = (bool)rtemplate->__tls; // Preserve TLS mode setting
8487 if (rtemplate->__tls_ctx != nullptr) { // If TLS context is defined, then do these important things:
8488 __tls_ctx = rtemplate->__tls_ctx; // 1. copy the pointer to SSL_CTX
8489 SSL_CTX_up_ref(__tls_ctx); // 2. increment SSL_CTX's internal reference count
8490 } // -x- if __tlx_ctx -x-
8491
8492 // --------------------------------------------------------------------------
8493 // Copy or override TLS flags.
8494 // --------------------------------------------------------------------------
8495 if (flags == TLS_FLAGS::TLS_DEFAULT) {
8496 __tls_exclusive = rtemplate->__tls_exclusive; // TLS policy
8497 __tls_egress = rtemplate->__tls_egress; // TLS policy
8498 __tls_ingress = rtemplate->__tls_ingress; // TLS policy
8499 } else { // Save flags
8500 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
8501 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
8502 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
8503 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
8504 } // -x- if flags -x-
8505
8506 return this;
8507 }; // -x- rsocket* tls_ctx -x-
8508
8509 /*======================================================================*//**
8510 @brief
8511 Initialize TLS and instantiate a TLS context, and add it to this rsocket's
8512 current collection of TLS contexts, and set it as the currently active TLS
8513 context (so that a certificate chain and private key may be added to it).
8514 @note
8515 At least one TLS context is needed to enable encrypted socket I/O for use in
8516 this rsocket().
8517 @post
8518 Encrypted socket I/O is only possible after a TLS context has been
8519 initialized (this is not a global setting as it has per-rsocket specificity).
8520 @note
8521 This is the default TLS context for this @c rsocket, which will also be used
8522 for non-SNI handshakes.
8523
8524 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8525 OpenSSL library doesn't document which errors may be returned)
8526
8527 @returns The same rsocket object so as to facilitate stacking
8528 @see tls_sni
8529 @qualifier TLS
8530 *///=========================================================================
8531 rsocket* tls_ctx(
8532 /// OpenSSL's TLS context to use (if not provided, a new context will be
8533 /// created using OpenSSL's defaults)
8534 SSL_CTX* ctx,
8535 /// Configuration parameters
8536 const int flags = TLS_FLAGS::TLS_DEFAULT) {
8537 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8538 + ", <ctx>"
8539 + ", " + std::to_string(flags)
8540 + ");");
8541
8542 // --------------------------------------------------------------------------
8543 // Ignore repeated calls to this method.
8544 // --------------------------------------------------------------------------
8545 if (__tls_ctx != nullptr) return this;
8546
8547 // --------------------------------------------------------------------------
8548 // Fire up OpenSSL's algorithms and pre-load its error strings.
8549 //
8550 // These two functions have been deprecated since OpenSSL v1.1.0, so we don't
8551 // need to call them. If someone needs them, then they can always call them
8552 // in their own code anyway.
8553 // --------------------------------------------------------------------------
8554 //OpenSSL_add_all_algorithms(); // Load and register cryptography algorithms
8555 //SSL_load_error_strings(); // Load all error messages into memory
8556
8557 // --------------------------------------------------------------------------
8558 // Save (ctx != nullptr) or create (ctx == nullptr) OpenSSL context.
8559 // --------------------------------------------------------------------------
8560 __tls_ctx = ctx == nullptr ? SSL_CTX_new(flags & TLS_FLAGS::TLS_SERVER ? TLS_server_method() : TLS_client_method()) // Create anew (default)
8561 : ctx; // Use OpenSSL context that was provided
8562 if (__tls_ctx == nullptr) randolf::rex::mk_exception("Cannot create TLS context", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8563
8564 // --------------------------------------------------------------------------
8565 // Enable read-ahead so that SSL_peek will work properly.
8566 // --------------------------------------------------------------------------
8567 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
8568
8569 // --------------------------------------------------------------------------
8570 // Save flags.
8571 // --------------------------------------------------------------------------
8572 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
8573 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
8574 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
8575 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
8576
8577 return this;
8578 }; // -x- rsocket* tls_ctx -x-
8579
8580 /*======================================================================*//**
8581 @brief
8582 Check the private key it to ensure it's consistent with the corresponding TLS
8583 certificate chain.
8584
8585 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8586 OpenSSL library doesn't document which errors may be returned)
8587
8588 @returns The same rsocket object so as to facilitate stacking
8589 @see tls_ctx_use_privatekey_file
8590 @see tls_ctx_use_privatekey_pem
8591 @qualifier TLS
8592 *///=========================================================================
8593 rsocket* tls_ctx_check_privatekey() {
8594 if (__debug) debug("tls_ctx_check_privatekey();");
8595 if (!SSL_CTX_check_private_key(__tls_ctx)) randolf::rex::mk_exception("Cannot validate consistency between certificate chain and private key", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8596 return this;
8597 }; // -x- rsocket* tls_ctx_check_privatekey -x-
8598
8599 /*======================================================================*//**
8600 @brief
8601 Load a TLS certificate chain and private key in PEM format from text files
8602 and use them in the TLS context.
8603
8604 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8605 OpenSSL library doesn't document which errors may be returned)
8606
8607 @returns The same rsocket object so as to facilitate stacking
8608 @see tls_ctx_use_certificate_chain_and_privatekey_pems
8609 @see tls_ctx_use_certificate_chain_file
8610 @see tls_ctx_use_certificate_chain_pem
8611 @see tls_ctx_use_privatekey_file
8612 @see tls_ctx_use_privatekey_pem
8613 @see tls_ctx_check_privatekey
8614 @qualifier TLS
8615 *///=========================================================================
8616 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
8617 /// Pointer to ASCIIZ path and filename to certificate chain file (@c nullptr
8618 /// will simply be ignored)
8619 const char* chain_file,
8620 /// Pointer to ASCIIZ path and filename to private key file (@c nullptr will
8621 /// simply be ignored)
8622 const char* key_file) {
8623 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
8624 + std::string(chain_file)
8625 + ", " + std::string( key_file)
8626 + ");");
8627 if (chain_file != nullptr) tls_ctx_use_certificate_chain_file(chain_file);
8628 if ( key_file != nullptr) tls_ctx_use_privatekey_file( key_file);
8629 return this;
8630 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
8631
8632 /*======================================================================*//**
8633 @brief
8634 Load a TLS certificate chain and private key in PEM format from text files
8635 and use them in the TLS context.
8636
8637 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8638 OpenSSL library doesn't document which errors may be returned)
8639
8640 @returns The same rsocket object so as to facilitate stacking
8641 @see tls_ctx_use_certificate_chain_and_privatekey_pems
8642 @see tls_ctx_use_certificate_chain_file
8643 @see tls_ctx_use_certificate_chain_pem
8644 @see tls_ctx_use_privatekey_file
8645 @see tls_ctx_use_privatekey_pem
8646 @see tls_ctx_check_privatekey
8647 @qualifier TLS
8648 *///=========================================================================
8649 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
8650 /// Pointer to ASCIIZ path and filename to certificate chain file (an empty
8651 /// string will simply be ignored)
8652 const std::string chain_file,
8653 /// Pointer to ASCIIZ path and filename to private key file (an empty string
8654 /// will simply be ignored)
8655 const std::string key_file) {
8656 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
8657 + chain_file
8658 + ", " + key_file
8659 + ");");
8660 if (!chain_file.empty()) tls_ctx_use_certificate_chain_file(chain_file);
8661 if ( !key_file.empty()) tls_ctx_use_privatekey_file( key_file);
8662 return this;
8663 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
8664
8665 /*======================================================================*//**
8666 @brief
8667 Load a TLS certificate chain and a TLS private key in PEM format from memory
8668 and use them in the TLS context.
8669
8670 Although this functionality doesn't exist in OpenSSL (at the time of writing
8671 this method), it's provided here in a manner that has exactly the same effect
8672 as the @ref tls_ctx_use_certificate_chain_and_privatekey_files() methods, but
8673 without needing the PEM-formatted certificate chain stored in files
8674 beforehand.
8675
8676 @note
8677 The @c cert_pem_data and key_pem_data parameters are pointers to the memory
8678 locations that holds the PEM formatted certificate chain data and private key
8679 data, respectively. If the corresponding lengths of each of these data aren't
8680 specified or are set to zero (default), then they will be treated as multiline
8681 ASCIIZ strings.
8682
8683 Behind the scenes, we're just writing the cert_pem_data and key_pem_data
8684 memory to temporary files with severely-limited permissions (), then
8685 optionally overwriting those temporary files with random data prior to
8686 deleting them (this is the default, since better security practices should be
8687 the default, but on a secured system it may not be necessary and so this
8688 option can also be disabled to save CPU cycles and reduce overall disk-write
8689 I/O operations).
8690
8691 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8692 OpenSSL library doesn't document which errors may be returned)
8693
8694 @returns The same rsocket object so as to facilitate stacking
8695 @see tls_ctx_use_certificate_chain_and_privatekey_files
8696 @see tls_ctx_use_certificate_chain_file
8697 @see tls_ctx_use_certificate_chain_pem
8698 @see tls_ctx_use_privatekey_file
8699 @see tls_ctx_use_privatekey_pem
8700 @see tls_ctx_check_privatekey
8701 @qualifier TLS
8702 *///=========================================================================
8703 rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems(
8704 /// Pointer to certificate chain data in PEM format
8705 const char* cert_pem_data,
8706 /// Pointer to private key data in PEM format
8707 const char* key_pem_data,
8708 /// Length of cert_pem_data (in bytes), or 0 to auto-detect length if cert_pem_data is an ASCIIZ string
8709 size_t cert_len = 0,
8710 /// Length of key_pem_data (in bytes), or 0 to auto-detect length if key_pem_data is an ASCIIZ string
8711 size_t key_len = 0,
8712 /// Whether to overwrite the temporary files with random data before deleting them
8713 const bool random_fill = true) {
8714 tls_ctx_use_certificate_chain_pem(cert_pem_data, cert_len, random_fill);
8715 tls_ctx_use_privatekey_pem( key_pem_data, key_len, random_fill);
8716 return this;
8717 }; // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems -x-
8718
8719 /*======================================================================*//**
8720 @brief
8721 Load a TLS certificate chain in PEM format from a text file and use it in the
8722 TLS context.
8723
8724 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8725 OpenSSL library doesn't document which errors may be returned)
8726
8727 @returns The same rsocket object so as to facilitate stacking
8728 @see tls_ctx_use_certificate_chain_file
8729 @see tls_ctx_use_certificate_chain_pem
8730 @see tls_ctx_check_privatekey
8731 @qualifier TLS
8732 *///=========================================================================
8733 rsocket* tls_ctx_use_certificate_chain_file(
8734 /// Pointer to ASCIIZ path and filename to certificate chain file
8735 const char* file) {
8736 if (__debug) debug("tls_ctx_use_certificate_chain_file("
8737 + std::string(file)
8738 + ");");
8739 if (SSL_CTX_use_certificate_chain_file(__tls_ctx, file) != 1) randolf::rex::mk_exception(std::string("Cannot use certificate chain file ") + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8740 return this;
8741 }; // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
8742
8743 /*======================================================================*//**
8744 @brief
8745 Load a TLS certificate chain in PEM format from a text file and use it in the
8746 TLS context.
8747
8748 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8749 OpenSSL library doesn't document which errors may be returned)
8750
8751 @returns The same rsocket object so as to facilitate stacking
8752 @see tls_ctx_use_certificate_chain_file
8753 @see tls_ctx_use_certificate_chain_pem
8754 @see tls_ctx_check_privatekey
8755 @qualifier TLS
8756 *///=========================================================================
8757 rsocket* tls_ctx_use_certificate_chain_file(
8758 /// Path and filename to certificate chain file
8759 const std::string file) {
8760 if (__debug) debug("tls_ctx_use_certificate_chain_file("
8761 + file
8762 + ");");
8763 if (SSL_CTX_use_certificate_chain_file(__tls_ctx, file.c_str()) != 1) randolf::rex::mk_exception("Cannot use certificate chain file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8764 return this;
8765 }; // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
8766
8767 /*======================================================================*//**
8768 @brief
8769 Load a TLS certificate chain in PEM format from memory and use it in the TLS
8770 context.
8771
8772 Although this functionality doesn't exist in OpenSSL (at the time of writing
8773 this method), it's provided here in a manner that has exactly the same effect
8774 as the @ref tls_ctx_use_certificate_chain_file() methods, but without needing
8775 the PEM-formatted certificate chain stored in a file beforehand.
8776
8777 @note
8778 The @c pem_data parameter is a pointer to the memory location that holds
8779 the PEM formatted certificate chain data. If the length of this data isn't
8780 specified or is set to zero (default), then it will be treated as a multiline
8781 ASCIIZ string.
8782
8783 Behind the scenes, we're just writing the pem_data memory to a temporary
8784 file with severely-limited permissions (), then optionally overwriting that
8785 temporary file with random data prior to deleting it (this is the default,
8786 since better security practices should be the default, but on a secured
8787 system it may not be necessary and so this option can also be disabled to
8788 save CPU cycles and reduce overall disk-write I/O operations).
8789
8790 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8791 OpenSSL library doesn't document which errors may be returned)
8792
8793 @returns The same rsocket object so as to facilitate stacking
8794 @see tls_ctx_use_certificate_chain_file
8795 @see tls_ctx_check_privatekey
8796 @qualifier TLS
8797 *///=========================================================================
8798 rsocket* tls_ctx_use_certificate_chain_pem(
8799 /// Pointer to certificate chain data in PEM format
8800 const char* pem_data,
8801 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
8802 size_t len = 0,
8803 /// Whether to overwrite the temporary file with random data before deleting it
8804 const bool random_fill = true) {
8805 if (__debug) debug("tls_ctx_use_certificate_chain_pem(<buf>, " + std::to_string(len)
8806 + ", " + (random_fill ? "true" : "false")
8807 + ");");
8808
8809 // --------------------------------------------------------------------------
8810 // Measure size of certificate chain if an ASCIIZ string was indicated.
8811 // --------------------------------------------------------------------------
8812 if (len == 0) len = std::strlen(pem_data);
8813
8814 // --------------------------------------------------------------------------
8815 // Generate filename for temporary use.
8816 // --------------------------------------------------------------------------
8817 std::string file = std::filesystem::temp_directory_path();
8818 file.append("/rsocket.")
8819 .append(std::to_string(::getpid()))
8820 .append(".")
8821 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
8822
8823 // --------------------------------------------------------------------------
8824 // Open temporary file.
8825 // --------------------------------------------------------------------------
8826 FILE* fp = fopen(file.c_str(), "w+");
8827 if (fp == nullptr) randolf::rex::mk_exception("Cannot open temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8828 { int attr;
8829 ioctl(fileno(fp), FS_IOC_GETFLAGS, &attr);
8830 attr |= FS_NOATIME_FL // Don't update access time attribute
8831 | FS_NODUMP_FL // Don't include in filesystem backup dumps
8832 | FS_SECRM_FL; // Mark file for secure deletion (where supported)
8833 ioctl(fileno(fp), FS_IOC_SETFLAGS, &attr);
8834 }
8835 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
8836 if (fputs(pem_data, fp) == EOF) {
8837 fclose(fp);
8838 randolf::rex::mk_exception("Cannot write to temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8839 } // -x- if !fputs -x-
8840 fflush(fp);
8841
8842 // --------------------------------------------------------------------------
8843 // Attempt to load certificate chain file, but save the error code for later
8844 // because we need to clean up the temporary file before possibly throwing an
8845 // exception.
8846 // --------------------------------------------------------------------------
8847 int rc = SSL_CTX_use_certificate_chain_file(__tls_ctx, file.c_str());
8848
8849 // --------------------------------------------------------------------------
8850 // Overwrite the contenst of the temporary file before deleting it so as to
8851 // sabotage a simple attempt to undelete the file and access the certificate.
8852 //
8853 // We're also re-using the "len" local variable because it's not needed once
8854 // we get the loop started, and it's more optimal to not allocate yet another
8855 // local variable while "len" goes to waste. :D
8856 // --------------------------------------------------------------------------
8857 if (random_fill) { // This option is configurable
8858 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
8859 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
8860 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
8861 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
8862 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
8863 } // -x- for i -x-
8864 } // -x- if randfill -x-
8865 fchmod(fileno(fp), 0); // Remove all permissions
8866 fclose(fp); // Close file handle
8867 unlink(file.c_str()); // Delete temporary file
8868
8869 // --------------------------------------------------------------------------
8870 // Error check ... was delayed here until after temporary file cleanup.
8871 // --------------------------------------------------------------------------
8872 if (rc != 1) randolf::rex::mk_exception("Cannot use certificate chain file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8873
8874 return this;
8875 }; // -x- rsocket* tls_ctx_use_certificate_chain_pem -x-
8876
8877 /*======================================================================*//**
8878 @brief
8879 Load a TLS private key in PEM format from a text file and use it in the TLS
8880 context.
8881
8882 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8883 OpenSSL library doesn't document which errors may be returned)
8884
8885 @returns The same rsocket object so as to facilitate stacking
8886 @see tls_ctx_use_privatekey_file
8887 @see tls_ctx_use_privatekey_pem
8888 @see tls_ctx_check_privatekey
8889 @qualifier TLS
8890 *///=========================================================================
8891 rsocket* tls_ctx_use_privatekey_file(
8892 /// Pointer to ASCIIZ path-and-filename of private key file
8893 const char* file) {
8894 if (__debug) debug("tls_ctx_use_privatekey_file("
8895 + std::string(file)
8896 + ");");
8897 if (SSL_CTX_use_PrivateKey_file(__tls_ctx, file, SSL_FILETYPE_PEM) != 1) randolf::rex::mk_exception(std::string("Cannot use private key file ") + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8898
8899 return this;
8900 }; // -x- rsocket* tls_ctx_use_privatekey_file -x-
8901
8902 /*======================================================================*//**
8903 @brief
8904 Load a TLS private key in PEM format from a text file and use it in the TLS
8905 context.
8906
8907 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8908 OpenSSL library doesn't document which errors may be returned)
8909
8910 @returns The same rsocket object so as to facilitate stacking
8911 @see tls_ctx_use_privatekey_file
8912 @see tls_ctx_use_privatekey_pem
8913 @see tls_ctx_check_privatekey
8914 @qualifier TLS
8915 *///=========================================================================
8916 rsocket* tls_ctx_use_privatekey_file(
8917 /// Path and filename to private key file
8918 const std::string file) {
8919 if (__debug) debug("tls_ctx_use_privatekey_file("
8920 + file
8921 + ");");
8922 if (SSL_CTX_use_PrivateKey_file(__tls_ctx, file.c_str(), SSL_FILETYPE_PEM) != 1) randolf::rex::mk_exception("Cannot use private key file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
8923
8924 return this;
8925 }; // -x- rsocket* tls_ctx_use_privatekey_file -x-
8926
8927 /*======================================================================*//**
8928 @brief
8929 Load a TLS private key in PEM format from memory and use it in the TLS
8930 context.
8931
8932 Although this functionality doesn't exist in OpenSSL (at the time of writing
8933 this method), it's provided here in a manner that has exactly the same effect
8934 as the @ref tls_ctx_use_privatekey_file() methods, but without needing the
8935 PEM-formatted private key stored in a file beforehand.
8936
8937 @note
8938 The @c pem_data parameter is a pointer to the memory location that holds the
8939 PEM formatted private key data. If the length of this data isn't specified
8940 or is set to zero (default), then it will be treated as a multiline ASCIIZ
8941 string.
8942
8943 Behind the scenes, we're just writing the pem_data memory to a temporary
8944 file (with severely-limited permissions), then optionally overwriting that
8945 temporary file with random data prior to deleting it (this is the default,
8946 since better security practices should be the default, but on a secured
8947 system it may not be necessary and so this option can also be disabled to
8948 save CPU cycles and reduce overall disk-write I/O operations).
8949
8950 @throws randolf::rex::xALL Catch this exception for now (at this time, the
8951 OpenSSL library doesn't document which errors may be returned)
8952
8953 @returns The same rsocket object so as to facilitate stacking
8954 @see tls_ctx_use_privatekey_file
8955 @see tls_ctx_check_privatekey
8956 @qualifier TLS
8957 *///=========================================================================
8958 rsocket* tls_ctx_use_privatekey_pem(
8959 /// Pointer to private key data in PEM format
8960 const char* pem_data,
8961 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
8962 size_t len = 0,
8963 /// Whether to overwrite the temporary file with random data before deleting it
8964 const bool random_fill = true) {
8965 if (__debug) debug("tls_ctx_use_privatekey_pem(<buf>, " + std::to_string(len)
8966 + ", " + (random_fill ? "true" : "false")
8967 + ");");
8968
8969 // --------------------------------------------------------------------------
8970 // Measure size of private key if an ASCIIZ string was indicated.
8971 // --------------------------------------------------------------------------
8972 if (len == 0) len = std::strlen(pem_data);
8973
8974 // --------------------------------------------------------------------------
8975 // Generate filename for temporary use.
8976 // --------------------------------------------------------------------------
8977 std::string file = std::filesystem::temp_directory_path();
8978 file.append("/rsocket.")
8979 .append(std::to_string(::getpid()))
8980 .append(".")
8981 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
8982
8983 // --------------------------------------------------------------------------
8984 // Open temporary file.
8985 // --------------------------------------------------------------------------
8986 FILE* fp = fopen(file.c_str(), "w+");
8987 if (fp == nullptr) randolf::rex::mk_exception("Cannot cannot open temporary file (to use private key) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8988 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
8989 if (fputs(pem_data, fp) == EOF) {
8990 fclose(fp);
8991 randolf::rex::mk_exception("Cannot cannot write to temporary file (to use private key) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
8992 } // -x- if !fputs -x-
8993 fflush(fp);
8994
8995 // --------------------------------------------------------------------------
8996 // Attempt to load private key file, but save the error code for later
8997 // because we need to clean up the temporary file before possibly throwing an
8998 // exception.
8999 // --------------------------------------------------------------------------
9000 int rc = SSL_CTX_use_PrivateKey_file(__tls_ctx, file.c_str(), SSL_FILETYPE_PEM);
9001
9002 // --------------------------------------------------------------------------
9003 // Overwrite the contenst of the temporary file before deleting it so as to
9004 // sabotage a simple attempt to undelete the file and access the certificate.
9005 //
9006 // We're also re-using the "len" local variable because it's not needed once
9007 // we get the loop started, and it's more optimal to not allocate yet another
9008 // local variable while "len" goes to waste. :D
9009 // --------------------------------------------------------------------------
9010 if (random_fill) { // This option is configurable
9011 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
9012 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
9013 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
9014 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
9015 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
9016 } // -x- for i -x-
9017 } // -x- if randfill -x-
9018 fchmod(fileno(fp), 0); // Remove all permissions
9019 fclose(fp); // Close file handle
9020 unlink(file.c_str()); // Delete temporary file
9021
9022 // --------------------------------------------------------------------------
9023 // Error check ... was delayed here until after temporary file cleanup.
9024 // --------------------------------------------------------------------------
9025 if (rc != 1) randolf::rex::mk_exception("Cannot use private key file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
9026
9027 return this;
9028 }; // -x- rsocket* tls_ctx_use_privatekey_pem -x-
9029
9030 /*======================================================================*//**
9031 @brief
9032 Initiate the TLS handshake with the endpoint (which is presumed to be a
9033 server).
9034 This method makes it easier to support application-level commands such as @c
9035 STARTTLS (which are implemented in protocols like SMTP, POP3, IMAP4, MEOW,
9036 FTP, NNTP, LDAP, XMPP, etc.).
9037
9038 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9039 OpenSSL library doesn't document which errors may be returned)
9040
9041 @returns The same rsocket object so as to facilitate stacking
9042 @see connect()
9043 @see connect(std::string, int)
9044 @see TLS_NO_INGRESS
9045 @qualifier TLS
9046 *///=========================================================================
9047 rsocket* tls_do_handshake() {
9048 if (__debug) debug("tls_handshake(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9049 + ");");
9050 __rc_check_tls(SSL_do_handshake(__tls_fd));
9051 return this;
9052 }; // -x- rsocket* tls_do_handshake -x-
9053
9054 /*======================================================================*//**
9055 @brief
9056 Get OpenSSL's TLS structure.
9057 @returns TLS structure
9058 @returns nullptr = TLS structure not yet allocated
9059 @qualifier TLS
9060 *///=========================================================================
9061 const SSL* tls_fd() noexcept {
9062 return __tls_fd;
9063 }; // -x- int tls_fd -x-
9064
9065 /*======================================================================*//**
9066 @brief
9067 Return the current @ref rsocket_sni object that this @c rsocket will use when
9068 accepting incoming encrypted connections.
9069 @returns Pointer to @c rsocket_sni object
9070 @returns nullptr = SNI is not assigned
9071 @see name_sni
9072 @see tls_sni(rsocket_sni*)
9073 @qualifier TLS
9074 *///=========================================================================
9075 rsocket_sni* tls_sni() noexcept {
9076 return __tls_sni;
9077 }; // -x- rsocket_sni* tls_sni -x-
9078
9079 /*======================================================================*//**
9080 @brief
9081 Set the current @ref rsocket_sni object that this @c rsocket will use when
9082 accepting incoming encrypted connections.
9083
9084 Use the @ref name() method to find out which server name was supplied by the
9085 endpoint that triggered the SNI callback, regardless of whether it matches
9086 any of the TLS certificates used with this rsocket object or the rsocket_sni
9087 object that's associated with this rsocket object. If an SNI callback wasn't
9088 triggered, or if the endpoint didn't provide a server name, then it will
9089 remain unaffected (and the default {empty string} will remain unchanged).
9090 @returns The same rsocket object so as to facilitate stacking
9091 @see name_sni
9092 @see tls_sni
9093 @see is_tls_sni_match
9094 @qualifier TLS
9095 *///=========================================================================
9096 rsocket* tls_sni(
9097 /// Pointer to the @ref rsocket_sni object to use, or specify @c nullptr to
9098 /// remove SNI support from this rsocket object
9099 rsocket_sni* sni) noexcept {
9100
9101 // --------------------------------------------------------------------------
9102 // Remove SNI support.
9103 // --------------------------------------------------------------------------
9104 if (sni == nullptr) {
9105 if (__tls_sni != nullptr) SSL_CTX_set_client_hello_cb(__tls_ctx, nullptr, this);
9106
9107 // --------------------------------------------------------------------------
9108 // Add or set SNI support.
9109 // --------------------------------------------------------------------------
9110 } else {
9111 SSL_CTX_set_client_hello_cb(__tls_ctx, tls_sni_callback, this); // Configure SNI callbacks for TLS.
9112 //SSL_CTX_set_tlsext_servername_callback(__tls_ctx, tls_sni_callback); // Don't use this anymore; it's outdated
9113 //SSL_CTX_set_tlsext_servername_arg(__tls_ctx, this); // Don't use this anymore; it's outdated
9114
9115 } // -x- if !sni -x-
9116
9117 // --------------------------------------------------------------------------
9118 // Update internal pointer to the SNI map.
9119 // --------------------------------------------------------------------------
9120 __tls_sni = sni;
9121
9122 return this;
9123 }; // -x- rsocket_sni* tls_sni -x-
9124
9125private:
9126 /*======================================================================*//**
9127 @brief
9128 Get OpenSSL's TLS structure.
9129 @returns TLS structure
9130 @returns nullptr = TLS structure not yet allocated
9131 @qualifier TLS
9132 *///=========================================================================
9133 int static tls_sni_callback(
9134 /// OpenSSL's socket descriptor/handle
9135 SSL* tls_fd,
9136 /// Where to store the @c alert value
9137 int* al,
9138 /// Context-specific argument
9139 void* arg) noexcept {
9140
9141 // --------------------------------------------------------------------------
9142 // Internal variables.
9143 // --------------------------------------------------------------------------
9144 rsocket* r = (rsocket*)arg; // We may be updating the TLS context (this is normally the new client's rsocket object)
9145 const unsigned char* out = nullptr;
9146 size_t out_size = 0;
9147
9148 // --------------------------------------------------------------------------
9149 // Obtain a pointer to newly-allocated ClientHello fields data. If *out is
9150 // nullptr, it means that no TLSEXT_TYPE_server_name was received from the
9151 // client, and so the default TLS context will suffice. If out_size is less
9152 // than or equal to 2, then it also won't have what we need.
9153 // --------------------------------------------------------------------------
9154 if (!SSL_client_hello_get0_ext(tls_fd, TLSEXT_TYPE_server_name, &out, &out_size)
9155 || out == nullptr || out_size <= 2) return SSL_CLIENT_HELLO_SUCCESS; // 1
9156
9157 // --------------------------------------------------------------------------
9158 // Scan for TLSEXT_NAMETYPE_host_name, but if we can't find it then we'll
9159 // just leave the default TLS context as is.
9160 // --------------------------------------------------------------------------
9161 unsigned char* p = (unsigned char*)out;
9162 size_t len = (*(p++) << 8);
9163 len += *(p++);
9164 if (len + 2 != out_size) goto finish;
9165
9166 // --------------------------------------------------------------------------
9167 // We're taking a shortcut by examining only the first element in the list,
9168 // but in the future we need to make this more robust in case other types of
9169 // elements precede what we're looking for.
9170 //
9171 // Unfortunately, there's no documentation that properly-explains the format
9172 // of the list, so some deeper research into OpenSSL's source code will be
9173 // needed (a cursory look so far has not yielded the necessary insight).
9174 //
9175 // TODO: Turn this into a loop that supports future clients that provide
9176 // multiple SNI server names in their requests. (Although this isn't
9177 // occuring at present with common end-user tools such as web browsers
9178 // and eMail software, it may happen in the future as client/server
9179 // software becomes more savvy.)
9180 // --------------------------------------------------------------------------
9181 if (out_size == 0 || *p++ != TLSEXT_NAMETYPE_host_name) goto finish;
9182
9183 // --------------------------------------------------------------------------
9184 // Avoid buffer overrun caused by corrupt or prematurely-truncated data.
9185 // --------------------------------------------------------------------------
9186 if (--out_size <= 2) goto finish;
9187
9188 // --------------------------------------------------------------------------
9189 // Extract and use the hostname (SNI server name) that was supplied by the
9190 // endpoint so that the correct TLS certificate can be selected and assigned.
9191 // --------------------------------------------------------------------------
9192 len = (*(p++) << 8);
9193 len += *(p++);
9194 if (!(len + 2 > out_size)) { // Only process SNI server name string if it's at least 2 bytes long
9195 // Debug: std::cout << "Server name: " << (const char*)p << std::endl; // Debug
9196
9197 // --------------------------------------------------------------------------
9198 // Obtain the correct TLS context (wildcards supported) that is associated
9199 // with the hostname (SNI server name) that was supplied by the endpoint.
9200 // --------------------------------------------------------------------------
9201 const char* sni_name = (const char*)p;
9202 SSL_CTX* new_ctx = r->__tls_sni->get_ctx(sni_name, true, r->tls_ctx());
9203
9204 // --------------------------------------------------------------------------
9205 // Change TLS context so that encryption with the endpoint uses the correct
9206 // TLS certificate (otherwise the endpoint will indicate security risk errors
9207 // to end users, log files, etc., and may {should} reject the connection).
9208 // --------------------------------------------------------------------------
9209//std::cout << "SNI setup... " << sni_name << std::endl;
9210 SSL_set_SSL_CTX(tls_fd, new_ctx);
9211//std::cout << "---1--- " << r->__tls_new_endpoint << std::endl;
9212 r->__tls_new_endpoint->tls_ctx(new_ctx);
9213//std::cout << "---2---" << std::endl;
9214 r->__tls_new_endpoint->__name_sni.assign(sni_name);
9215//std::cout << "SNI name() = " << r->__tls_new_endpoint->name() << std::endl;
9216 r->__tls_new_endpoint->__tls_sni_match = true;
9217
9218 } // -x- if len+2 -x-
9219
9220 finish:
9221 // --------------------------------------------------------------------------
9222 // Free the ClientHello fields data, then return SSL_CLIENT_HELLO_SUCCESS.
9223 // Even if we encountered a problem with the ClientHello fields data, we
9224 // still return SSL_CLIENT_HELLO_SUCCESS so that the TLS context will be
9225 // accepted as valid.
9226 //
9227 // If we return SSL_CLIENT_HELLO_ERROR the connection will fail unnecessarily
9228 // for valid TLS certificates. If the ClientHello fields data is malformed
9229 // to a degree that doesn't satisfy OpenSSL, then OpenSSL will reject it, so
9230 // there's really no point in duplicating what OpenSSL already does properly,
9231 // which OpenSSL will pass through the standard error channels with normal
9232 // error details-and-diagnostics anyway.
9233 // --------------------------------------------------------------------------
9234 //OPENSSL_free((char*)out); // OpenSSL documentation says to do this, but it fails and crashes the program
9235
9236 return SSL_CLIENT_HELLO_SUCCESS; // 1
9237 }; // -x- int tls_sni_callback -x-
9238
9239public:
9240 /*======================================================================*//**
9241 @brief
9242 Convert a 48-bit integer to a machine address in the form of @c
9243 xx:xx:xx:xx:xx:xx where every instance of @c xx is a hexadecimal
9244 representation of each respective 8-bit byte portion.
9245
9246 This method is needed because we don't want to bring in the heavy fmt::format
9247 class as a dependency.
9248 @returns Mac address as 17-character in the typical format expected by system
9249 administrators
9250 @qualifier TLS
9251 *///=========================================================================
9252 static std::string to_mac(
9253 /// Pointer to 48-bit integer
9254 const void* addr) noexcept {
9255 std::string h;
9256 h.resize(18); // 48-bit mac address needs 6 hexadecimal pairs of nybbles delimited by colons plus a NULL terminator
9257 h.resize(snprintf(h.data(),
9258 h.size(),
9259 "%02x:%02x:%02x:%02x:%02x:%02x",
9260 ((u_char*)addr)[0],
9261 ((u_char*)addr)[1],
9262 ((u_char*)addr)[2],
9263 ((u_char*)addr)[3],
9264 ((u_char*)addr)[4],
9265 ((u_char*)addr)[5])); // Convert, and truncate NULL terminator
9266 return h;
9267 }; // -x- std::string to_mac -x-
9268
9269 /*======================================================================*//**
9270 @brief
9271 Convert a 48-bit integer to a node address in the form of @c xxxx:xxxx:xxxx
9272 where every instance of @c xxxx is a hexadecimal representation of each
9273 respective 16-bit word portion.
9274
9275 This method is needed because we don't want to bring in the heavy fmt::format
9276 class as a dependency.
9277 @returns Node address as 14-character in the typical format expected by
9278 network administrators
9279 @qualifier TLS
9280 *///=========================================================================
9281 static std::string to_node(
9282 /// Pointer to 48-bit integer
9283 const void* addr) noexcept {
9284 std::string h;
9285 h.resize(15); // 48-bit node address needs 3 hexadecimal sets of words delimited by colons plus a NULL terminator
9286 h.resize(snprintf(h.data(),
9287 h.size(),
9288 "%04x:%04x:%04x",
9289 ((u_int16_t*)addr)[0],
9290 ((u_int16_t*)addr)[1],
9291 ((u_int16_t*)addr)[2])); // Convert, and truncate NULL terminator
9292 return h;
9293 }; // -x- std::string to_node -x-
9294
9295 /*======================================================================*//**
9296 @brief
9297 Send a formatted string to the @ref rsocket endpoint.
9298
9299 The @c format is described in the documentation for the POSIX or Standard C
9300 Library @c printf() function.
9301 @throws randolf::rex::xEBADF The underlying socket is not open
9302 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
9303 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
9304 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
9305 @throws randolf::rex::xENOMEM Insufficient memory
9306 @returns The same rsocket object so as to facilitate stacking
9307 @see eol_fix_printf
9308 @see is_eol_fix_printf
9309 @see net_io
9310 @see printf
9311 @see printfline
9312 @see vprintfline
9313 @qualifier POSIX
9314 @qualifier TLS
9315 *///=========================================================================
9316 rsocket* vprintf(
9317 /// Format string to use
9318 const char* format,
9319 /// Variadic arguments in @c va_list format
9320 va_list args) {
9321 char* buf;
9322 int rc = ::vasprintf(&buf, format, args);
9323 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
9324 if (__eol_fix_printf && !__eol.empty()) {
9325 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
9326 ::free(buf);
9327 __send(str.c_str(), str.length());
9328 } else {
9329 try {
9330 __send(buf, rc);
9331 } catch (std::exception& e) { // Free buf then re-throw the exception
9332 ::free(buf); // Prevent memory leak when an exception is thrown
9333 throw;
9334 }
9335 } // -x- if __eol_fix_printf -x-
9336 return this;
9337 }; // -x- rsocket* vprintf -x-
9338
9339 /*======================================================================*//**
9340 @brief
9341 Send a formatted string to the @ref rsocket endpoint, and append an EoL
9342 sequence.
9343
9344 The @c format is described in the documentation for the POSIX or Standard C
9345 Library @c printf() function.
9346 @throws randolf::rex::xEBADF The underlying socket is not open
9347 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
9348 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
9349 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
9350 @throws randolf::rex::xENOMEM Insufficient memory
9351 @returns The same rsocket object so as to facilitate stacking
9352 @see eol
9353 @see eol_fix_printf
9354 @see is_eol_fix_printf
9355 @see net_io
9356 @see printf
9357 @see printfline
9358 @see sendline
9359 @see vprintf
9360 @qualifier TLS
9361 *///=========================================================================
9362 rsocket* vprintfline(
9363 /// Format string to use
9364 const char* format,
9365 /// Variadic arguments in @c va_list format
9366 va_list args) {
9367 char* buf;
9368 int rc = ::vasprintf(&buf, format, args);
9369 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
9370 if (__eol_fix_printf && !__eol.empty()) {
9371 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
9372 .append(__eol);
9373 ::free(buf);
9374 __send(str.c_str(), str.length());
9375 } else {
9376 try {
9377 __sendline(buf, rc);
9378 } catch (std::exception& e) { // Free buf then re-throw the exception
9379 ::free(buf); // Prevent memory leak when an exception is thrown
9380 throw;
9381 }
9382 } // -x- if __eol_fix_printf -x-
9383 return this;
9384 }; // -x- rsocket* vprintfline -x-
9385
9386 private:
9387 /*======================================================================*//**
9388 Track unencrypted bytes received. When the number of bytes is negative or
9389 zero, it isn't recorded.
9390 This is an internal function.
9391 @returns same value provided in @ref n
9392 @qualifier TLS
9393 *///=========================================================================
9394 int __track_bytes_rx(
9395 /// Number of bytes transferred
9396 const int n) {
9397 if (n > 0) __bytes_rx.fetch_add(n, std::memory_order_relaxed);
9398 return n;
9399 }; // -x- __track_bytes_rx -x-
9400
9401 /*======================================================================*//**
9402 Track unencrypted bytes transmitted. When the number of bytes is negative or
9403 zero, it isn't recorded.
9404 This is an internal function.
9405 @returns same value provided in @ref n
9406 @qualifier TLS
9407 *///=========================================================================
9408 int __track_bytes_tx(
9409 /// Number of bytes transferred
9410 const int n) {
9411 if (n > 0) __bytes_tx.fetch_add(n, std::memory_order_relaxed);
9412 return n;
9413 }; // -x- __track_bytes_tx -x-
9414
9415 /*======================================================================*//**
9416 Track encrypted bytes received. When the number of bytes is negative or
9417 zero, it isn't recorded.
9418 This is an internal function.
9419 @returns same value provided in @ref n
9420 @qualifier TLS
9421 *///=========================================================================
9422 int __track_crypt_rx(
9423 /// Number of bytes transferred
9424 const int n) {
9425 if (n > 0) __crypt_rx.fetch_add(n, std::memory_order_relaxed);
9426 return n;
9427 }; // -x- __track_crypt_rx -x-
9428
9429 /*======================================================================*//**
9430 Track encrypted bytes transmitted. When the number of bytes is negative or
9431 zero, it isn't recorded.
9432 This is an internal function.
9433 @returns same value provided in @ref n
9434 @qualifier TLS
9435 *///=========================================================================
9436 int __track_crypt_tx(
9437 /// Number of bytes transferred
9438 const int n) {
9439 if (n > 0) __crypt_tx.fetch_add(n, std::memory_order_relaxed);
9440 return n;
9441 }; // -x- __track_crypt_tx -x-
9442
9443 }; // -x- class rsocket -x-
9444
9445}; // -x- namespace randolf -x-
9446
9447// Save this for a future sendlines() methods.
9448// const void* msg_ptr = msg.c_str(); // Prevent repeated calls to c_str() method
9449// const int len = msg.length(); // Prevent repeated calls to length() method