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 <algorithm> // std::min and std::max
13#include <atomic>
14#include <bit> // std::endian
15#include <cstdarg> // std::va_list
16#include <cstring> // std::strlen
17#include <ctime> // std::strftime
18#include <exception> // std::exception
19#include <filesystem> // std::filesystem::temp_directory_path
20#include <initializer_list>
21#include <iostream> // std::put_date
22#include <map> // std::map
23#include <memory> // std::shared_ptr
24#include <mutex> // std::mutex
25#include <regex>
26#include <set>
27//#include <sstream> // std::ostringstream
28#include <unordered_map> // std::unordered_map
29#include <vector>
30
31#include <ifaddrs.h>
32#include <netdb.h>
33#include <poll.h>
34#include <string.h> // strerror()
35#include <unistd.h>
36
37#include <arpa/inet.h>
38
39#include <linux/fs.h> // Flags for ioctl()
40
41#include <net/if.h> // ifreq structure used by bind()
42
43#include <netinet/icmp6.h>
44#include <netinet/in.h>
45
46#include <netpacket/packet.h> // struct sockaddr_ll
47
48#include <openssl/err.h>
49#include <openssl/ossl_typ.h>
50#include <openssl/ssl.h>
51
52#include <sys/ioctl.h>
53#include <sys/socket.h>
54#include <sys/stat.h> // fchmod()
55#include <sys/time.h> // TIMEVAL_TO_TIMESPEC macro
56#include <sys/types.h>
57#include <sys/un.h>
58
59static_assert((sizeof(__time_t) * CHAR_BIT) >= 64, "__time_t of at least 64-bit is required");
60
61namespace randolf {
62
63 /*======================================================================*//**
64 @brief
65 This @ref rsocket class provides an easy-to-use and thoroughy-implemented
66 object-oriented socket I/O interface for C++, intended to make socket I/O
67 programming (with or without TLS encryption) easier and more enjoyable.
68
69 Here's a short list of benefits that are helpful in developing high quality
70 code that's consistent-and-reliable, and improves overall productivity:
71
72 - eliminating the need to repeatedly write blocks of code that check for
73 errors, by throwing exceptions instead (see @ref randolf::rex::rex class
74 for details and the long list of exceptions that are supported)
75 - eliminating the need to track socket descriptors
76 - eliminating the need to handle encrypted I/O separately (most functions)
77 - eliminating the need to manage memory for many common structures used to
78 interface with socket options, etc.
79 - eliminating the need to record socket I/O statistics with every call to
80 underlying socket I/O functions (see @ref randolf::rsocket_io for
81 details)
82 - text-line reading/writing with an adapative approach (invented by
83 Randolf Richardson in the 1980s for a custom BBS software project) to
84 automatically detect EoL (End-of-Line) character sequences (unless the
85 developer provides a specific sequence via an @ref eol method) that can
86 determine whether an endpoint is sending Linux/UNIX (standard), MacOS,
87 DOS CR/LF, or even broken LF/CR (reverse of DOS) EoL sequences
88 - transparent support for encryption with many additional features,
89 including STARTTLS, ingress/egress policy enforcement, and SNI
90 - eliminating the complexity of handling events with poll(), select(), and
91 related functions (see the @ref randolf::rsocket_mux class for details)
92 - providing a variety of other useful features that make it easier to
93 communicate with socket endpoints, such as receiving/sending an entire
94 structure via a single call to the new-and-specialized @ref recv_struct
95 or @ref send_struct methods, respectively
96
97 An rsocket is either the endpoint that our underlying socket will connect to,
98 or it's the server daemon that our underying socket will listen() to and
99 accept() [inbound] connections from.
100
101 @par Use case
102
103 Using the C interface, the programming must check for errors by testing the
104 response codes (most of which are consistent, with a few subtle outliers),
105 which leads to a lot of additional error-checking code with the potential for
106 unintended errors (a.k.a., bugs). This style is necessary in C, but with C++
107 the way to handle errors is with exceptions, so I created this rsocket class
108 to handle all these tedious details behind-the-scenes and, for any socket
109 errors, to generate exceptions so that source code can be greatly simplified
110 (and, as a result, also easier to read and review).
111
112 Pre-allocating buffers is also handled internally, which is particularly
113 useful when making repeated calls to recv() and related methods. These
114 methods return `std::shared_ptr<structure>` (a C++ smart pointer that aids
115 in the prevention of resource leaks) or `std::vector<char>` (resized to the
116 actual number of bytes received) or `std::string` as appropriate, which
117 eliminates the need to track @c size_t separately.
118
119 @par Conventions
120 Lower-case letter "r" is regularly used in partial example code to represent
121 an instantiated rsocket object.
122
123 An ASCIIZ string is a C-string (char* array) that includes a terminating null
124 (0) character at the end.
125
126 The following custom qualifiers are incorporated into headings by Doxygen
127 alongside method titles throughout the documentation:
128 - @c POSIX denotes a method that is based on POSIX functions by the same
129 name and don't deviate significantly from the POSIX function arguments
130 (intended to be helpful to developers transitioning to/from rsocket or
131 working on source code that utilizes @ref rsocket and POSIX functions)
132 - @c TLS denotes that a method works properly with TLS-encrypted sockets
133 (most of the POSIX functions have been made to work properly with TLS,
134 but for the few rare cases of functions that can't be made to work with
135 TLS an effort has also been made to mention this using Doxygen's
136 "warning" sections in addition to omitting the TLS qualifier)
137
138 @par Getting started with a few simple examples
139
140 This is an example of connecting to an HTTP server, using the "GET" command
141 to request the home page (using HTTP/1.0), then receiving-and-displaying the
142 resulting web page's contents via STDOUT (or sending an error message to
143 STDERR). Finally, we exit with an EXIT_SUCCESS (or EXIT_FAILURE) code.
144
145 @code{.cpp}
146 #include <iostream> // std::cout, std::cerr, std::endl, etc.
147 #include <randolf/rex>
148 #include <randolf/rsocket>
149
150 int main(int argc, char *argv[]) {
151 try {
152 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
153 r.connect("www.example.com", 80);
154 r.sendline("GET / HTTP/1.0");
155 r.sendline("Host: www.example.com");
156 r.sendline("Connection: close");
157 r.send_eol();
158 while (r.is_open()) {
159 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
160 } // -x- while data -x-
161 r.close();
162 } catch (const randolf::rex::xALL e) {
163 std::cerr << "Socket exception: " << e.what() << std::endl;
164 return EXIT_FAILURE;
165 } catch (const std::exception e) {
166 std::cerr << "Other exception: " << e.what() << std::endl;
167 return EXIT_FAILURE;
168 }
169 return EXIT_SUCCESS;
170 } // -x- int main -x-
171 @endcode
172
173 Parameter stacking is supported (with methods that return @c rsocket*); in
174 this example, notice that semicolons (";") and "r." references are omittted
175 (when compared with the above):
176
177 @code{.cpp}
178 #include <iostream> // std::cout, std::cerr, std::endl, etc.
179 #include <randolf/rex>
180 #include <randolf/rsocket>
181
182 int main(int argc, char *argv[]) {
183 try {
184 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
185 r.connect("www.example.com", 80)
186 ->sendline("GET / HTTP/1.0")
187 ->sendline("Host: www.example.com")
188 ->sendline("Connection: close")
189 ->send_eol();
190 while (r.is_open()) {
191 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
192 } // -x- while data -x-
193 r.close();
194 } catch (const randolf::rex::xALL e) {
195 std::cerr << "Socket exception: " << e.what() << std::endl;
196 return EXIT_FAILURE;
197 } catch (const std::exception e) {
198 std::cerr << "Other exception: " << e.what() << std::endl;
199 return EXIT_FAILURE;
200 }
201 return EXIT_SUCCESS;
202 } // -x- int main -x-
203 @endcode
204
205 @par Features
206
207 This is meant to be a comprehensive socket class for C++, which is intended
208 to make socket I/O coding easier for developers with some key features:
209
210 - easy conversion from C-style POSIX sockets due to API consistency
211 - transparent TLS support (OpenSSL dependency)
212 - keys and certificate chains can also be loaded from memory
213 - SNI support (see the @ref rsocket_sni class for more information)
214 - underlying socket handle is accessible (via the @ref socket_fd() method)
215 - socket options are easier to get and to set
216 - sensible support for future or unknown socket options
217 - errors are presented as ~100 separate exception classes: @ref rex::rex
218 - one parent exception class makes it easier to catch all socket errors
219 - a few exceptions groups are also provided to catch groups of errors
220 - new buffers are returned as std::shared_ptr's to eliminate memory leaks
221 - constructors with sensible defaults help to simplify coding
222 - documentation includes code samples (with @c \#include lines as needed)
223 - debug output with helpful output (and option to set output file handle)
224 - low-overhead is considered (this is why there's a bit more overloading)
225 - thread-safety is noted where it is absolutely available (if any caution
226 is warranted, it will also be noted)
227 - can send ASCIIZ strings (a.k.a., C-strings) without needing to specify
228 string length
229 - can send @c std::string (which tracks its own string length)
230 - each socket can optionally have a @ref name arbitrarily assigned (e.g.,
231 by an algorithm, or even populated with the connecting user's name, or
232 whatever purpose the development goals find it useful for)
233
234 Additional features that are not part of the typical POSIX standard, but
235 deserve special mention because they are needed so often:
236
237 - easy access to internal I/O counters (see @ref rsocket_io for details)
238 - your rsocket_io structure can be updated automatically by rsocket's
239 destructor after underlying socket is closed (see @c net_io_final()
240 method for details)
241 - printf(), printfline(), vprintf(), vprintfline() // Formatted strings
242 - with automatic EoL sequence substitution (and/or the addition of)
243 - recv_asciiz(), send_asciiz() // ASCIIZ string I/O operations
244 - recv_byte() , send_byte() // 8-bit byte operations (LSB/MSB is N/A)
245 - recv_struct(), send_struct() // Multi-byte operations
246 - recv_uint16(), send_uint16() // 16-bit operations with LSB/MSB variants
247 - recv_uint32(), send_uint32() // 32-bit operations with LSB/MSB variants
248 - recv_uint64(), send_uint64() // 64-bit operations with LSB/MSB variants
249 - recvline(), recv_rline(), sendline() // ASCII text line I/O operations
250 - class-wide configurable newline sequence (defaults to @e autodetect)
251 - @ref rsocket_group class for grouping rsockets (and automatic inclusion
252 by accept() and accept4() methods; this may be a different group from
253 whichever group the parent rsocket is in, if it's even in one)
254 - option to send data to all rsocket objects in the rsocket_group
255 - with support from the rsocket_mux class (for multiplexing operations)
256 - automatic naming policies (possibly like net_io() formatting style)
257
258 Some advanced features are planned that exceed what the basic socket I/O
259 functions provide, but are also needed:
260
261 - recv_uint128()/send_uint128() // 128-bit operations w/ LSB/MSB variants
262 - recv_uint(n)/send_uint(n) where "n" specifies the number of bits (which
263 must be a multiple of 8), with LSB/MSB variants
264 - auto-detection of inbound TLS connection (this is turned off by default)
265 - This is not the same as STARTTLS (an application-level command, for
266 which the @ref tls_do_handshake() method will likely be used)
267 - simple timing tracking options using timing_start() and timing_stop()
268 methods, the results of which can be retrieved with timing_get() or a
269 similarly-named group of methods
270
271 Other features that are not a high priority:
272
273 - internal support for portability to Microsoft Windows, which is a major
274 undertaking that I know will be time-consuming since Windows Sockets
275 exhibit some nuanced behaviours and are not consistent with POSIX
276 sockets APIs that are used by Linux, UNIX, MacOS, and pretty much all
277 other Operating Systems. Because of this, MS-Windows portability just
278 isn't a high priority for me (without sufficient demand and sufficient
279 funding so I can commit my time without missing mortgage payments,
280 student loan payments {for my kids}, various living expenses, etc.).
281
282 @par Requirements
283 The @c __time_t structure must be 64-bit (or larger). This becomes espcially
284 important for the @ref recvline method, in addition to Year 2038 compliance.
285
286 @par Notes
287
288 I use the term "ASCIIZ string" to indicate an array of characters that's
289 terminated by a 0 (a.k.a., null). Although this is very much the same as a
290 C-string, the difference is that in many API functions a C-string must often
291 be accompanied by its length value. When referring to an ASCIIZ string, I'm
292 intentionally indicating that the length of the string is not needed because
293 the string is null-terminated. (This term was also commonly used in assembly
294 language programming in the 1970s, 1980s, and 1990s, and as far as I know is
295 still used by machine language programmers today.)
296
297 UTF-8 works without any problems as it is backward-compatible to 8-bit ASCII,
298 and because @c std::string uses 8-bit bytes to store strings internally. Do
299 keep in mind that the manipulation of UTF-8 substrings will require working
300 with UTF-8 codepoints that may consume one or more bytes, which is beyond the
301 scope of the impartial (to UTF-8 and ASCII) functionality that this rsocket
302 class provides.
303
304 UTF-8 newline codepoints NEL (c2 85), LS (e2 80 a8), and PS (e2 80 a9) garner
305 no special handling at this time - unlike CR/LF (0d/0a) - and are merely
306 treated as non-newline codepoints. There is a possibility of adding support
307 for this in the future, but additional research and planning is required to
308 make sure this works properly. What is most likely is that some UTF-8 flags
309 will be added to support each of these (which will probably be disabled by
310 default) that will be integrated into the readline() methods. This also
311 depends on how widely used these particular codepoints are, and pending
312 further research to determine whether these really are supposed to be used
313 functionally as newlines...
314
315 So far, there are two UTF-8 codepoints that absolutely are not functional,
316 yet which a small number of people have mistakenly assumed are:
317 - <sup>C</sup><sub>R</sub> = superscript "C" with subscript "R" (e2 90 9d)
318 - <sup>N</sup><sub>L</sub> = superscript "N" with subscript "L" (e2 90 a4)
319
320 The special characters above are intended to represent the Carriage-Return
321 and New-Line respectively in documentation such as ASCII character reference
322 charts, which were used mostly by IBM in the 1970s and 1980s for instruction
323 manuals (and in other documentation), and also on a few keyboard overlays.
324
325 @par Background
326
327 I created this class to make it easier to write internet server daemons. I
328 started out using C-style socket functions (because C++ doesn't come with a
329 socket class), but I found that I didn't enjoy mixing something as important
330 and detailed as socket I/O in a procedural way into the object-oriented
331 paradigm that C++ provides.
332
333 After looking for existing solutions (none of which served as comprehensive
334 replacements for socket I/O), I embarked on creating the rsocket class, and
335 then I began to understand why this probably hadn't been done -- it's a
336 massive undertaking, primarily because there are a lot of functions that are
337 needed to handle socket I/O. Further, [at the time of this writing] the @c
338 sockaddr_storage structure wasn't as widely used as it should be, and so
339 information about it tended to be scarce, incomplete, or incorrect (further
340 research, and diving down into some pretty deep "rabbit holes," was required
341 to understand this properly, which was worthwhile because it resulted in
342 having transparent support for IPv4 and IPv6 without breaking backward
343 compatibility for code expecting specific structures).
344
345 Moving error codes into exceptions is also a major effort because they are
346 diverse and plentiful, and there are so many errors that can occur at various
347 stages for many different reasons. There are also a few outlier functions
348 that require slightly different approaches to error handling due to subtly
349 different rules for handling their errors, and so the exception-generation
350 wasn't as straight-forward as one might optimistically expect, but this is
351 one of the many benefits of the object-oriented programming pardigm because
352 handling edge cases internally results in a consistent error-handling
353 interface using exceptions that also simplifies the source code. (Need to
354 handle a specific set of conditions? Catch the relevant exceptions for those
355 cases in an inner set of exceptions, and just catch all the others in a more
356 general way without the added complexity of repeatedly checking for errors
357 every step along the way.)
358
359 So, I dedicated time to make this work, and with the intention of making it
360 an open source project once I got it into a state that's ready for the
361 general public. This required putting my other C++ projects on hold, which
362 was fine because they didn't have strict deadlines and using this socket
363 class in them will speed up development in the long-term anyway, so it's
364 clearly worth the effort to me ... and I sincerely hope that my efforts will
365 be helpful to others too.
366
367 My background in programming began when I was a young child, teaching myself
368 BASIC and then machine language (when I found BASIC to be too limited) before
369 moving on to other languages like Perl and Java many years later. Eventually
370 I circled around to C (which I chose to learn the hard way by writing some
371 PostgreSQL extensions) and then C++ a few years after that. I have a lot of
372 experience with socket communications, including fully-featured DNS resolver
373 library code in machine language for Novell's NetWare that used C calling
374 conventions and supported varargs, which worked well for the few developers
375 who needed or wanted it.
376
377 @par History
378 - 2022-Nov-09 v1.00 Initial version
379 - 2022-Nov-26 v1.00 Moved exception handling to a separate class
380 - 2022-Nov-28 v1.00 Completed readline/send functionality
381 - 2022-Dec-03 v1.00 Added endianness transparency
382 - 2022-Dec-04 v1.00 Added printf() support
383 - 2022-Dec-24 v1.00 Added socket MUXing
384 - 2023-Feb-22 v1.00 Added TLS/SSL support
385 - 2023-Mar-10 v1.00 Added TLS-SNI support
386 - 2023-Apr-19 v1.00 Added TLS certificate loading from memory
387 - 2023-May-24 v1.00 Added support for clang++ compilation
388 - 2023-Jun-09 v1.00 Improvements to dynamic internal read buffering
389 - 2023-Oct-31 v1.00 Improvements to various classes
390 - 2024-Feb-21 v1.00 Added is_buffered() method
391 - 2024-Mar-31 v1.00 Completed @ref recvline (implemented a work-around for
392 a subtle SSL_peek failure to extract additional data
393 when a user at an end-point is communicating with
394 "icanon" mode enabled)
395 - 2024-May-24 v1.00 Changed source code to accomodate @c clang++ compiler
396 - 2024-Jun-04 v1.00 Added @c POSIX and @c TLS qualifiers to all applicable
397 methods (Doxygen incorporates into the documentation)
398 - 2024-Oct-23 v1.00 Various minor improvements to the documentation since
399 the previous update
400 - 2024-Nov-05 v1.00 Added eol_consumed_seq() method
401 - 2024-Nov-10 v1.00 Added discard() method
402 - 2024-Nov-17 v1.00 Added recv_rline() method
403 - 2024-Nov-19 v1.00 Added recv_as_string() method
404 - 2024-Nov-22 v1.00 Added discard_line() method
405 - 2024-Nov-28 v1.00 Switched internal ring buffer to a proper ring buffer
406 that's now available in the rring class (which was made
407 initially for this purpose)
408 - 2024-Dec-08 v1.00 Improved shutdown() method to handle SSL shutdown fully
409 and added a parameter to prevent calling SSL_shutdown()
410 - 2024-Dec-10 v1.00 Changed eos() method to use faster ioctl POSIX function
411 and OpenSSL's SSL_has_pending function (with TLS)
412 - 2024-Dec-10 v1.00 Added RECVLINE_FLAGS enum to expand the functionality
413 of how the recvline() and recv_rline methods deal with
414 data in specific scenarios, which helps to satisify the
415 needs of specific advanced data processing scenarios
416 - 2024-Dec-23 v1.00 Updated calls to methods that were renamed today in the
417 @ref rring class
418 - 2024-Dec-28 v1.00 Improvements to internal handling of SNI in all four
419 accept() methods along with the SNI callback function,
420 and added @ref is_tls_sni_has_name() method; <s>also
421 added the @c TLS_SNI_PROMISCUOUS flag</s>
422 - 2024-Dec-30 v1.00 Added the setsockopt() method without a value (and uses
423 a zero-length value internally)
424 - 2024-Dec-31 v1.00 Removed SSL_BIO handle generation calls along with all
425 references to it because we don't need it since we're
426 already handling our own buffering directly and working
427 with OpenSSL's raw API functions directly (the extra
428 layer of abstraction through BIO therefore doesn't
429 benefit what we're doing, and also adds slight overhead
430 that will become noticeable on extremely busy systems)
431 - 2025-Jan-05 v1.00 Added send_rline() method
432 - 2025-Jan-07 v1.00 Made major improvements to the @c recv(), @c recvline()
433 and @c recv_rilne methods, and added an internal class
434 that utilizes RAII to temporarily change the timeout
435 (for the @c recvline and @c recv_rline methods), then
436 finally restores the previous timeout, and added debug
437 output support accordingly; also made major
438 improvements to all recv() and related methods in how
439 they interact with the internal ring buffers (the code
440 is more efficient and a lot simpler)
441 - 2025-Jan-08 v1.00 Updated the @ref timeout methods by adding @c direction
442 (@c SO_RCVTIMEO {default} and @c SO_SNDTIMEO options),
443 and changed @c seconds from type @c int to type @c long
444 - 2025-Jan-18 v1.00 Improved internal memory handling, optimized discard()
445 and discard() line method loops, and simplified the
446 family() method, on top of various minor improvements
447 throughout this entire class during the past week
448 @version 1.00
449 @author Randolf Richardson
450 *///=========================================================================
451 class rsocket {
452
453 // --------------------------------------------------------------------------
454 // The rsocket_group class needs access to some of our internal variables.
455 // --------------------------------------------------------------------------
456 friend class rsocket_group; // Grant the rsocket_group class access to our internals
457
458 // --------------------------------------------------------------------------
459 // Socket variables.
460 // --------------------------------------------------------------------------
461 int __socket_fd = 0; // Raw socket handle
462 int __socket_type = 0; // Socket type (e.g., SOCK_STREAM)
463 int __socket_protocol = 0; // Socket protocol (e.g., AF_INET)
464 struct sockaddr_storage* __socket_addr = (struct sockaddr_storage*)::malloc(sizeof(sockaddr_storage)); // Initialize to all elements to their default values
465 socklen_t __socket_addr_size = sizeof(sockaddr_storage); // We need to point to this (and it might be modified)
466 int __socket_backlog = SOMAXCONN; // Default backlog is 4096 on Linux, and 128 on older systems
467
468 // --------------------------------------------------------------------------
469 // Socket flags (internal, but with get-methods to provide read-only access).
470 // --------------------------------------------------------------------------
471 std::atomic_bool __socket_open = false; // Socket is open by way of socket() function
472 std::atomic_bool __socket_connected = false; // Socket is connected
473
474 // --------------------------------------------------------------------------
475 // TLS flags and variables.
476 // --------------------------------------------------------------------------
477 std::atomic_bool __tls = false; // Indicates whether socket I/O is encrypted with OpenSSL
478 SSL_CTX* __tls_ctx = nullptr; // TLS context (nullptr == no encryption initialized)
479 rsocket_sni* __tls_sni = nullptr; // SNI maps
480 bool __tls_sni_has_name = false; // Set to TRUE only if SNI received a hostname
481 bool __tls_sni_match = false; // Set to TRUE only if SNI matched in the SNI callback
482 SSL* __tls_fd = nullptr; // TLS connection structure (nullptr == no connection)
483 bool __tls_exclusive = false; // See enum TLS_FLAGS::TLS_EXCLUSIVE
484 bool __tls_egress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_EGRESS
485 bool __tls_ingress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_INGRESS
486 bool __tls_server_mode = true; // See enum TLS_FLAGS::TLS_SERVER
487 inline static std::atomic_int __fn_counter = 0; // Used in generating unique-and-threadsafe temporary filenames
488 const rsocket* __rtemplate = nullptr; // Weak pointer to rsocket that was a template for this rsocket
489
490 // --------------------------------------------------------------------------
491 // Keep track of test for whether this host stores integers using big endian.
492 // --------------------------------------------------------------------------
493 inline static const bool __endian_is_msb = htons(42) == 42; // TRUE==MSB / FALSE==LSB
494
495 // --------------------------------------------------------------------------
496 // General variables. Atomic variables are used for variables as needed to
497 // support operations in a thread-safe manner.
498 // --------------------------------------------------------------------------
499 std::string __name; // Arbitrary name of this rsocket (used by some applications)
500 std::string __name_sni; // SNI hostname (only used when tls_sni is configured)
501 std::atomic_bool __debug = false;
502 std::atomic<std::FILE*> __debug_fd = stderr;
503 std::string __debug_prefix = "rsocket-debug";
504
505 // --------------------------------------------------------------------------
506 // Dynamic buffer expansion for extra-long lines resolves problems with the
507 // default of 4,096 bytes for the read-ahead policy in Linux sockets, so some
508 // automatic expansion is handled by the randolf::rring class (which was
509 // originally created to simplify most of coding for the ring buffer in this
510 // randolf::rsocket class).
511 // --------------------------------------------------------------------------
512 #define RSOCKET_BUFFER_SIZE 8192 // Hard-coded default buffer size
513 size_t __buffer_size = RSOCKET_BUFFER_SIZE; // Buffer size used by recv() when a buffer size isn't provided
514 rring* __buffer = nullptr; // Ring buffer; instantiated by calling recvline() or recv_rline()
515 rring_bam* __buffer_bam = nullptr; // Map of rring's memory blocks
516
517 // --------------------------------------------------------------------------
518 // EoL sequence variables.
519 // --------------------------------------------------------------------------
520 static const char __CR = (char)13; // CTRL-M / ASCII 13 (0Dh) / "\r" / Used by eol() methods
521 static const char __LF = (char)10; // CTRL-J / ASCII 10 (0Ah) / "\n" / Used by eol() methods
522 static constexpr const char* __CRLF = "\x0d\x0a"; // Used by eol() methods
523 std::string __eol; // Used by recvline() method; maintained by eol() methods
524 std::string __eol_consumed_seq; // The EoL sequence that was successfully received by the most recent use of the recvline() method
525 bool __eol_adoption = true; // Whether to adopt the dynamically detected EoL sequence when __eol is empty (the default)
526 std::string __eol_out = std::string(__CRLF); // Used by sendline() method; maintained by eol() methods; also used by sendline() method
527 bool __eol_fix_printf = true; // Whether to replace "\n" sequence in printf() methods to EoL sequence
528
529 // --------------------------------------------------------------------------
530 // Fine-tuning for __recvline(). This is used with nanosleep().
531 //
532 // To test, also use commands in canonical mode, like this:
533 // stty -icanon && openssl s_client host:port
534 // stty -icanon && netcat host port
535 // stty -icanon && telnet host port
536 // --------------------------------------------------------------------------
537 long __recvline_timeout = 0; // Number of seconds (0 = unlimited; maximum for "long" equates to approximately 292 billion years since the UNIX/Linux epoch)
538
539 // --------------------------------------------------------------------------
540 // Statistical variables.
541 // --------------------------------------------------------------------------
542 rsocket_io* __io_final_addr = nullptr; // Used by ~rsocket() destructor, net_io_final(), and net_io_update()
543 std::atomic_ulong __bytes_rx = 0; // Total number of bytes received
544 std::atomic_ulong __bytes_tx = 0; // Total number of bytes transmitted
545 std::atomic_ulong __crypt_rx = 0; // Total number of encrypted bytes recevied
546 std::atomic_ulong __crypt_tx = 0; // Total number of encrypted bytes transmitted
547
548 public:
549 /*======================================================================*//**
550 @brief
551 Optional flags used with rsocket's @ref recvline() and @ref recv_rline()
552 methods to specify relevant text-line reading policies/semantics.
553 @see recvline
554 @see recv_rline
555 *///=========================================================================
556 enum RECVLINE_FLAGS: int {
557
558 /*----------------------------------------------------------------------*//**
559 The RECVLINE_DEFAULT flag isn't necessary, but it's included here for
560 completeness as it accomodates programming styles that prefer to emphasize
561 when defaults are being relied upon.
562 *///-------------------------------------------------------------------------
563 RECVLINE_DEFAULT = 0,
564
565 /*----------------------------------------------------------------------*//**
566 The RECVLINE_NO_DISCARD_ON_OVERFLOW flag prevents data from being discarded
567 when an @ref randolf::rex::xEOVERFLOW exception is thrown (which is caused
568 when a line of text exceeds the specified maximum line length).
569
570 @note
571 The @c MSG_PEEK flag also prevents the automatic discarding of data,
572 regardless of whether the @ref randolf::rex::xEOVERFLOW is thrown, thus it
573 differs from this flag in multiple ways that vary with how and why it's used.
574
575 @warning
576 Data will need to be consumed in a different way, such as by attempting to
577 read into a larger buffer than what had been allocated, or by receiving
578 portions or discarding some or all that remains.
579
580 By default, when the @ref randolf::rex::xEOVERFLOW exception is thrown, all
581 pending data (up to and including the upcoming EoL sequence, or the EoS,
582 whichever comes first) is discarded.
583 @see discard_line()
584 *///-------------------------------------------------------------------------
585 RECVLINE_NO_DISCARD_ON_OVERFLOW = 1,
586
587 /*----------------------------------------------------------------------*//**
588 The RECVLINE_PARTIAL flag receives an incomplete line of text and returns it
589 instead of throwing the @ref randolf::rex::xEOVERFLOW exception, and causes
590 the @ref eol_consumed_seq() method to return an empty string (which indicates
591 that no EoL sequence was consumed).
592 @see eol_consumed_seq()
593 *///-------------------------------------------------------------------------
594 RECVLINE_PARTIAL = 2,
595
596 }; // -x- enum RECVLINE_FLAGS -x-
597
598 /*======================================================================*//**
599 @brief
600 Optional flags used with various methods to determine whether they will throw
601 an @ref randolf::rex::xETIMEDOUT exception or merely return a @c nullptr, an
602 empty set, or a 0 (zero) when a timeout duration elapses.
603
604 If you're not sure of whether to use @ref TIMEOUT_EMPTY, @ref TIMEOUT_NULL,
605 or @ref TIMEOUT_ZERO in your code, then I suggest using @ref TIMEOUT_NULL
606 since @c NULL will likely be the most recognizable in most code reviews.
607
608 @note
609 You'll know when this is an option because the method will support this.
610 *///=========================================================================
611 enum TIMEOUT_BEHAVIOUR: bool {
612
613 /*----------------------------------------------------------------------*//**
614 Indicate that an @ref randolf::rex::xETIMEDOUT exception should be thrown
615 when the timeout duration elapses.
616 *///-------------------------------------------------------------------------
617 TIMEOUT_EXCEPTION = true,
618
619 /*----------------------------------------------------------------------*//**
620 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
621 when the timeout duration elapses.
622 *///-------------------------------------------------------------------------
623 TIMEOUT_EMPTY = false,
624
625 /*----------------------------------------------------------------------*//**
626 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
627 when the timeout duration elapses.
628 *///-------------------------------------------------------------------------
629 TIMEOUT_NULL = false,
630
631 /*----------------------------------------------------------------------*//**
632 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
633 when the timeout duration elapses.
634 *///-------------------------------------------------------------------------
635 TIMEOUT_ZERO = false,
636
637 }; // -x- enum TIMEOUT_BEHAVIOUR -x-
638
639 /*======================================================================*//**
640 @brief
641 Optional flags used with rsocket's @ref tls() and @ref tls_ctx() methods to
642 specify relevant policies/semantics.
643 *///=========================================================================
644 enum TLS_FLAGS: int {
645
646 /*----------------------------------------------------------------------*//**
647 The TLS_DEFAULT flag isn't necessary, but it's included here for completeness
648 as it accomodates programming styles that prefer to emphasize when defaults
649 are being relied upon.
650 *///-------------------------------------------------------------------------
651 TLS_DEFAULT = 0,
652
653 /*----------------------------------------------------------------------*//**
654 Only encrypted connections are permitted, initially, and all attempts to
655 begin with unencrypted connections will consistently fail.
656
657 Encrypted connections must begin with a cryptographic handshake packet, or
658 else the connection will be rejected as due to being reasonably assumed to be
659 "not encrypted."
660 @post
661 If this flag isn't set, an application-level mechanism can be used to upgrade
662 to TLS encryption (this is common with connections that begin unencrypted,
663 and issue a proprietary command {such as the @c STARTTLS command in SMTP} to
664 upgrade to TLS later on {of which the @ref tls() method is used to affect an
665 ingress; see the @ref TLS_NO_INGRESS flag for additional information}).
666 @note
667 Creating an exclusively @c unencrypted connection is accomplished by not
668 initializing TLS.
669 @see tls()
670 @see TLS_NO_EGRESS to prevent support for application-level downgrades to
671 unencrypted connections
672 *///-------------------------------------------------------------------------
673 TLS_EXCLUSIVE = 1,
674
675 /*----------------------------------------------------------------------*//**
676 Mid-stream upgrades to encrypted connections are not permitted (e.g., via
677 application-level initiations like the @c STARTTLS command as seen in SMTP),
678 which will also cause calls to the @ref tls() method to fail fast after plain
679 non-encrypted data has already been sent or received.
680 @pre
681 This flag is not necessary when the @ref TLS_EXCLUSIVE flag is set.
682 @see is_tls_ingress_okay()
683 @see tls_do_handshake()
684 @see TLS_NO_EGRESS
685 *///-------------------------------------------------------------------------
686 TLS_NO_INGRESS = 2,
687
688 /*----------------------------------------------------------------------*//**
689 Mid-stream downgrades to unencrypted connections are not permitted (e.g., via
690 application-level initiations like a hypothetical @c STOPTLS command as seen
691 in FTPS), which will also cause calls to the @ref tls() method to fail fast
692 after encrypted data has already been sent or received.
693 @pre
694 This flag may be combined with the @ref TLS_EXCLUSIVE flag to prevent a
695 connection from being downgraded programatically.
696 @note
697 Although egress to an unencrypted connection doesn't occur automatically
698 (since egress can only be affected programatically to support commands at the
699 application level), this flag is useful to prevent third-party code from
700 downgrading an encrypted @ref rsocket to unencrypted.
701 @warning
702 Supporting unencrypted communications is strongly discouraged over public
703 networks (e.g., the internet) because unencrypted streams are trivially
704 susceptible to man-in-the-middle attacks that can alter the contents of the
705 data in both directions (which is a particularly dangerous prospect for
706 sending/receiving sensitive information).
707 @see is_tls_egress_okay()
708 @see TLS_NO_INGRESS
709 *///-------------------------------------------------------------------------
710 TLS_NO_EGRESS = 4,
711
712 /*----------------------------------------------------------------------*//**
713 This is a convenience flag that provides an option for developers to be more
714 clear in their use of the @ref tls() and @ref tls_ctx() methods to indicate
715 intent to rely on what is already the default.
716 @see tls_do_handshake()
717 @see TLS_SERVER
718 *///-------------------------------------------------------------------------
719 TLS_CLIENT = 0,
720
721 /*----------------------------------------------------------------------*//**
722 Indicates that this rsocket will be for a server daemon, and to initialize a
723 new TLS context (when one isn't being provided) using OpenSSL's
724 @c TLS_server_method() function instead of OpenSSL's @c TLS_client_method()
725 function (the latter is the default because most code is anticipated to be
726 client-oriented).
727
728 @attention
729 Setting the CLIENT/SERVER flag incorrectly will typically cause the following
730 error that's difficult to track down, which is usually triggered by calling
731 @ref accept, @ref accept4(), or @ref connect(), and so I hope that including
732 this error message here in this documentation will be helpful:
733 @verbatim
734 error:140C5042:SSL routines:ssl_undefined_function:called a function you should not call
735 @endverbatim
736
737 The absence of this flag has the same effect as specifying the @ref
738 TLS_CLIENT flag.
739 @see TLS_CLIENT
740 *///-------------------------------------------------------------------------
741 TLS_SERVER = 8,
742
743 }; // -x- enum TLS_FLAGS -x-
744
745 private:
746 /*======================================================================*//**
747 Return Code check, and throws an rsocket-specific exception if an error
748 occurred, which is indicated by @c -1 (a lot of example code shows @c < @c 1
749 comparisons, but we specifically test for @c -1 because the documentation
750 clearly states @c -1. This is important because a system that can support an
751 extremely high number of socket handles might, in theory, assign handles with
752 values that get interpreted as negative integers, and so less-than-zero tests
753 would result in dropped packets or dropped sockets (any such socket code that
754 allocates such handles obviously must not ever allocate @c -1 since this
755 would definitely be misinterpreted as an error).
756
757 If rc is not @c -1, then it is simply returned as is.
758
759 If n is nonzero and rc is 0, then n will override errno. (This option is
760 provided to accomodate the few socket library functions that return values
761 that are never errors, and expect the developer to rely on other means of
762 detecting whether an error occurred. This is an example of where Object
763 Oriented Programming is helpful in making things better.)
764 @returns Original value of @c rc (if no exceptions were thrown)
765 *///=========================================================================
766 const int __rc_check(
767 /// Return code
768 const int rc,
769 /// Override @c errno (if not 0)
770 const int n = 0,
771 /// Exception handling flags (see @ref rex::REX_FLAGS for details)
772 const int flags = rex::rex::REX_FLAGS::REX_DEFAULT) {
773 if (rc == -1 || (rc == 0 && n != 0)) {
774 const int socket_errno = n == 0 ? errno : n; // If "n" is not zero, use it instead
775 if (__debug) debug("__rc_check(" + std::to_string(rc) + ", " + std::to_string(n) + ", " + std::to_string(flags) + ") throwing exception (errno=" + std::to_string(socket_errno) + ")"); // TODO: Remove this
776 randolf::rex::mk_exception("", socket_errno, flags); // This function doesn't return (this code ends here)
777 } // -x- if rc -x-
778 return rc;
779 } // -x- int __rc_check -x-
780
781 /*======================================================================*//**
782 Similar to @ref __rc_check(), but specialized for handling OpenSSL return
783 codes.
784 *///=========================================================================
785 const int __rc_check_tls(
786 /// Return code (from OpenSSL's API functions)
787 const int rc) {
788 if (rc <= 0) {
789 if (__debug) debug("__rc_check_tls(" + std::to_string(rc) + ") throwing exception"); // TODO: Remove this
790 randolf::rex::mk_exception("", SSL_get_error(__tls_fd, rc), rex::rex::REX_FLAGS::REX_TLS); // This function doesn't return (this code ends here)
791 } // -x- if rc -x-
792 return rc;
793 } // -x- int __rc_check -x-
794
795 /*======================================================================*//**
796 Internal function that opens the socket. This is used by the constructors
797 and their accompanying socket() methods.
798
799 Throws randolf::rex::xEALREADY exception if the socket is already open to
800 prevent multiple calls to @c open, which can be particularly problematic if
801 any of the additional function calls specify different parameters -- this
802 helps developer(s) to avoid unexpected results if they're inadvertently using
803 the same rsocket object when they intend to use different rsocket objects.
804 *///=========================================================================
805 void __socket(
806 /// Communication domain; usually one of:@n
807 /// AF_INET (IPv4)@n
808 /// AF_INET6 (IPv6)@n
809 /// AF_UNIX (UNIX domain sockets)
810 const int family,
811 /// Communication semantics; usually one of:@n
812 /// SOCK_STREAM (common for TCP)@n
813 /// SOCK_DGRAM (common for UDP)
814 const int type,
815 /// Network protocol; usually one of:@n
816 /// IPPROTO_TCP@n
817 /// IPPROTO_UDP@n
818 /// IPPROTO_IP
819 const int protocol) {
820 if (__debug) debug("socket(" + std::to_string(family)
821 + ", " + std::to_string(type)
822 + ", " + std::to_string(protocol)
823 + ");");
824
825 // --------------------------------------------------------------------------
826 // Syntax checks.
827 // --------------------------------------------------------------------------
828 if (__socket_open) throw randolf::rex::xEALREADY("EALREADY: Socket is already open"); // Socket has already been opened
829 if (family == AF_UNSPEC) throw randolf::rex::xEAFNOSUPPORT("EAFNOSUPPORT: AF_UNSPEC family (SO_DOMAIN) is not supported by socket()");
830
831 // --------------------------------------------------------------------------
832 // Build minimum parts of __socket_addr structure.
833 // --------------------------------------------------------------------------
834 __socket_addr->ss_family = family;
835 __socket_type = type;
836 __socket_protocol = protocol;
837
838 // --------------------------------------------------------------------------
839 // Create new socket handle, and save it, then set internal variable to
840 // indicate that the socket is open.
841 // --------------------------------------------------------------------------
842 __socket_fd = __rc_check(::socket(__socket_addr->ss_family, __socket_type, __socket_protocol));
843 __socket_open = true;
844
845 } // -x- void __socket -x-
846
847 public:
848 /*======================================================================*//**
849 @brief
850 Instantiate an empty rsocket without actually opening a socket, and therefore
851 also without throwing any exceptions (useful in header-file definitions).
852
853 @details
854 Instantiating an empty rsocket is particularly useful for header-file
855 definitions since exceptions can't be handled outside of subroutines, and
856 it's also useful for enabling debug() mode @em before setting the socket's
857 configuration with one of the socket() methods; for example:
858 @code{.cpp}
859 randolf::rsocket r; // Empty rsocket (empty / incomplete initialization)
860 r.debug(true); // Enable debug mode
861 r.socket(...); // Required to complete rsocket initialization
862 @endcode
863
864 @par Notes
865
866 The built-in defaults, when not provided, are as follows ("family" is also
867 known as the "communication domain"):
868 - @c family = AF_INET
869 - @c type = SOCK_STREAM
870 - @c protocol = PF_UNSPEC
871
872 You will need to use one of the socket(...) methods to specify socket details
873 after defining rsocket objects with empty constructors so that you can catch
874 runtime exceptions. (This also provides you with an option to enable debug
875 mode during runtime prior to attempting to open an rsocket.)
876
877 @par Examples
878
879 @code{.cpp}
880 #include <iostream> // std::cout, std::cerr, std::endl, etc.
881 #include <randolf/rex>
882 #include <randolf/rsocket>
883
884 randolf::rsocket r; // <-- Empty rsocket initialization (no exceptions)
885
886 int main(int argc, char *argv[]) {
887 try {
888 r.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Required to complete rsocket initialization
889 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
890 // ... other socket I/O operations
891 r.close();
892 } catch (const randolf::rex::xALL e) {
893 std::cerr << "Socket exception: " << e.what() << std::endl;
894 return EXIT_FAILURE;
895 } catch (const std::exception e) {
896 std::cerr << "Other exception: " << e.what() << std::endl;
897 return EXIT_FAILURE;
898 }
899 return EXIT_SUCCESS;
900 } // -x- int main -x-
901 @endcode
902 @see rsocket()
903 @see socket()
904 @see socket_family()
905 @see socket_fd()
906 @see socket_protocol()
907 @see socket_type()
908 @qualifier TLS
909 *///=========================================================================
910 rsocket() noexcept {} // -x- constructor rsocket -x-
911
912 /*======================================================================*//**
913 @brief
914 Instantiate an rsocket based on a minimal subset of the settings in the
915 specified rsocket (using it as a template), without actually opening a
916 socket, and therefore also without throwing any exceptions.
917
918 @note
919 This constructor does not suffice as a full clone()-like operation, and is
920 minimal because it's used internally by the @ref accept() and @ref accept4()
921 methods.
922
923 Details that are absorbed from the template/source rsocket (which eliminates
924 the need to assign, set, and configure various parameters (TLS and TLS SNI
925 parameters will be copied in a passive way by default):
926 - Socket family (SO_DOMAIN)
927 - Socket type (SO_TYPE)
928 - Socket protocol (SO_PROTOCOL)
929 - TLS details (status, context {which includes loaded certificates},
930 policies specified with @ref TLS_FLAGS, etc.)
931 - EoL details
932 - TLS context (you'll still need to call `tls_ctx(r->tls_ctx())`)
933 - TLS SNI map (you'll still need to call `tls_sni(r->tls_sni())`)
934
935 @post
936 The TLS Context will not be initialized because it needs a real socket to
937 draw from. If using TLS, you'll need to use the @ref tls() method after
938 the underlying socket has been initiated.
939
940 When @c flag_create_socket is set to FALSE, no exceptions will be thrown.
941
942 @see rsocket()
943 @see socket()
944 @see socket_family()
945 @see socket_fd()
946 @see socket_protocol()
947 @see socket_type()
948 @qualifier TLS
949 *///=========================================================================
950 rsocket(
951 /// Source rsocket object to use as a template to absorb settings from
952 const rsocket* rtemplate,
953 /// TRUE = create a new socket handle (default)@n
954 /// FALSE = don't create a new socket because a new one will be assigned or
955 /// created later (all variants of the @ref accept() methods do this)
956 const bool flag_create_socket = true) {
957
958 // --------------------------------------------------------------------------
959 // General socket variables.
960 // --------------------------------------------------------------------------
961 __rtemplate = rtemplate; // Used by SNI callback (TODO)
962 if (flag_create_socket) {
963 __socket(rtemplate->__socket_addr->ss_family,
964 rtemplate->__socket_type,
965 rtemplate->__socket_protocol);
966 } else { // !flag_create_socket
967 __socket_addr->ss_family = rtemplate->__socket_addr->ss_family;
968 __socket_type = rtemplate->__socket_type;
969 __socket_protocol = rtemplate->__socket_protocol;
970 } // -x- if flag_create_socket -x-
971 __name = rtemplate->__name;
972
973 // --------------------------------------------------------------------------
974 // TLS and SNI settings, but not whether TLS is enabled or other settings
975 // since this is at a pre-handshake stage since this constructor is mostly
976 // used by the accept() and accept4() methods before accepting a connection.
977 // --------------------------------------------------------------------------
978 __tls_ctx = rtemplate->__tls_ctx;
979 __tls_sni = rtemplate->__tls_sni;
980
981 // --------------------------------------------------------------------------
982 // EoL variables.
983 // --------------------------------------------------------------------------
984 __eol.assign( rtemplate->__eol); // Copy source std::string's contents (not a reference)
985 __eol_fix_printf = rtemplate->__eol_fix_printf;
986
987 } // -x- constructor rsocket -x-
988
989 /*======================================================================*//**
990 @brief
991 Instantiate an rsocket with IP/host address and [optional] port number.
992
993 This is either the endpoint that our underlying socket will be connecting to,
994 or it's the local address of the server daemon that our socket will listen()
995 to and accept() inbound connections from.
996
997 @par Notes
998
999 The built-in defaults, when not provided, are as follows ("family" is also
1000 known as the "communication domain"):
1001 - @c family = AF_INET
1002 - @c type = SOCK_STREAM
1003 - @c protocol = PF_UNSPEC
1004
1005 The socket() methods do the same work as the constructors with matching
1006 arguments, and are provided as convenience methods intended to augment
1007 empty rsocket constructors used in header files, but do require an address to
1008 be specified (for protocols that need port numbers, such as TCP or UDP, a
1009 "port" number also needs to be specified since the default port 0 will result
1010 in the dynamic allocation of a port number by the system).
1011
1012 For UNIX domain sockets use family AF_UNIX, type SOCK_STREAM, and protocol
1013 IPPROTO_IP when instantiating or opening an rsocket.
1014
1015 @par Examples
1016
1017 @code{.cpp}
1018 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1019 #include <randolf/rex>
1020 #include <randolf/rsocket>
1021
1022 int main(int argc, char *argv[]) {
1023 try {
1024 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Same as socket() method
1025 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
1026 r.bind("127.0.0.1", 32768);
1027 // ... other socket I/O operations
1028 r.close();
1029 } catch (const randolf::rex::xALL e) {
1030 std::cerr << "Socket exception: " << e.what() << std::endl;
1031 return EXIT_FAILURE;
1032 } catch (const std::exception e) {
1033 std::cerr << "Other exception: " << e.what() << std::endl;
1034 return EXIT_FAILURE;
1035 }
1036 return EXIT_SUCCESS;
1037 } // -x- int main -x-
1038 @endcode
1039
1040 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
1041 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
1042 @throws randolf::rex::xEINVAL Protocal family invalid or not available
1043 @throws randolf::rex::xEINVAL Invalid flags in type
1044 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1045 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1046 @throws randolf::rex::xENOBUFS Insufficient memory
1047 @throws randolf::rex::xENOMEM Insufficient memory
1048 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1049 supported within the specified family (a.k.a., communication domain)
1050 @see rsocket()
1051 @see socket()
1052 @qualifier TLS
1053 *///=========================================================================
1054 rsocket(
1055 /// Communication domain; usually one of:@n
1056 /// AF_INET (IPv4)@n
1057 /// AF_INET6 (IPv6)@n
1058 /// AF_UNIX (UNIX domain sockets)
1059 const int family,
1060 /// Communication semantics; usually one of:@n
1061 /// SOCK_STREAM (common for TCP)@n
1062 /// SOCK_DGRAM (common for UDP)
1063 const int type = SOCK_STREAM,
1064 /// Network protocol; usually one of:@n
1065 /// IPPROTO_TCP@n
1066 /// IPPROTO_UDP@n
1067 /// IPPROTO_IP@n
1068 /// PF_UNSPEC (auto-detect)
1069 const int protocol = PF_UNSPEC) {
1070 __socket(family, type, protocol);
1071 } // -x- constructor rsocket -x-
1072
1073 /*======================================================================*//**
1074 @brief
1075 Destructor, which closes any underlying sockets, frees any TLS structures
1076 that were allocated by OpenSSL, and performs any other necessary clean-up
1077 before finally copying the I/O statistics to a designated structure (if one
1078 was specified with the @ref net_io_final() method).
1079 @attention
1080 Developers should take care to check that the @ref rsocket_io::is_final flag
1081 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
1082 since there's no guarantee that the destructor will necessarily be executed
1083 in a timely manner (this flag is set last, after all other statistics are
1084 copied into the structure while in a mutex-locked state).
1085 @see net_io_final()
1086 @qualifier TLS
1087 *///=========================================================================
1088 ~rsocket() noexcept {
1089
1090 // --------------------------------------------------------------------------
1091 // Debug.
1092 // --------------------------------------------------------------------------
1093 if (__debug) {
1094 debug("destructor(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1095 + ");");
1096 debug(false); // Disable debug mode (might trigger extra clean-up in future)
1097 } // -x- if _debug -x-
1098
1099 // --------------------------------------------------------------------------
1100 // Free memory and resources and close handles that need to be freed/closed.
1101 // --------------------------------------------------------------------------
1102 if (__tls_sni != nullptr) tls_sni(nullptr); // Remove any SNI callbacks // WE DON'T NEED THIS
1103 if (__tls_fd != nullptr) SSL_free(__tls_fd); // OpenSSL resource clean-up
1104 //if (__tls_ctx != nullptr) SSL_CTX_free(__tls_ctx); // This causes high CPU on non-encrypted connections
1105 if (__socket_fd != 0) ::close(__socket_fd); // POSIX close() is ensured with ::close()
1106 if (__buffer_bam != nullptr) ::free( __buffer_bam); // Release the rring_bam structure (C)
1107 if (__buffer != nullptr) delete __buffer; // Release the rring buffer (C++)
1108 if (__socket_addr != nullptr) ::free(__socket_addr);
1109 if (__rtemplate != nullptr) __rtemplate = nullptr;
1110
1111 // --------------------------------------------------------------------------
1112 // Copy statistics to final location. (We do this last in case there's an
1113 // issue with locking; there shouldn't be, but if there is then at least
1114 // we're not inadvertently hanging on to any other resources at this point
1115 // that need to be closed and freed.)
1116 // --------------------------------------------------------------------------
1117 if (__io_final_addr != nullptr) {
1118 __io_final_addr->lock();
1119 __io_final_addr->bytes_rx = __bytes_rx;
1120 __io_final_addr->bytes_tx = __bytes_tx;
1121 // ->bytes_xx = <bytes in buffer; these are lost bytes that should be subtracted from __bytes_rx>
1122 __io_final_addr->crypt_rx = __crypt_rx;
1123 __io_final_addr->crypt_tx = __crypt_tx;
1124 // ->crypt_xx = <bytes in buffer; these are lost bytes that should be subtracted from __crypt_rx>
1125 __io_final_addr->is_final = true;
1126 __io_final_addr->unlock();
1127 } // -x- if __io_final_addr -x-
1128
1129 } // -x- destructor ~rsocket -x-
1130
1131 /*======================================================================*//**
1132 @brief
1133 Accept new [inbound] socket connections. (This is typically used in a loop.)
1134
1135 @pre
1136 The resulting rsocket object is created before the actual call to the @c
1137 accept() function.
1138
1139 For faster initial connections, disabling the Nagle Algorithm (which was
1140 originally introduced to the Standards Track in 1984 by John Nagle as a
1141 solution that actually solved a lot of network congestion problems by adding
1142 a 200ms internal timeout in TCP packet assembly that made it possible to
1143 reduce the total quantity of packets being transmitted by combining multiple
1144 small portions of data, within this 200ms timeframe, into a single packet
1145 instead of sending multiple packets in rapid succession) will likely improve
1146 responsiveness from a direct end-user experience perspective. To disable the
1147 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1148 connections:
1149 @code{.cpp}
1150 #include <netinet/tcp.h> // TCP_NODELAY
1151 // ...
1152 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1153 randolf::rsocket* c = r->accept();
1154 @endcode
1155 This particular socket option will also be carried through automatically by
1156 the network stack and therefore also be set on the resulting client socket,
1157 so it's more efficient to set it this way.
1158
1159 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1160 socket option on the resulting client socket, although this takes more work
1161 despite being used only where it's needed:
1162 @code{.cpp}
1163 #include <netinet/tcp.h> // TCP_CORK
1164 // ...
1165 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1166 c->sendline("Welcome");
1167 c->sendline("=======");
1168 c->send("Login name: ");
1169 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1170 @endcode
1171 Upon the end of the packet-assembly phase, which is communicated to the
1172 network stack by setting the @c TCP_CORK socket potion to @c false, the
1173 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1174 which effectively serves as a temporary bypass of the Nagle Algorithm
1175 (this is probably better overall for optimizing internet traffic for the
1176 internet as a whole if your application doesn't need the Nagle Algorithm
1177 disabled for everything).
1178
1179 @note
1180 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1181 select(), and accept()/accept4() when using multiple sockets.
1182
1183 @post
1184 To prevent resource leaks, the resulting rsocket needs to be deleted after
1185 it's no longer needed.
1186
1187 @throws randolf::rex::xEBADF The underlying socket is not open
1188 @throws randolf::rex::xECONNABORTED The connection was aborted
1189 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1190 part of the user address space
1191 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1192 @throws randolf::rex::xEHOSTUNREACH No route to host
1193 @throws randolf::rex::xEINTR Interrupted by a signal
1194 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1195 length of the address is invalid
1196 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1197 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1198 @throws randolf::rex::xENETUNREACH No route to network
1199 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1200 @throws randolf::rex::xENOBUFS Insufficient memory
1201 @throws randolf::rex::xENOMEM Insufficient memory
1202 @throws randolf::rex::xENONET Requested host is not reachable on the network
1203 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1204 is not supported
1205 @throws randolf::rex::xENOSR System ran out of stream resources
1206 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1207 doesn't refer to a socket
1208 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1209 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1210 @throws randolf::rex::xEPROTO Protocol error
1211 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1212 supported within the specified family (a.k.a., communication domain)
1213 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1214 a system call is restartable and can be intercepted-and-redirected
1215 (there is no need to catch this exception unless you are an advanced
1216 developer, in which case you likely still won't need to catch it in
1217 code at this level)
1218 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1219 supported within the specified family (a.k.a., communication domain)
1220 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1221 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1222 no inbound connections are waiting
1223
1224 @returns Newly-created rsocket object representing the connection received
1225 @see accept_sp()
1226 @see accept4()
1227 @see accept4_sp()
1228 @see listen
1229 @see setsockopt(int, int, int)
1230 @qualifier POSIX
1231 @qualifier TLS
1232 *///=========================================================================
1233 rsocket* accept() {
1234 if (__debug) debug("accept(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1235 + ");");
1236
1237 // --------------------------------------------------------------------------
1238 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1239 // new incoming connection.
1240 // --------------------------------------------------------------------------
1241 rsocket* r = new rsocket(this, false);
1242
1243 // --------------------------------------------------------------------------
1244 // Wait for incoming connection, and update internal flags in client rsocket.
1245 // --------------------------------------------------------------------------
1246 r->__socket_fd = ::accept(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size);
1247 if (r->__socket_fd == -1) {
1248 delete r; // Memory management
1249 __rc_check(-1); // This part throws the exception
1250 } // -x- if rc -x-
1251 r->__socket_open = true;
1252 r->__socket_connected = true;
1253
1254 // --------------------------------------------------------------------------
1255 // Perform TLS accept() as well, but only if TLS is enabled.
1256 //
1257 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1258 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1259 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1260 // --------------------------------------------------------------------------
1261 if (__tls) {
1262 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1263 r->tls(true); // Make sure __tls_fd is configured correctly
1264 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1265 __rc_check_tls(SSL_accept(r->__tls_fd));
1266 } // -x- if __tls -x-
1267
1268 return r;
1269 } // -x- rsocket* accept -x-
1270
1271 /*======================================================================*//**
1272 @brief
1273 Accept new [inbound] socket connections. (This is typically used in a loop.)
1274
1275 @pre
1276 The resulting rsocket object is created before the actual call to the @c
1277 accept() function.
1278
1279 For faster initial connections, disabling the Nagle Algorithm (which was
1280 originally introduced to the Standards Track in 1984 by John Nagle as a
1281 solution that actually solved a lot of network congestion problems by adding
1282 a 200ms internal timeout in TCP packet assembly that made it possible to
1283 reduce the total quantity of packets being transmitted by combining multiple
1284 small portions of data, within this 200ms timeframe, into a single packet
1285 instead of sending multiple packets in rapid succession) will likely improve
1286 responsiveness from a direct end-user experience perspective. To disable the
1287 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1288 connections:
1289 @code{.cpp}
1290 #include <netinet/tcp.h> // TCP_NODELAY
1291 // ...
1292 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1293 std::shared_ptr<randolf::rsocket> c = r->accept_sp();
1294 @endcode
1295 This particular socket option will also be carried through automatically by
1296 the network stack and therefore also be set on the resulting client socket,
1297 so it's more efficient to set it this way.
1298
1299 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1300 socket option on the resulting client socket, although this takes more work
1301 despite being used only where it's needed:
1302 @code{.cpp}
1303 #include <netinet/tcp.h> // TCP_CORK
1304 // ...
1305 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1306 c->sendline("Welcome");
1307 c->sendline("=======");
1308 c->send("Login name: ");
1309 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1310 @endcode
1311 Upon the end of the packet-assembly phase, which is communicated to the
1312 network stack by setting the @c TCP_CORK socket potion to @c false, the
1313 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1314 which effectively serves as a temporary bypass of the Nagle Algorithm
1315 (this is probably better overall for optimizing internet traffic for the
1316 internet as a whole if your application doesn't need the Nagle Algorithm
1317 disabled for everything).
1318
1319 @note
1320 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1321 select(), and accept()/accept4() when using multiple sockets.
1322
1323 @post
1324 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1325 aids in the prevention of resource leaks).
1326
1327 @throws randolf::rex::xEBADF The underlying socket is not open
1328 @throws randolf::rex::xECONNABORTED The connection was aborted
1329 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1330 part of the user address space
1331 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1332 @throws randolf::rex::xEHOSTUNREACH No route to host
1333 @throws randolf::rex::xEINTR Interrupted by a signal
1334 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1335 length of the address is invalid
1336 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1337 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1338 @throws randolf::rex::xENETUNREACH No route to network
1339 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1340 @throws randolf::rex::xENOBUFS Insufficient memory
1341 @throws randolf::rex::xENOMEM Insufficient memory
1342 @throws randolf::rex::xENONET Requested host is not reachable on the network
1343 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1344 is not supported
1345 @throws randolf::rex::xENOSR System ran out of stream resources
1346 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1347 doesn't refer to a socket
1348 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1349 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1350 @throws randolf::rex::xEPROTO Protocol error
1351 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1352 supported within the specified family (a.k.a., communication domain)
1353 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1354 a system call is restartable and can be intercepted-and-redirected
1355 (there is no need to catch this exception unless you are an advanced
1356 developer, in which case you likely still won't need to catch it in
1357 code at this level)
1358 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1359 supported within the specified family (a.k.a., communication domain)
1360 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1361 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1362 no inbound connections are waiting
1363
1364 @returns Newly-created rsocket object representing the connection received,
1365 wrapped in std::shared_ptr (a C++ smart pointer)
1366 @see accept()
1367 @see accept4()
1368 @see accept4_sp()
1369 @see listen
1370 @see setsockopt(int, int, int)
1371 @qualifier TLS
1372 *///=========================================================================
1373 std::shared_ptr<rsocket> accept_sp() {
1374 if (__debug) debug("accept_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1375 + ");");
1376
1377 // --------------------------------------------------------------------------
1378 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1379 // new incoming connection.
1380 // --------------------------------------------------------------------------
1381 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1382
1383 // --------------------------------------------------------------------------
1384 // Wait for incoming connection, and update internal flags in client rsocket.
1385 // --------------------------------------------------------------------------
1386 r->__socket_fd = __rc_check(::accept(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size));
1387 r->__socket_open = true;
1388 r->__socket_connected = true;
1389
1390 // --------------------------------------------------------------------------
1391 // Perform TLS accept() as well, but only if TLS is enabled.
1392 //
1393 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1394 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1395 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1396 // --------------------------------------------------------------------------
1397 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1398 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1399 r->tls(true); // Make sure __tls_fd is configured correctly
1400 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1401 __rc_check_tls(SSL_accept(r->__tls_fd));
1402 } // -x- if __tls -x-
1403
1404 return r;
1405 } // -x- std::shared_ptr<rsocket> accept_sp -x-
1406
1407 /*======================================================================*//**
1408 @brief
1409 Accept new [inbound] socket connections, with socket flags specified. (This
1410 is typically used in a loop.)
1411
1412 @pre
1413 The resulting rsocket object is created before the actual call to the @c
1414 accept4() function.
1415
1416 For faster initial connections, disabling the Nagle Algorithm (which was
1417 originally introduced to the Standards Track in 1984 by John Nagle as a
1418 solution that actually solved a lot of network congestion problems by adding
1419 a 200ms internal timeout in TCP packet assembly that made it possible to
1420 reduce the total quantity of packets being transmitted by combining multiple
1421 small portions of data, within this 200ms timeframe, into a single packet
1422 instead of sending multiple packets in rapid succession) will likely improve
1423 responsiveness from a direct end-user experience perspective. To disable the
1424 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1425 connections:
1426 @code{.cpp}
1427 #include <netinet/tcp.h> // TCP_NODELAY
1428 // ...
1429 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1430 randolf::rsocket* c = r->accept4();
1431 @endcode
1432 This particular socket option will also be carried through automatically by
1433 the network stack and therefore also be set on the resulting client socket,
1434 so it's more efficient to set it this way.
1435
1436 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1437 socket option on the resulting client socket, although this takes more work
1438 despite being used only where it's needed:
1439 @code{.cpp}
1440 #include <netinet/tcp.h> // TCP_CORK
1441 // ...
1442 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1443 c->sendline("Welcome");
1444 c->sendline("=======");
1445 c->send("Login name: ");
1446 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1447 @endcode
1448 Upon the end of the packet-assembly phase, which is communicated to the
1449 network stack by setting the @c TCP_CORK socket potion to @c false, the
1450 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1451 which effectively serves as a temporary bypass of the Nagle Algorithm
1452 (this is probably better overall for optimizing internet traffic for the
1453 internet as a whole if your application doesn't need the Nagle Algorithm
1454 disabled for everything).
1455
1456 @note
1457 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1458 select(), and accept()/accept4() when using multiple sockets.
1459
1460 @post
1461 To prevent resource leaks, the resulting rsocket needs to be deleted after
1462 it's no longer needed.
1463
1464 @throws randolf::rex::xEBADF The underlying socket is not open
1465 @throws randolf::rex::xECONNABORTED The connection was aborted
1466 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1467 part of the user address space
1468 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1469 @throws randolf::rex::xEHOSTUNREACH No route to host
1470 @throws randolf::rex::xEINTR Interrupted by a signal
1471 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1472 length of the address is invalid
1473 @throws randolf::rex::xEINVAL Invalid value in flags
1474 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1475 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1476 @throws randolf::rex::xENETUNREACH No route to network
1477 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1478 @throws randolf::rex::xENOBUFS Insufficient memory
1479 @throws randolf::rex::xENOMEM Insufficient memory
1480 @throws randolf::rex::xENONET Requested host is not reachable on the network
1481 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1482 is not supported
1483 @throws randolf::rex::xENOSR System ran out of stream resources
1484 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1485 doesn't refer to a socket
1486 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1487 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1488 @throws randolf::rex::xEPROTO Protocol error
1489 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1490 supported within the specified family (a.k.a., communication domain)
1491 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1492 a system call is restartable and can be intercepted-and-redirected
1493 (there is no need to catch this exception unless you are an advanced
1494 developer, in which case you likely still won't need to catch it in
1495 code at this level)
1496 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1497 supported within the specified family (a.k.a., communication domain)
1498 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1499 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1500 no inbound connections are waiting
1501
1502 @returns Newly-created rsocket object representing the connection received
1503 @see accept()
1504 @see accept_sp()
1505 @see accept4_sp()
1506 @see listen
1507 @see setsockopt(int, int, int)
1508 @qualifier POSIX
1509 @qualifier TLS
1510 *///=========================================================================
1511 rsocket* accept4(
1512 /// SOCK_NONBLOCK@n
1513 /// SOCK_CLOEXEC
1514 const int posix_flags = 0) {
1515 if (__debug) debug("accept4(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1516 + ", " + std::to_string(posix_flags)
1517 + ");");
1518
1519 // --------------------------------------------------------------------------
1520 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1521 // new incoming connection.
1522 // --------------------------------------------------------------------------
1523 rsocket* r = new rsocket(this, false);
1524
1525 // --------------------------------------------------------------------------
1526 // Wait for incoming connection, and update internal flags in client rsocket.
1527 // --------------------------------------------------------------------------
1528 r->__socket_fd = ::accept4(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size, posix_flags);
1529 if (r->__socket_fd == -1) {
1530 delete r; // Memory management
1531 __rc_check(-1); // This part throws the exception
1532 } // -x- if rc -x-
1533 r->__socket_open = true;
1534 r->__socket_connected = true;
1535
1536 // --------------------------------------------------------------------------
1537 // Perform TLS accept() as well, but only if TLS is enabled.
1538 //
1539 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1540 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1541 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1542 // --------------------------------------------------------------------------
1543 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1544 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1545 r->tls(true); // Make sure __tls_fd is configured correctly
1546 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1547 __rc_check_tls(SSL_accept(r->__tls_fd));
1548 } // -x- if __tls -x-
1549
1550 return r;
1551 } // -x- rsocket* accept4 -x-
1552
1553 /*======================================================================*//**
1554 @brief
1555 Accept new [inbound] socket connections, with socket flags specified. (This
1556 is typically used in a loop.)
1557
1558 @pre
1559 The resulting rsocket object is created before the actual call to the @c
1560 accept4() function.
1561
1562 For faster initial connections, disabling the Nagle Algorithm (which was
1563 originally introduced to the Standards Track in 1984 by John Nagle as a
1564 solution that actually solved a lot of network congestion problems by adding
1565 a 200ms internal timeout in TCP packet assembly that made it possible to
1566 reduce the total quantity of packets being transmitted by combining multiple
1567 small portions of data, within this 200ms timeframe, into a single packet
1568 instead of sending multiple packets in rapid succession) will likely improve
1569 responsiveness from a direct end-user experience perspective. To disable the
1570 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1571 connections:
1572 @code{.cpp}
1573 #include <netinet/tcp.h> // TCP_NODELAY
1574 // ...
1575 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1576 std::shared_ptr<randolf::rsocket> c = r->accept4_sp();
1577 @endcode
1578 This particular socket option will also be carried through automatically by
1579 the network stack and therefore also be set on the resulting client socket,
1580 so it's more efficient to set it this way.
1581
1582 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1583 socket option on the resulting client socket, although this takes more work
1584 despite being used only where it's needed:
1585 @code{.cpp}
1586 #include <netinet/tcp.h> // TCP_CORK
1587 // ...
1588 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1589 c->sendline("Welcome");
1590 c->sendline("=======");
1591 c->send("Login name: ");
1592 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1593 @endcode
1594 Upon the end of the packet-assembly phase, which is communicated to the
1595 network stack by setting the @c TCP_CORK socket potion to @c false, the
1596 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1597 which effectively serves as a temporary bypass of the Nagle Algorithm
1598 (this is probably better overall for optimizing internet traffic for the
1599 internet as a whole if your application doesn't need the Nagle Algorithm
1600 disabled for everything).
1601
1602 @note
1603 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1604 select(), and accept()/accept4() when using multiple sockets.
1605
1606 @post
1607 The resulting rsocket is wrapped in std::shared_ptr (a C++ smart pointer that
1608 aids in the prevention of resource leaks).
1609
1610 @throws randolf::rex::xEBADF The underlying socket is not open
1611 @throws randolf::rex::xECONNABORTED The connection was aborted
1612 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1613 part of the user address space
1614 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1615 @throws randolf::rex::xEHOSTUNREACH No route to host
1616 @throws randolf::rex::xEINTR Interrupted by a signal
1617 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1618 length of the address is invalid
1619 @throws randolf::rex::xEINVAL Invalid value in flags
1620 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1621 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1622 @throws randolf::rex::xENETUNREACH No route to network
1623 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1624 @throws randolf::rex::xENOBUFS Insufficient memory
1625 @throws randolf::rex::xENOMEM Insufficient memory
1626 @throws randolf::rex::xENONET Requested host is not reachable on the network
1627 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1628 is not supported
1629 @throws randolf::rex::xENOSR System ran out of stream resources
1630 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1631 doesn't refer to a socket
1632 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1633 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1634 @throws randolf::rex::xEPROTO Protocol error
1635 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1636 supported within the specified family (a.k.a., communication domain)
1637 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1638 a system call is restartable and can be intercepted-and-redirected
1639 (there is no need to catch this exception unless you are an advanced
1640 developer, in which case you likely still won't need to catch it in
1641 code at this level)
1642 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1643 supported within the specified family (a.k.a., communication domain)
1644 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1645 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1646 no inbound connections are waiting
1647
1648 @returns Newly-created rsocket object representing the connection received,
1649 wrapped in std::shared_ptr (a C++ smart pointer)
1650 @see accept()
1651 @see accept_sp()
1652 @see accept4()
1653 @see listen
1654 @see setsockopt(int, int, int)
1655 @qualifier TLS
1656 *///=========================================================================
1657 std::shared_ptr<rsocket> accept4_sp(
1658 /// SOCK_NONBLOCK@n
1659 /// SOCK_CLOEXEC
1660 const int posix_flags = 0) {
1661 if (__debug) debug("accept4_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1662 + ", " + std::to_string(posix_flags)
1663 + ");");
1664
1665 // --------------------------------------------------------------------------
1666 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1667 // new incoming connection.
1668 // --------------------------------------------------------------------------
1669 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1670
1671 // --------------------------------------------------------------------------
1672 // Wait for incoming connection, and update internal flags in client rsocket.
1673 // --------------------------------------------------------------------------
1674 r->__socket_fd = __rc_check(::accept4(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size, posix_flags));
1675 r->__socket_open = true;
1676 r->__socket_connected = true;
1677
1678 // --------------------------------------------------------------------------
1679 // Perform TLS accept() as well, but only if TLS is enabled.
1680 //
1681 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1682 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1683 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1684 // --------------------------------------------------------------------------
1685 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1686 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress); // | __tls_server_mode);
1687 r->tls(true); // Make sure __tls_fd is configured correctly
1688 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1689 __rc_check_tls(SSL_accept(r->__tls_fd));
1690 } // -x- if __tls -x-
1691// if (__tls_ctx != nullptr) { r->tls(true); r->__tls = (bool)__tls; } // Make sure __tls_fd is allocated appropriately
1692
1693 return r;
1694 } // -x- std::shared_ptr<rsocket> accept4_sp -x-
1695
1696 /*======================================================================*//**
1697 @brief
1698 Override the default @ref listen backlog for this rsocket.
1699
1700 @note
1701 The default backlog is SOMAXCONN (4096 on Linux, and 128 on older systems).
1702
1703 @returns The same rsocket object so as to facilitate stacking
1704 @see listen
1705 @qualifier TLS
1706 *///=========================================================================
1707 rsocket* backlog(
1708 /// Backlog queue size (0 = use SOMAXCONN, which is the system default of
1709 /// 4096 on Linux, and 128 on older systems)
1710 int backlog = 0) noexcept {
1711 __socket_backlog = backlog == 0 ? SOMAXCONN : backlog;
1712 return this;
1713 } // -x- rsocket* backlog -x-
1714
1715 /*======================================================================*//**
1716 @brief
1717 Find out what this rsocket's default listen backlog is.
1718 @returns The default listen backlog
1719 @see listen
1720 @qualifier TLS
1721 *///=========================================================================
1722 int backlog() noexcept {
1723 return __socket_backlog;
1724 } // -x- int backlog -x-
1725
1726 /*======================================================================*//**
1727 @brief
1728 Bind this socket to the specified network address (and port number if the
1729 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1730 used for server deamons, but can also be used to control the address from
1731 which the local host will make an outbound connection via the @ref connect()
1732 method (this bound address is the address from which the endpoint will
1733 recieve your connection).
1734
1735 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
1736 (or 1023 on some Operating Systems that test only for below 1024) in
1737 the absence of elevated access or the absence of a capability flag
1738 having been set
1739 @throws randolf::rex::xEACCES If binding to an interface on systems that
1740 require elevated access for direct interface binding in absence of
1741 said elevated access
1742 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
1743 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
1744 address is not available on any local interface
1745 @throws randolf::rex::xEBADF The underlying socket is not open
1746 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1747 part of the user address space
1748 @throws randolf::rex::xEINVAL Socket is already bound
1749 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
1750 valid for this socket's family (a.k.a., communication domain)
1751 @throws randolf::rex::xENOMEM Insufficient memory
1752 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1753 doesn't refer to a socket
1754 @n@n
1755 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
1756 @throws randolf::rex::xEACCES Write permission or search permission denied
1757 on a component of the path prefix (@c AF_UNIX family)
1758 @throws randolf::rex::xELOOP Too many symbolic links encountered while
1759 resolving address (@c AF_UNIX family)
1760 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
1761 @throws randolf::rex::xENOENT One of the path's directory components doesn't
1762 exist (@c AF_UNIX family)
1763 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
1764 directory (@c AF_UNIX family)
1765 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
1766
1767 @returns The same rsocket object so as to facilitate stacking
1768 @see accept()
1769 @see accept_sp()
1770 @see accept4()
1771 @see accept4_sp()
1772 @see bind(std::string, int)
1773 @see connect
1774 @see listen
1775 @qualifier POSIX
1776 @qualifier TLS
1777 *///=========================================================================
1778 rsocket* bind(
1779 /// Socket address structure
1780 const struct sockaddr* addr,
1781 /// Length of socket structure
1782 const socklen_t addrlen = sizeof(sockaddr)) {
1783 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1784 + ", <sockaddr*>"
1785 + ", size=" + std::to_string(addrlen)
1786 + ");");
1787 if (addrlen > sizeof(sockaddr_storage)) randolf::rex::mk_exception("addlen is too large for bind()", EINVAL);
1788 __rc_check(::bind(__socket_fd, addr, addrlen));
1789 std::memcpy(__socket_addr, addr, addrlen); // No errors were returned by bind(), so now we'll copy addr to __socket_addr
1790 return this;
1791 } // -x- rsocket* bind -x-
1792
1793 //===========================================================================
1794 //
1795 // TODO: Document usage of IP_BIND_ADDRESS_NO_PORT to support connecting
1796 // from a specific IP address without losing ephemeral port selection.
1797 // Notes: https://idea.popcount.org/2014-04-03-bind-before-connect/
1798 //
1799 // TODO: Support AF_BLUETOOTH addresses
1800 // Reference:
1801 // https://man7.org/linux/man-pages/man7/address_families.7.html
1802 //
1803 // TODO: Support AF_IPX addresses
1804 // Reference:
1805 // https://man7.org/linux/man-pages/man7/address_families.7.html
1806 //
1807 // TODO: Support AF_APPLETALK addresses
1808 // Reference: https://man7.org/linux/man-pages/man7/ddp.7.html
1809 //
1810 // TODO: Support AF_PACKET addresses
1811 // Reference: https://man7.org/linux/man-pages/man7/packet.7.html
1812 //
1813 // TODO: Support AF_X25 addresses
1814 // Reference: https://man7.org/linux/man-pages/man7/x25.7.html
1815 //
1816 // TODO: Support AF_NETLINK addresses
1817 // Reference: https://man7.org/linux/man-pages/man7/netlink.7.html
1818 //
1819 /*======================================================================*//**
1820 @brief
1821 Bind this socket to the specified network address (and port number if the
1822 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
1823 used for server deamons, but can also be used to control the address from
1824 which the local host will make an outbound connection via the @ref connect()
1825 method (this bound address is address from which the endpoint will recieve
1826 your connection).
1827
1828 In addition to the standard IPv4 (AF_INET family) and IPv6 (AF_INET6 family)
1829 support, rsocket also supports a few other binding options in a manner that
1830 makes it easy to utilize, without having to handle special implementation
1831 details (because they're handled behind-the-scenese by this rsocket class).
1832
1833 The address string supports a number of different notations and formats,
1834 which are documented, hereunder, with examples:
1835 - IPv4 addresses
1836 - IPv6 addresses
1837 - Network interfaces
1838 - UNIX domain sockets
1839
1840 <hr>
1841 @par IPv4 address notations (address family @c AF_INET)
1842 Takes the form of @c x.x.x.x where each "x" represents an octet in the range
1843 of @c 0 through @c 255 (e.g., @c 127.0.0.1).
1844
1845 Under a proper implenetation of TCP/IP, an IPv4 address can be abbreviated
1846 to fewer octets. The following examples demonstrate this (an unabbreviated
1847 IPv4 address is included for completeness):
1848 - @c 0.0.0.1 may be abbreviated to @c 1
1849 - @c 4.0.0.1 may be abbrevaited to @c 4.1
1850 - @c 4.3.0.1 may be abbreviated to @c 4.3.1
1851 - @c 4.3.2.1 is not abbreviated (this is the most common usage)
1852
1853 @par Example of binding to IPv4 localhost
1854
1855 @code{.cpp}
1856 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1857 #include <randolf/rex>
1858 #include <randolf/rsocket>
1859
1860 int main(int argc, char *argv[]) {
1861 try {
1862 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1863 r.bind("127.0.0.1", 32768); // <-- You are here
1864 // ... other socket I/O operations
1865 r.close();
1866 } catch (const randolf::rex::xEACCES e) {
1867 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1868 return EXIT_FAILURE;
1869 } catch (const randolf::rex::xALL e) {
1870 std::cerr << "Socket exception: " << e.what() << std::endl;
1871 return EXIT_FAILURE;
1872 } catch (const std::exception e) {
1873 std::cerr << "Other exception: " << e.what() << std::endl;
1874 return EXIT_FAILURE;
1875 }
1876 return EXIT_SUCCESS;
1877 } // -x- int main -x-
1878 @endcode
1879
1880 @note
1881 Specifying the IP address of @c 0.0.0.0 will bind to all IPv4 addresses that
1882 are assigned to all of the host machine's network interfaces with IPv4
1883 bindings.
1884 @n@n
1885 Specifying the IP address of @c 127.0.0.1 (localhost) is useful for serving
1886 only those applications that are running on the local host and use an IPv4
1887 socket to communicate.
1888
1889 <hr>
1890 @par IPv6 address notations (address family @c AF_INET6)
1891 Takes the form of @c x:x:x:x:x:x:x:x where each "x" represents a segment in
1892 the range of @c 0 through @c ffff in hexadecimal (e.g., `0:0:0:0:0:0:0:1`),
1893 or `x:x:x:x:x:x:y.y.y.y` where `y.y.y.y` represents an [unabbreviated] IPv4
1894 address (which merely replaces the last two IPv6 segments).
1895
1896 Under a proper implenetation of TCP/IP, an IPv6 address can be abbreviated
1897 to fewer segments by using a sequence of two colons (`::`) to indicate that
1898 the undefined segments are @c 0 (this abbreviation can only be used once,
1899 and may represent segments at the beginning or end, or anywhere in between).
1900 The following examples demonstrate this (an unabbreviated IPv6 address is
1901 included for completeness):
1902 - `0:0:0:0:0:0:0:1` may be abbreviated to `::1`
1903 - `0:0:0:0:0:0:2:1` may be abbreviated to `::2:1`
1904 - `8:0:0:0:0:0:2:1` may be abbreviated to `8::2:1`
1905 - `8:7:0:0:0:0:2:1` may be abbreviated to `8:7::2:1`
1906 - `8:7:0:0:0:0:0:0` may be abbreviated to `8:7::`
1907 - `8:0:0:0:0:0:0:0` may be abbreviated to `8::`
1908 - `8:7:6:5:4:3:2:1` is not abbreviated
1909
1910 @par Example of binding to IPv6 localhost
1911
1912 @code{.cpp}
1913 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1914 #include <randolf/rex>
1915 #include <randolf/rsocket>
1916
1917 int main(int argc, char *argv[]) {
1918 try {
1919 randolf::rsocket r(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1920 r.bind("::1", 32768); // <-- You are here
1921 // ... other socket I/O operations
1922 r.close();
1923 } catch (const randolf::rex::xEACCES e) {
1924 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1925 return EXIT_FAILURE;
1926 } catch (const randolf::rex::xALL e) {
1927 std::cerr << "Socket exception: " << e.what() << std::endl;
1928 return EXIT_FAILURE;
1929 } catch (const std::exception e) {
1930 std::cerr << "Other exception: " << e.what() << std::endl;
1931 return EXIT_FAILURE;
1932 }
1933 return EXIT_SUCCESS;
1934 } // -x- int main -x-
1935 @endcode
1936
1937 @note
1938 Specifying the IP address of @c :: will bind to all IPv6 addresses that are
1939 assigned to all of the host machine's network interfaces with IPv6 bindings.
1940 @n@n
1941 Specifying the IP address of @c ::1 (localhost) is useful for serving only
1942 those applications that are running on the local host and use an IPv6 socket
1943 to communicate.
1944
1945 <hr>
1946 @par Interface address notation (address families @c AF_INET and @c AF_INET6)
1947 Takes the form of @c if=name where "name" represents the name of a local
1948 network interface.
1949
1950 Interface binding is useful when binding to interfaces that aren't configured
1951 with a static IP address because they were dymamically configured via the
1952 Dynamic Host Configuration Protocol (DHCP) or Stateless Address Configuration
1953 (SLAAC), or the configuration was changed manually by an administrator and
1954 you don't want your software to handle such changes gracefully, even if the
1955 new IP address is within the scope of an entirely different CIDR. (Changing
1956 between the IPv4 and IPv6 addresses is not supported, however, due to the
1957 fundamental differences between these two address families that includes
1958 differences beyond that of IP address format, although under a proper
1959 implementation of TCP/IP the binding should survive such changes when the IP
1960 address is reverted to the initial IP address family of the bound interface.)
1961
1962 Examples of interfaces include:
1963 - `if=lo` typical for localhost virtual network adapter
1964 - `if=bond0` typical for the first bonded virtual network adapter (used in
1965 failover and load-balancing network configurations)
1966 - `if=br0` typical for the first bridge virtual network adapter
1967 - `if=eth0` typical for the first ethernet network adapter
1968 - `if=tap0` typical for the first virtual layer 2 ethernet switch (used by
1969 VPNs to extend a remote network into the local network stack)
1970 - `if=tun0` typical for the first virtual layer 3 ethernet tunnel (used by
1971 VPNs to provide access to a remote network)
1972 - `if=wlo1` typical for the first wireless network adapter
1973
1974 @par Example of binding to interface localhost
1975
1976 @code{.cpp}
1977 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1978 #include <randolf/rex>
1979 #include <randolf/rsocket>
1980
1981 int main(int argc, char *argv[]) {
1982 try {
1983 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1984 r.bind("if=lo", 32768); // <-- You are here
1985 // ... other socket I/O operations
1986 r.close();
1987 } catch (const randolf::rex::xEACCES e) {
1988 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
1989 return EXIT_FAILURE;
1990 } catch (const randolf::rex::xALL e) {
1991 std::cerr << "Socket exception: " << e.what() << std::endl;
1992 return EXIT_FAILURE;
1993 } catch (const std::exception e) {
1994 std::cerr << "Other exception: " << e.what() << std::endl;
1995 return EXIT_FAILURE;
1996 }
1997 return EXIT_SUCCESS;
1998 } // -x- int main -x-
1999 @endcode
2000
2001 @note
2002 This is not a standard feature of the POSIX bind() function. This rsocket
2003 class uses the setsockopt() function behind-the-scenes to configure (enable)
2004 the @c SO_BINDTODEVICE option, and then the bind() function is called with
2005 the IPv4 address @c 0.0.0.0 if the underlying socket was initialized to the
2006 @c AF_INET family, or the IPv6 address @c :: if the underlying socket was
2007 initialized to the @c AF_INET6 family.
2008 @n@n
2009 Specifying the interface address of `if=lo` (localhost) is useful for serving
2010 only those applications that are running on the local host and use an IPv4 or
2011 IPv6 socket to communicate.
2012
2013 <hr>
2014 @par UNIX Domain Sockets address-notation (address family @c AF_UNIX)
2015 Takes the form of @c /path where "/path" represents an absolute path on the
2016 local file system (the path can also be relative, in which case it doesn't
2017 begin with a slash (`/`) character, but extra care is needed to ensure that
2018 the path will actually be at its expected location).
2019
2020 @par Example of binding to UNIX domain sockets
2021
2022 @code{.cpp}
2023 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2024 #include <randolf/rex>
2025 #include <randolf/rsocket>
2026
2027 int main(int argc, char *argv[]) {
2028 try {
2029 randolf::rsocket r(AF_UNIX, SOCK_STREAM);
2030 r.bind("/var/run/rsocket-test-socket.tmp"); // <-- You are here
2031 // ... other socket I/O operations
2032 r.close();
2033 } catch (const randolf::rex::xEACCES e) {
2034 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
2035 return EXIT_FAILURE;
2036 } catch (const randolf::rex::xALL e) {
2037 std::cerr << "Socket exception: " << e.what() << std::endl;
2038 return EXIT_FAILURE;
2039 } catch (const std::exception e) {
2040 std::cerr << "Other exception: " << e.what() << std::endl;
2041 return EXIT_FAILURE;
2042 }
2043 return EXIT_SUCCESS;
2044 } // -x- int main -x-
2045 @endcode
2046
2047 @note
2048 Normal socket I/O functionality is used to exchange data with another process
2049 that can open the same UNIX domain socket (normally on the same host,
2050 although it may not be outside the realm of possibility for future Linux
2051 kernels to support UNIX domain sockets on remote hosts).
2052
2053 <hr>
2054 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
2055 (or 1023 on some Operating Systems that test only for below 1024) in
2056 the absence of elevated access or the absence of a capability flag
2057 having been set
2058 @throws randolf::rex::xEACCES If binding to an interface on systems that
2059 require elevated access for direct interface binding in absence of
2060 said elevated access
2061 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
2062 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
2063 address is not available on any local interface
2064 @throws randolf::rex::xEBADF The underlying socket is not open
2065 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2066 part of the user address space
2067 @throws randolf::rex::xEINVAL Socket is already bound
2068 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
2069 valid for this socket's family (a.k.a., communication domain)
2070 @throws randolf::rex::xENOMEM Insufficient memory
2071 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2072 doesn't refer to a socket
2073 @n@n
2074 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
2075 @throws randolf::rex::xEACCES Write permission or search permission denied
2076 on a component of the path prefix (@c AF_UNIX family)
2077 @throws randolf::rex::xELOOP Too many symbolic links encountered while
2078 resolving address (@c AF_UNIX family)
2079 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
2080 @throws randolf::rex::xENOENT One of the path's directory components doesn't
2081 exist (@c AF_UNIX family)
2082 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
2083 directory (@c AF_UNIX family)
2084 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
2085
2086 @returns The same rsocket object so as to facilitate stacking
2087 @see accept()
2088 @see accept_sp()
2089 @see accept4()
2090 @see accept4_sp()
2091 @see bind(const struct sockaddr*, const socklen_t)
2092 @see connect
2093 @see listen
2094 @qualifier TLS
2095 *///=========================================================================
2096 rsocket* bind(
2097 /// IPv4 address, IPv6 address, hostname, UNIX domain sockets path, or specialized variant (see notes, above)
2098 const std::string address,
2099 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
2100 const int port = 0) {
2101 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2102 + ", " + address
2103 + " port=" + std::to_string(port) + (port == 0 ? " (emphemeral or not-applicable)" : "")
2104 + ", size=" + std::to_string(__socket_addr_size)
2105 + ");");
2106
2107//std::cout << " address: " << address << std::endl;
2108 if (address.starts_with("if=")) { // Bind to interface
2109// TODO: Use family() to simplify this section of code
2110 struct ifreq ifr{};
2111 if (address.size() - 3 >= sizeof(ifr.ifr_name)) {} // TODO: Throw exception because ASCIIZ name will be too long
2112 address.copy(ifr.ifr_name, address.size() - 3, 3); // Copy address, excluding the "if=" portion
2113 __rc_check(::setsockopt(__socket_fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)));
2114 // TODO: Figure out how to use INADDR_ANY instead of 0 or :: (assuming this is even possible)
2115 // TODO: Test IPv6 more (telnet doesn't seem to be able to connect, and continues to work on IPv4 address)
2116// __socket_addr = *mk_sockaddr_storage(__socket_addr.ss_family == AF_INET6 ? "::" : "0", port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
2117 addrinfo ai{0, __socket_addr->ss_family, __socket_type, __socket_protocol};
2118 struct sockaddr_storage* sa = mk_sockaddr_storage(__socket_addr->ss_family == AF_INET6 ? "::" : "0", port, &ai);
2119 ::free((void*)__socket_addr);
2120 __socket_addr = sa;
2121 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
2122// TODO: Figure out whether to use (sockaddr*)&__socket_addr or (sockaddr*)__socket_addr
2123// } else if (address.starts_with("if_cidr=")) { // Bind to interface based on matching CIDR
2124// // REMINDER: Update static family() method as well
2125// getifaddrs: https://man7.org/linux/man-pages/man3/getifaddrs.3.html
2126// } else if (address.starts_with("if_mac=")) { // Bind to interface based on its physical/hardware machine address
2127// // REMINDER: Update static family() method as well
2128// // Resolve interface name, and then just do what if= does (above)
2129
2130 } else {
2131// __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_protocol});
2132 addrinfo ai{0, __socket_addr->ss_family, __socket_type, __socket_protocol};
2133 struct sockaddr_storage* sa = mk_sockaddr_storage(address, port, &ai);
2134 ::free((void*)__socket_addr);
2135 __socket_addr = sa;
2136// TODO: Figure out whether to use (sockaddr*)&__socket_addr or (sockaddr*)__socket_addr
2137// __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
2138 __rc_check(::bind(__socket_fd, (sockaddr*)__socket_addr, __socket_addr_size));
2139// __rc_check(::bind(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
2140 }
2141
2142 return this;
2143 } // -x- rsocket* bind -x-
2144
2145 /*======================================================================*//**
2146 @brief
2147 Find out what buffer size is used by the various recv() methods.
2148 @returns Buffer size (in bytes)
2149 @see buffer_size(const size_t nbytes)
2150 @see buffer_size_reset
2151 @see is_buffered
2152 @qualifier TLS
2153 *///=========================================================================
2154 const size_t buffer_size() noexcept { return __buffer->get_size(); } // -x- size_t buffer_size -x-
2155
2156 /*======================================================================*//**
2157 @brief
2158 Override the default buffer size (typically 8,192 bytes) used by the various
2159 @ref recv() methods.
2160
2161 If resetting to the compiled-in default, use the buffer_size_reset() method
2162 instead of setting the value directly. This ensures that future versions,
2163 with a different compiled-in default, will be reset to the compiled-in value.
2164 @exception std::overflow_error If the new size exceeds the amount of data
2165 that's already present in the ring buffer
2166 @returns The same rsocket object so as to facilitate stacking
2167 @see buffer_size()
2168 @see buffer_size_reset()
2169 @qualifier TLS
2170 *///=========================================================================
2171 rsocket* buffer_size(
2172 /// Size of the new buffer (in bytes)
2173 const size_t nbytes) {
2174 __buffer_size = nbytes;
2175 if (__debug) debug("buffer_size(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2176 + " nbytes=" + std::to_string(nbytes)
2177 + ");");
2178 if (__buffer != nullptr) __buffer->set_size(nbytes);
2179 return this;
2180 } // -x- rsocket* buffer_size -x-
2181
2182 /*======================================================================*//**
2183 @brief
2184 Reset the default buffer size (typically 8,192 bytes) used by the various
2185 @ref recv() methods.
2186
2187 This method is preferred for resetting to the compiled-in default instead of
2188 setting the value directly. This ensures that future versions, with a
2189 different compiled-in default, will be reset to the compiled-in value.
2190 @exception std::overflow_error If the new size exceeds the amount of data
2191 that's already present in the ring buffer
2192 @returns The same rsocket object so as to facilitate stacking
2193 @see buffer_size(const size_t nbytes)
2194 @qualifier TLS
2195 *///=========================================================================
2196 rsocket* buffer_size_reset() {
2197 __buffer_size = RSOCKET_BUFFER_SIZE;
2198 if (__debug) debug("buffer_size_reset(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2199 + " nbytes=" + std::to_string(RSOCKET_BUFFER_SIZE)
2200 + ");");
2201 if (__buffer != nullptr) __buffer->set_size(__buffer_size);
2202 return this;
2203 } // -x- rsocket* buffer_size_reset -x-
2204
2205 //===========================================================================
2206 //
2207 // TODO: If family is AF_UNIX then also unlink the domain socket file.
2208 //
2209 /*======================================================================*//**
2210 @brief
2211 Close this rsocket. (If this rsocket was already closed, then calling this
2212 method additional times will have no effect, and will not cause exceptions to
2213 be thrown.)
2214 @warning
2215 This method may throw exceptions in some circumstances, which is extremely
2216 rare. If you prefer to close without having to contend with any exceptions
2217 (e.g., while calling close() from within a destructor), the close_passive()
2218 method will return an integer indicating success/failure instead of throwing
2219 an exception, and then you'll have to handle errno manually (which is useful
2220 in destructors because any error can merely be handled procedurally).
2221
2222 @throws randolf::rex::xEBADF The underlying socket is not open
2223 @throws randolf::rex::xEINTR Interrupted by a signal
2224 @throws randolf::rex::xEIO An I/O error occurred
2225
2226 @returns The same rsocket object so as to facilitate stacking
2227 @see is_closed()
2228 @qualifier POSIX
2229 @qualifier TLS
2230 *///=========================================================================
2231 rsocket* close() {
2232 if (__socket_open) {
2233 __rc_check(::close(__socket_fd));
2234 __socket_open = false;
2235 } // -x- if __socket_open -x-
2236 return this;
2237 } // -x- rsocket* close -x-
2238
2239 /*======================================================================*//**
2240 @brief
2241 Close this rsocket without throwing any exceptions (an error code is returned
2242 instead, which is useful while calling close_passive() from within a
2243 destructor).
2244 @returns 0 = success
2245 @returns -1 = error (errno will be set accordingly)
2246 @see is_closed()
2247 @qualifier TLS
2248 *///=========================================================================
2249 int close_passive() noexcept {
2250 if (__socket_open) {
2251 int rc = ::close(__socket_fd);
2252 if (rc == 0) __socket_open = false;
2253 return rc;
2254 } // -x- if __socket_open -x-
2255 return 0; // Indicate success (because the socket was previously closed successfully)
2256 } // -x- int close_passive -x-
2257
2258 /*======================================================================*//**
2259 @brief
2260 Connect this socket to a specific endpoint (which may differ from this
2261 rsocket's address that was previously configured by the @ref bind() method).
2262
2263 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
2264 @throws randolf::rex::xEADDRNOTAVAIL No ephemeral ports are available for
2265 assignment to unbound socket
2266 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
2267 @throws randolf::rex::xEAGAIN Insufficient entries in the routing cache
2268 @throws randolf::rex::xEALREADY Previous connection attempt not completed on
2269 nonblocking socket
2270 @throws randolf::rex::xEBADF The underlying socket is not open
2271 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
2272 connections
2273 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2274 part of the user address space
2275 @throws randolf::rex::xEINPROGRESS Connection cannot be completed immediately
2276 on nonblocking socket (@c AF_UNIX family fails with xEAGAIN instead)
2277 @throws randolf::rex::xEINTR Interrupted by a signal
2278 @throws randolf::rex::xEISCONN Socket is already connected
2279 @throws randolf::rex::xENETUNREACH No route to network
2280 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2281 doesn't refer to a socket
2282 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
2283 @throws randolf::rex::xEPERM Can't connect to a broadcast address when the
2284 socket's broadcast flag isn't enabled
2285 @throws randolf::rex::xEPROTOTYPE Socket type doesn't support the requested
2286 communications protocol (e.g., connecting a UNIX domain socket of
2287 type @c SOCK_STREAM to an endpoint expecting type @c SOCK_DGRAM)
2288 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
2289 @n@n
2290 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
2291 @throws randolf::rex::xEACCES Write permission or search permission denied
2292 on a component of the path prefix (@c AF_UNIX family)
2293 @throws randolf::rex::xEAGAIN Connection cannot be completed immediately on
2294 nonblocking socket (@c AF_UNIX family)
2295
2296 @returns The same rsocket object so as to facilitate stacking
2297 @see tls_do_handshake()
2298 @qualifier POSIX
2299 @qualifier TLS
2300 *///=========================================================================
2301 rsocket* connect(
2302 /// IPv4 address, IPv6 address, hostname, or UNIX domain sockets path
2303 const std::string address,
2304 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
2305 const int port = 0) {
2306 if (__debug) debug("connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2307 + ", " + address
2308 + " (port=" + std::to_string(port) + ")"
2309 + ", " + std::to_string(__socket_addr_size)
2310 + ");");
2311
2312 addrinfo ai{0, __socket_addr->ss_family, __socket_type, __socket_protocol};
2313 struct sockaddr_storage* sa = mk_sockaddr_storage(address, port, &ai);
2314 ::free((void*)__socket_addr); // Free before re-assigning new pointer
2315 __socket_addr = sa;
2316
2317 __rc_check(::connect(__socket_fd, (sockaddr*)__socket_addr, __socket_addr_size), 0, rex::rex::REX_FLAGS::REX_ALT_EAGAIN);
2318 __socket_connected = true;
2319
2320 // --------------------------------------------------------------------------
2321 // TLS.
2322 // --------------------------------------------------------------------------
2323 if (__tls) {
2324 if (__debug) debug("tls_connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2325 + ");");
2326 __rc_check_tls(SSL_connect(__tls_fd));
2327 } // -x- if __tls -x-
2328
2329 return this;
2330 } // -x- rsocket* connect -x-
2331
2332 /*======================================================================*//**
2333 @brief
2334 Find out whether debug mode is enabled.
2335
2336 @par Threads
2337 This method is threadsafe.
2338 @returns TRUE = enabled
2339 @returns FALSE = not enabled
2340 @see net_io
2341 @qualifier TLS
2342 *///=========================================================================
2343 const bool debug() noexcept { return __debug; } // -x- bool debug -x-
2344
2345 /*======================================================================*//**
2346 @brief
2347 Debug mode. When debug mode is enabled, output is sent to stderr by default,
2348 unless a second parameter specifies a different file handle (e.g., stdout, or
2349 even a socket).
2350
2351 debug(...) returns -1 if fd can't be written to (errno will be set in
2352 accordance with std::fprintf's behaviour since std::fprintf is used for
2353 writing debug output). Normally we'd throw an exception for such errors, but
2354 with debug is a special case because debugging needs to be as quick and
2355 convenient as possible for developers.
2356
2357 debug(...) returns -2 if writing to stderr failed when attempting to announce
2358 that fd can't be written to (as described above).
2359
2360 debug(...) returns -3 if some other error occurs (e.g., internal formatting
2361 error {which should not happen} or memory allocation failed, in which case
2362 there's a more serious problem that will be causing other problems elsewhere
2363 anyway).
2364
2365 Developers may add their own debug messages by using debug(std::string),
2366 which will only be written out if debug mode is enabled. This same method is
2367 used internally, so messages will be indistinguishable from developer's
2368 messages.
2369
2370 @par Threads
2371 This method is thread-safe.
2372 @returns 0 = success
2373 @returns -1 = error writing to stream (errno will be set accordingly)
2374 @returns -2 = error writing to stderr (errno will be set accordingly)
2375 @returns -3 = error (errno will be set accordingly)
2376 @see net_io
2377 @qualifier TLS
2378 *///=========================================================================
2379 const int debug(
2380 /// TRUE = enable debug mode@n
2381 /// FALSE = disable debug mode (does not close any file handles)
2382 const bool debug_flag,
2383 /// File descriptor/handle to use for debug output
2384 std::FILE* fd = stderr) noexcept {
2385
2386 int rc = debug_fd(fd); // Attempt to change debug handle
2387 if (rc != 0) return rc; // Return error id new debug handle failed
2388 time_t tm = std::time(nullptr);
2389 std::string dt(27, 0);
2390 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2391 try {
2392 if (debug_flag || __debug) std::fprintf(__debug_fd,
2393 "[%s] %s: Debug mode is %s\n",
2394 dt.data(),
2395 __debug_prefix.data(),
2396 debug_flag ? "ON" : "OFF");
2397 } catch (const std::system_error& e) { // Error writing to fd
2398 try {
2399 std::fprintf(stderr,
2400 "[%s] %s: error writing to stream\n",
2401 dt.data(),
2402 __debug_prefix.data());
2403 } catch (const std::exception& e) {
2404 return -2;
2405 }
2406 return -1;
2407 } catch (const std::exception& e) {
2408 return -3;
2409 }
2410 __debug = debug_flag; // Save debug flag
2411
2412 return 0; // Indicate success (no errors)
2413 } // -x- int debug -x-
2414
2415 /*======================================================================*//**
2416 @brief
2417 Send the specified message as debug output (as long as debug mode is enabled;
2418 if disabled, no debug output will be sent).
2419 @returns 0 = success
2420 @returns -1 = error writing to stream (errno will be set accordingly)
2421 @returns -2 = error writing to stderr (errno will be set accordingly)
2422 @returns -3 = error (errno will be set accordingly)
2423 @see debug_prefix
2424 @see net_io
2425 @qualifier TLS
2426 *///=========================================================================
2427 const int debug(
2428 /// Debug message as an ASCIIZ string
2429 const char* msg) noexcept {
2430 return __debug ? __debug_out(std::string(msg)) : 0;
2431 } // -x- int debug -x-
2432
2433 /*======================================================================*//**
2434 @copydoc debug(const char*)
2435 @qualifier TLS
2436 *///=========================================================================
2437 const int debug(
2438 /// Debug message as an std::string object
2439 const std::string msg) noexcept {
2440 return __debug ? __debug_out(msg) : 0;
2441 } // -x- int debug -x-
2442
2443 /*======================================================================*//**
2444 @brief
2445 Find out which file descriptor/handle is used for debug output.
2446 @returns file handle/descriptor currently used for debug output
2447 *///=========================================================================
2448 const std::FILE* debug_fd() noexcept { return __debug_fd; } // Get debug-output file handle
2449
2450 /*======================================================================*//**
2451 @brief
2452 Specify a different file descriptor/handle to use for debug output
2453 @returns 0 = success
2454 @returns -1 = error writing to stream (errno will be set accordingly)
2455 @returns -2 = error writing to stderr (errno will be set accordingly)
2456 @returns -3 = error (errno will be set accordingly)
2457 *///=========================================================================
2458 const int debug_fd(
2459 /// File descriptor/handle to use for debug output
2460 std::FILE* fd) noexcept { // Set debug-output file handle
2461 if (__debug_fd != fd) {
2462 debug("Current debug-output file handle unset"); // For this message, we only want to send this out if debug mode is enabled
2463 __debug_fd = fd; // Save new debug-output file handle
2464 return debug("New debug-output file handle set"); // For this message, we only want to send this out if debug mode is enabled
2465 } // -x- if ~fd -x-
2466 return 0; // Indicate success (no errors)
2467 } // -x- int debug_fd -x-
2468
2469 /*======================================================================*//**
2470 @brief
2471 Find out what the current prefix is set to that's used in debug output.
2472
2473 This may be set at any time, including before or after enabling or disabling
2474 debug mode.
2475 @returns debug prefix string
2476 @see debug
2477 @qualifier TLS
2478 *///=========================================================================
2479 const std::string debug_prefix() noexcept { return __debug_prefix; } // -x- std::string debug_prefix -x-
2480
2481 /*======================================================================*//**
2482 @brief
2483 Change the prefix used in debug output.
2484 @returns The same rsocket object so as to facilitate stacking
2485 @see debug
2486 @qualifier TLS
2487 *///=========================================================================
2488 rsocket* debug_prefix(
2489 /// New debug prefix string
2490 const std::string prefix) noexcept {
2491 __debug_prefix = prefix;
2492 return this;
2493 } // -x- std::string debug_prefix -x-
2494
2495 private:
2496 /*----------------------------------------------------------------------*//**
2497 @brief
2498 Internal debug-output function. This doesn't check __debug flag because it's
2499 expected that the normal debug() methods are taking care of this.
2500 @returns 0 = success
2501 @returns -1 = error writing to stream (errno will be set accordingly)
2502 @returns -2 = error writing to stderr (errno will be set accordingly)
2503 @returns -3 = error (errno will be set accordingly)
2504 @see debug
2505 @see debug_prefix
2506 *///-------------------------------------------------------------------------
2507 const int __debug_out(
2508 /// Debugging message
2509 const std::string msg) noexcept {
2510
2511 time_t tm = std::time(nullptr);
2512 std::string dt(27, 0);
2513 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2514 try {
2515 std::fprintf(__debug_fd,
2516 "[%s] %s: %s\n",
2517 dt.data(),
2518 __debug_prefix.data(),
2519 msg.data());
2520 } catch (const std::system_error& e) { // Error writing to fd
2521 try {
2522 std::fprintf(stderr,
2523 "[%s] %s: error writing to stream\n",
2524 dt.data(),
2525 __debug_prefix.data());
2526 } catch (const std::exception& e) {
2527 return -2;
2528 }
2529 return -1;
2530 } catch (const std::exception& e) {
2531 return -3;
2532 }
2533
2534 return 0; // Indicate success (no errors)
2535 } // -x- int __debug_out -x-
2536
2537 // --------------------------------------------------------------------------
2538 // These are specialized internal debug output methods for presenting socket
2539 // options when they are get or set. These methods are marked as "private"
2540 // because they are really aren't useful outside of internal-use contexts.
2541 //
2542 // @par Threads
2543 // Some of these methods are thread-safe.
2544 //
2545 // These methods are thread-safe when they don't reference structures (e.g.,
2546 // integer {int}, unsigned integer {u_int}, and unsigned character {u_char}).
2547 //
2548 // These methods are not necessarily thread-safe if they reference structures
2549 // unless those structures are defined with std::atomic, or they were
2550 // constructed on-the-fly as anonymous parameters.
2551 // --------------------------------------------------------------------------
2552 const int __debug_sockopt(
2553 /// Name of function or method
2554 const std::string func,
2555 /// The level at which the option resides; typically @c SOL_SOCKET
2556 const int level,
2557 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2558 const int option) {
2559 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2560 + ", " + std::to_string(level)
2561 + ", " + std::to_string(option)
2562 + ", <zero>"
2563 + ", size=0"
2564 + ");");
2565 } // -x- int __debug_sockopt -x-
2566 const int __debug_sockopt(
2567 /// Name of function or method
2568 const std::string func,
2569 /// The level at which the option resides; typically @c SOL_SOCKET
2570 const int level,
2571 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2572 const int option,
2573 /// The structure that this socket option will be set to
2574 const int value) {
2575 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2576 + ", " + std::to_string(level)
2577 + ", " + std::to_string(option)
2578 + ", " + std::to_string(value)
2579 + ", size=" + std::to_string(sizeof(value))
2580 + ");");
2581 } // -x- int __debug_sockopt -x-
2582 const int __debug_sockopt(
2583 /// Name of function or method
2584 const std::string func,
2585 /// The level at which the option resides; typically @c SOL_SOCKET
2586 const int level,
2587 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2588 const int option,
2589 /// The structure that this socket option will be set to
2590 const u_int value) {
2591 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2592 + ", " + std::to_string(level)
2593 + ", " + std::to_string(option)
2594 + ", u_int{" + std::to_string(value) + "}"
2595 + ", size=" + std::to_string(sizeof(value))
2596 + ");");
2597 } // -x- int __debug_sockopt -x-
2598 const int __debug_sockopt(
2599 /// Name of function or method
2600 const std::string func,
2601 /// The level at which the option resides; typically @c SOL_SOCKET
2602 const int level,
2603 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2604 const int option,
2605 /// The structure that this socket option will be set to
2606 const u_char value) {
2607 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2608 + ", " + std::to_string(level)
2609 + ", " + std::to_string(option)
2610 + ", u_char{" + std::to_string(value) + "}"
2611 + ", size=" + std::to_string(sizeof(value))
2612 + ");");
2613 } // -x- int __debug_sockopt -x-
2614 const int __debug_sockopt(
2615 /// Name of function or method
2616 const std::string func,
2617 /// The level at which the option resides; typically @c SOL_SOCKET
2618 const int level,
2619 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2620 const int option,
2621 /// The structure that this socket option will be set to
2622 const linger* value) {
2623 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2624 + ", " + std::to_string(level)
2625 + ", " + std::to_string(option)
2626 + ", linger{l_onoff=" + std::to_string(value->l_onoff)
2627 + ", l_linger=" + std::to_string(value->l_linger) + "}"
2628 + ", " + std::to_string(sizeof(value))
2629 + ");");
2630 } // -x- int __debug_sockopt -x-
2631 const int __debug_sockopt(
2632 /// Name of function or method
2633 const std::string func,
2634 /// The level at which the option resides; typically @c SOL_SOCKET
2635 const int level,
2636 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2637 const int option,
2638 /// The structure that this socket option will be set to
2639 const timeval* value) {
2640 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2641 + ", " + std::to_string(level)
2642 + ", " + std::to_string(option)
2643 + ", timeval{tv_sec=" + std::to_string(value->tv_sec)
2644 + ", tv_usec=" + std::to_string(value->tv_usec) + "}"
2645 + ", " + std::to_string(sizeof(value))
2646 + ");");
2647 } // -x- int __debug_sockopt -x-
2648 const int __debug_sockopt(
2649 /// Name of function or method
2650 const std::string func,
2651 /// The level at which the option resides; typically @c SOL_SOCKET
2652 const int level,
2653 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2654 const int option,
2655 /// The structure that this socket option will be set to
2656 const in_addr* value) {
2657 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2658 + ", " + std::to_string(level)
2659 + ", " + std::to_string(option)
2660 + ", in_addr{" + std::to_string(value->s_addr) + "}"
2661 + ", " + std::to_string(sizeof(value))
2662 + ");");
2663 } // -x- int __debug_sockopt -x-
2664 const int __debug_sockopt(
2665 /// Name of function or method
2666 const std::string func,
2667 /// The level at which the option resides; typically @c SOL_SOCKET
2668 const int level,
2669 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2670 const int option,
2671 /// The structure that this socket option will be set to
2672 const ip_mreq* value) {
2673 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2674 + ", " + std::to_string(level)
2675 + ", " + std::to_string(option)
2676 + ", ip_mreq{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2677 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2678 + ", " + std::to_string(sizeof(value))
2679 + ");");
2680 } // -x- int __debug_sockopt -x-
2681 const int __debug_sockopt(
2682 /// Name of function or method
2683 const std::string func,
2684 /// The level at which the option resides; typically @c SOL_SOCKET
2685 const int level,
2686 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2687 const int option,
2688 /// The structure that this socket option will be set to
2689 const ip_mreq_source* value) {
2690 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2691 + ", " + std::to_string(level)
2692 + ", " + std::to_string(option)
2693 + ", ip_mreq_source{addr=" + std::to_string(value->imr_multiaddr.s_addr)
2694 + ", source=" + std::to_string(value->imr_sourceaddr.s_addr)
2695 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
2696 + ", " + std::to_string(sizeof(value))
2697 + ");");
2698 } // -x- int __debug_sockopt -x-
2699 const int __debug_sockopt(
2700 /// Name of function or method
2701 const std::string func,
2702 /// The level at which the option resides; typically @c SOL_SOCKET
2703 const int level,
2704 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2705 const int option,
2706 /// The structure that this socket option will be set to
2707 const icmp6_filter* value) {
2708 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2709 + ", " + std::to_string(level)
2710 + ", " + std::to_string(option)
2711 + ", icmp6_filter{[0]=" + std::to_string(value->icmp6_filt[0])
2712 + ", [1]=" + std::to_string(value->icmp6_filt[1])
2713 + ", [2]=" + std::to_string(value->icmp6_filt[2])
2714 + ", [3]=" + std::to_string(value->icmp6_filt[3])
2715 + ", [4]=" + std::to_string(value->icmp6_filt[4])
2716 + ", [5]=" + std::to_string(value->icmp6_filt[5])
2717 + ", [6]=" + std::to_string(value->icmp6_filt[6])
2718 + ", [7]=" + std::to_string(value->icmp6_filt[7]) + "}"
2719 + ", " + std::to_string(sizeof(value))
2720 + ");");
2721 } // -x- int __debug_sockopt -x-
2722 const int __debug_sockopt(
2723 /// Name of function or method
2724 const std::string func,
2725 /// The level at which the option resides; typically @c SOL_SOCKET
2726 const int level,
2727 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2728 const int option,
2729 /// The structure that this socket option will be set to
2730 const sockaddr_in6* value) {
2731 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2732 + ", " + std::to_string(level)
2733 + ", " + std::to_string(option)
2734 + ", sockaddr_in6{family=" + std::to_string(value->sin6_family)
2735 + ", port=" + std::to_string(value->sin6_port)
2736 + ", flowinfo=" + std::to_string(value->sin6_flowinfo)
2737 + ", addr=" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[0])
2738 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[1])
2739 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[2])
2740 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[3])
2741 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[4])
2742 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[5])
2743 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[6])
2744 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[7])
2745 + ", scope_id=" + std::to_string(value->sin6_scope_id) + "}"
2746 + ", " + std::to_string(sizeof(value))
2747 + ");");
2748 } // -x- int __debug_sockopt -x-
2749 const int __debug_sockopt(
2750 /// Name of function or method
2751 const std::string func,
2752 /// The level at which the option resides; typically @c SOL_SOCKET
2753 const int level,
2754 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2755 const int option,
2756 /// The structure that this socket option will be set to
2757 const ip6_mtuinfo* value) {
2758 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2759 + ", " + std::to_string(level)
2760 + ", " + std::to_string(option)
2761 + ", ip6_mtuinfo{addr=" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[0])
2762 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[1])
2763 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[2])
2764 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[3])
2765 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[4])
2766 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[5])
2767 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[6])
2768 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[7])
2769 + ", mtu=" + std::to_string(value->ip6m_mtu) + "}"
2770 + ", " + std::to_string(sizeof(value))
2771 + ");");
2772 } // -x- int __debug_sockopt -x-
2773 const int __debug_sockopt(
2774 /// Name of function or method
2775 const std::string func,
2776 /// The level at which the option resides; typically @c SOL_SOCKET
2777 const int level,
2778 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2779 const int option,
2780 /// The structure that this socket option will be set to
2781 const ipv6_mreq* value) {
2782 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2783 + ", " + std::to_string(level)
2784 + ", " + std::to_string(option)
2785 + ", ipv6_mreq{addr=" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[0])
2786 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[1])
2787 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[2])
2788 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[3])
2789 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[4])
2790 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[5])
2791 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[6])
2792 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[7])
2793 + ", iface=" + std::to_string(value->ipv6mr_interface) + "}"
2794 + ", " + std::to_string(sizeof(value))
2795 + ");");
2796 } // -x- int __debug_sockopt -x-
2797 const int __debug_sockopt(
2798 /// Name of function or method
2799 const std::string func,
2800 /// The level at which the option resides; typically @c SOL_SOCKET
2801 const int level,
2802 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2803 const int option,
2804 /// The structure that this socket option will be set to
2805 const group_req* value) {
2806 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2807 + ", " + std::to_string(level)
2808 + ", " + std::to_string(option)
2809 + ", group_req{iface=" + std::to_string(value->gr_interface)
2810 + ", <group_req>}"
2811 + ", " + std::to_string(sizeof(value))
2812 + ");");
2813 } // -x- int __debug_sockopt -x-
2814 const int __debug_sockopt(
2815 /// Name of function or method
2816 const std::string func,
2817 /// The level at which the option resides; typically @c SOL_SOCKET
2818 const int level,
2819 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2820 const int option,
2821 /// The structure that this socket option will be set to
2822 const group_source_req* value) {
2823 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2824 + ", " + std::to_string(level)
2825 + ", " + std::to_string(option)
2826 + ", group_source_req{iface=" + std::to_string(value->gsr_interface)
2827 + ", <group_source_req>}"
2828 + ", " + std::to_string(sizeof(value))
2829 + ");");
2830 } // -x- int __debug_sockopt -x-
2831 const int __debug_sockopt_other(
2832 /// Name of function or method
2833 const std::string func,
2834 /// The level at which the option resides; typically @c SOL_SOCKET
2835 const int level,
2836 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2837 const int option,
2838 /// The structure that this socket option will be set to
2839 const socklen_t size) {
2840 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2841 + ", " + std::to_string(level)
2842 + ", " + std::to_string(option)
2843 + ", other=?"
2844 + ", " + std::to_string(size)
2845 + ");");
2846 } // -x- int __debug_sockopt_other -x-
2847
2848 public:
2849 /*======================================================================*//**
2850 @brief
2851 Discards the specified number of 8-bit bytes efficiently, and without closing
2852 the stream, and without consuming excessive quantities of memory.
2853 @exception randolf::rex::xERANGE An invalid value was specified for either
2854 the @c nbytes or @c memory_size parameter (e.g., a negative value,
2855 except for -1 with the @c nbytes parameter)
2856 @returns Number of bytes that were successfully discarded
2857 @see buffer_size
2858 @see pending
2859 @see recv
2860 @qualifier TLS
2861 *///=========================================================================
2862 int discard(
2863 /// Number of bytes to discard@n
2864 /// 0 = use internal @ref buffer_size() @n
2865 /// -1 = all remaining data waiting to be received (in other words, this
2866 /// method will repeatedly consume all data until encountering @ref eos)
2867 int nbytes,
2868 /// MSG_OOB@n
2869 /// MSG_PEEK (ignored, to prevent an endless loop)@n
2870 /// MSG_WAITALL@n
2871 /// MSG_DONTWAIT@n
2872 /// MSG_CMSG_CLOEXEC
2873 int posix_flags = 0,
2874 /// Maximum internal read size@n
2875 /// 0 = default to @ref buffer_size (when @c nbytes is larger than this size,
2876 /// multiple reads into this buffer will be utilized behind-the-scenes to
2877 /// consume the quantity of data specified by the @c nbytes parameter)
2878 const size_t memory_size = 0) {
2879 int buf_size = memory_size != 0 ? memory_size : __buffer_size;
2880 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2881 + ", " + std::to_string(nbytes)
2882 + ", " + std::to_string(posix_flags)
2883 + ", " + std::to_string(buf_size)
2884 + ");");
2885
2886 // --------------------------------------------------------------------------
2887 // Syntax checks.
2888 // --------------------------------------------------------------------------
2889 if (nbytes == 0) nbytes = __buffer_size;
2890 else if (nbytes < -1) throw randolf::rex::xERANGE( "nbytes parameter of " + std::to_string(nbytes) + " is below -1");
2891 if (memory_size < 0) throw randolf::rex::xERANGE("memory_size parameter of " + std::to_string(memory_size) + " is below 0");
2892
2893 // --------------------------------------------------------------------------
2894 // The MSG_PEEK flag is incompatible with this method, so we're turning it
2895 // off to prevent an endless loop. We're providing a debug message for this
2896 // in case someone's trying to track something down where they're using the
2897 // MSG_PEEK flag with a misunderstanding of the purpose of this method.
2898 // --------------------------------------------------------------------------
2899 if (posix_flags & MSG_PEEK) {
2900 posix_flags &= (~MSG_PEEK); // Remove MSG_PEEK flag if it was enabled
2901 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2902 + " ignoring incompatible MSG_PEEK flag"
2903 + ");");
2904 } // -x- if MSG_PEEK -x-
2905
2906 // --------------------------------------------------------------------------
2907 // Internal variables.
2908 // --------------------------------------------------------------------------
2909 int discarded = 0; // Total number of bytes discarded
2910 char buf[buf_size];
2911
2912 // --------------------------------------------------------------------------
2913 // Discard loop. We are intentionally handling nbytes in two different ways
2914 // hear with a little bit more code to benefit from the trade-off of faster
2915 // overall performance.
2916 // --------------------------------------------------------------------------
2917 try {
2918
2919 // --------------------------------------------------------------------------
2920 // Discard all remaining data waiting to be received.
2921 // --------------------------------------------------------------------------
2922 if (nbytes < 0) {
2923 while (!eos()) {
2924 discarded += __recv(buf, buf_size, posix_flags);
2925 } // -x- while n -x-
2926
2927 // --------------------------------------------------------------------------
2928 // Discard specific amount of data waiting to be received.
2929 // --------------------------------------------------------------------------
2930 } else {
2931 int n = 0;
2932 int inbytes = nbytes;
2933 while (inbytes > 0 && !eos()) {
2934 int n = __recv(buf, std::min(inbytes, buf_size), posix_flags);
2935 inbytes -= n;
2936 discarded += n;
2937 } // -x- while nbytes -x-
2938 } // -x- if nbytes -x-
2939
2940 // --------------------------------------------------------------------------
2941 // Ignore exception so that we can simply indicate how many bytes were
2942 // successfully received and discarded.
2943 // --------------------------------------------------------------------------
2944 } catch(const rex::xALL e) {}
2945
2946 // --------------------------------------------------------------------------
2947 // Erase all data to prevent data from being unexpectedly exposed through any
2948 // other memory allocations later that use some or all of the same memory.
2949 // --------------------------------------------------------------------------
2950// for (int i = nbytes <= 0 ? buf_size : std::min(nbytes, buf_size); i > 0; i--) {
2951 for (int i = std::min(discarded, buf_size); i > 0; i--) {
2952 buf[i] = '\0';
2953 } // -x- for i -x-
2954
2955 return discarded;
2956 } // -x- int discard -x-
2957
2958 /*======================================================================*//**
2959 @brief
2960 Discards the specified number of 8-bit bytes efficiently, but stops upon
2961 encountering an EoL sequence (which will also be discarded; to find out which
2962 EoL sequence was consumed, use the @ref eol_consumed_seq method which will
2963 also return an empty string if no EoL sequence was consumed), and without
2964 closing the stream, and without consuming excessive quantities of memory.
2965
2966 This method is particularly useful for discarding lines that are too long,
2967 which, for example, makes it possible to ignore lines that are too long (and
2968 optionally present or log an error) and continue reading the next line(s)
2969 that follow.
2970
2971 @exception randolf::rex::xERANGE An invalid value was specified for either
2972 the @c nbytes or @c memory_size parameter (e.g., a negative value,
2973 except for -1 with the @c nbytes parameter)
2974 @returns Number of bytes that were successfully discarded
2975 @see buffer_size
2976 @see pending
2977 @see recv
2978 @qualifier TLS
2979 *///=========================================================================
2980 int discard_line(
2981 /// Number of bytes to discard@n
2982 /// 0 = use internal @ref buffer_size() @n
2983 /// -1 = all remaining data waiting to be received (in other words, this
2984 /// method will repeatedly consume all data until encountering an EoL
2985 /// sequence or once @ref eos is reached)
2986 int nbytes = -1,
2987 /// MSG_OOB@n
2988 /// MSG_PEEK (ignored, to prevent an endless loop)@n
2989 /// MSG_WAITALL@n
2990 /// MSG_DONTWAIT@n
2991 /// MSG_CMSG_CLOEXEC
2992 int posix_flags = 0,
2993 /// Line timeout (in seconds)@n
2994 /// 0 = no timeout (default), unless it was configured by way of the
2995 /// @ref timeout_recvline(long) method
2996 long timeout = 0,
2997 /// Maximum internal read size@n
2998 /// 0 = default to internal buffer's base buffer size (when @c nbytes is
2999 /// larger than this size, multiple reads into this buffer will be utilized
3000 /// behind-the-scenes to consume @c nbytes quantity of data)
3001 const size_t memory_size = 0) {
3002 int buf_size = memory_size != 0 ? memory_size : __buffer->get_base_size();
3003 if (__debug) debug("discard_line(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3004 + ", " + std::to_string(nbytes)
3005 + ", " + std::to_string(posix_flags)
3006 + ", " + std::to_string(buf_size)
3007 + ");");
3008
3009 // --------------------------------------------------------------------------
3010 // Syntax checks.
3011 // --------------------------------------------------------------------------
3012 bool infinite = nbytes == -1; // Keep track of whether nbytes was -1 before changing its value
3013 if (nbytes == 0) nbytes = __buffer_size;
3014 else if (nbytes == -1) nbytes = INT_MAX;
3015 else if (nbytes < -1) throw randolf::rex::xERANGE("nbytes parameter of " + std::to_string(nbytes) + " is below -1");
3016//std::cout << "buf_size=" << buf_size << std::endl;
3017
3018 // --------------------------------------------------------------------------
3019 // Syntax checks as part of preparation of timeout variable for faster
3020 // comparisons within line-reading loop.
3021 // --------------------------------------------------------------------------
3022 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
3023 if (timeout == 0) timeout = __recvline_timeout;
3024 timeout = timeout == 0 ? LONG_MAX : timeout + time(0);
3025
3026 // --------------------------------------------------------------------------
3027 // The MSG_PEEK flag is incompatible with this method, so we're turning it
3028 // off to prevent an endless loop. We're providing a debug message for this
3029 // in case someone's trying to track something down where they're using the
3030 // MSG_PEEK flag with a misunderstanding of the purpose of this method.
3031 // --------------------------------------------------------------------------
3032 if (posix_flags & MSG_PEEK) {
3033 posix_flags &= (~MSG_PEEK); // Remove MSG_PEEK flag if it was enabled
3034 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3035 + " ignoring incompatible MSG_PEEK flag"
3036 + ");");
3037 } // -x- if MSG_PEEK -x-
3038
3039 // --------------------------------------------------------------------------
3040 // Internal variables.
3041 // --------------------------------------------------------------------------
3042 int discarded = 0; // Total number of bytes discarded
3043 int buf_size_eol = buf_size + 1;
3044 char buf[buf_size_eol];
3045// std::string buf;
3046// buf.resize(buf_size_eol); // Pre-allocate underlying array to prevent memory corruption when __recv writes directly into it, with +1 for straddled EoL
3047
3048 // --------------------------------------------------------------------------
3049 // Discard loop. We are intentionally handling nbytes in two different ways
3050 // hear with a little bit more code to benefit from the trade-off of faster
3051 // overall performance.
3052 // --------------------------------------------------------------------------
3053 try {
3054
3055 // --------------------------------------------------------------------------
3056 // Discard all remaining data waiting to be received.
3057 // --------------------------------------------------------------------------
3058 int len_with_eol = 0;
3059 int n = -1;
3060 if (infinite) {
3061 while (len_with_eol == 0 && n != 0 && !eos()) {
3062 n = __recv(buf, buf_size_eol, posix_flags | MSG_PEEK);
3063
3064 // --------------------------------------------------------------------------
3065 // Affect an actual discard of the correct amount of data. If "len" is -1,
3066 // then just discard everything because it means that we didn't encounter an
3067 // EoL sequence in this iteration.
3068 // --------------------------------------------------------------------------
3069 int len = eol_index(buf, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
3070//std::cout << "///// n=" << n << " len=" << len << " len_with_eol=" << len_with_eol << " buf_size_eol=" << buf_size_eol << std::endl;
3071 n = __recv(buf, len > 0 ? len_with_eol : n, posix_flags); // Consume data
3072//std::cout << " n=" << n << std::endl;
3073 discarded += n;
3074 } // -x- while n -x-
3075
3076 // --------------------------------------------------------------------------
3077 // Discard specific amount of data waiting to be received.
3078 // --------------------------------------------------------------------------
3079 } else {
3080 int inbytes = nbytes;
3081 while (len_with_eol == 0 && n != 0 && inbytes > 0 && !eos()) {
3082 n = __recv(buf, std::min(inbytes + 1, buf_size_eol), posix_flags | MSG_PEEK);
3083
3084 // --------------------------------------------------------------------------
3085 // Affect an actual discard of the correct amount of data. If "len" is -1,
3086 // then just discard everything because it means that we didn't encounter an
3087 // EoL sequence in this iteration.
3088 // --------------------------------------------------------------------------
3089 int len = eol_index(buf, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
3090//std::cout << "||||| n=" << n << " len=" << len << " len_with_eol=" << len_with_eol << " buf_size_eol=" << buf_size_eol << std::endl;
3091 n = __recv(buf, len >= 0 ? len_with_eol : n, posix_flags); // Consume data
3092//std::cout << " n=" << n << std::endl;
3093 inbytes -= n;
3094 discarded += n;
3095 } // -x- while nbytes -x-
3096 } // -x- if nbytes -x-
3097
3098 // --------------------------------------------------------------------------
3099 // Ignore exception so that we can simply indicate how many bytes were
3100 // successfully received and discarded.
3101 // --------------------------------------------------------------------------
3102 } catch(const rex::xALL e) {
3103//std::cout << "Exception: " << e.what() << std::endl;
3104 }
3105
3106//std::cout << "discarded=" << discarded << std::endl;
3107
3108 // --------------------------------------------------------------------------
3109 // Erase all data to prevent data from being unexpectedly leaked to any other
3110 // memory allocations later that use some or all of the same memory.
3111 //
3112 // Note: The +1 in the buffer size calcuation is to include a straddled EoL.
3113 // --------------------------------------------------------------------------
3114// for (int i = infinite ? buf_size : std::min(nbytes + 1, buf_size_eol); i > 0; i--) {
3115 for (int i = std::min(discarded, buf_size); i > 0; i--) {
3116 buf[i] = '\0';
3117 } // -x- for i -x-
3118
3119 return discarded;
3120 } // -x- int discard_line -x-
3121
3122 /*======================================================================*//**
3123 @brief
3124 Find out what the current EoL (End of Line) sequence is set to.
3125 @warning
3126 To send an EoL sequence do not use `send(r.eol())` because it may not be
3127 initialized yet and the endpoint you're sending to may seem unresponsive or
3128 other unexpected behaviour may occur.@n
3129 @n
3130 To send an EoL sequence properly, use @ref sendline(); although specifying no
3131 parameters is more efficient than specifying an empty string (@c ""), the
3132 specialized @ref send_eol() method is the most efficient option for sending
3133 an EoL sequence separately.
3134 @note
3135 An empty EoL sequence means that CRLF will be sent by @ref sendline(), and
3136 that @ref recvline(), and @ref recv_rline() will automatically detect from
3137 one of @c CR, @c CRLF, @c LF, and @c LFCR.
3138 @returns Current EoL sequence
3139 @see eol_adoption
3140 @see eol_fix_printf
3141 @see printfline
3142 @see recvline
3143 @see recv_rline
3144 @see sendline
3145 @see send_eol
3146 @see vprintfline
3147 @qualifier TLS
3148 *///=========================================================================
3149 const std::string eol() noexcept { return __eol; } // -x- std::string eol -x-
3150
3151 /*======================================================================*//**
3152 @brief
3153 Set EoL (End of Line) sequence. This sequence is used by recvline(),
3154 recv_rline(), sendline(), and related functions, and it defaults to an empty
3155 string which results in the EoL sequence being detected automatically
3156 on-the-fly.
3157
3158 - @c "" (empty string) = automatically detect on-the-fly (default)
3159 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
3160 - @c \\r (CR) = Carriage Return (typical for MacOS)
3161 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
3162
3163 @note
3164 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
3165 IMAP4, FINGER, NICNAME/WHOIS, etc.).
3166 @returns The same rsocket object so as to facilitate stacking
3167 @see eol_adoption
3168 @see eol_fix_printf
3169 @see printfline
3170 @see vprintfline
3171 @see recv_rline
3172 @see recvline
3173 @see sendline
3174 @see send_eol
3175 @qualifier TLS
3176 *///=========================================================================
3177 rsocket* eol(
3178 /// EoL sequence as an ASCIIZ string
3179 const char* eol) noexcept {
3180 __eol = std::string(eol);
3181 __eol_out = __eol.empty() ? __CRLF : __eol;
3182 return this;
3183 } // -x- rsocket* eol -x-
3184
3185 /*======================================================================*//**
3186 @brief
3187 Set EoL (End of Line) sequence. This sequence is used by recvline(),
3188 recv_rline(), sendline(), and related functions, and it defaults to an empty
3189 string which results in the EoL sequence being detected automatically
3190 on-the-fly.
3191
3192 - @c "" (empty string) = automatically detect on-the-fly (default)
3193 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
3194 - @c \\r (CR) = Carriage Return (typical for MacOS)
3195 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
3196
3197 @note
3198 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
3199 IMAP4, FINGER, NICNAME/WHOIS, etc.).
3200 @returns The same rsocket object so as to facilitate stacking
3201 @see eol_fix_printf
3202 @see printfline
3203 @see vprintfline
3204 @see recv_rline
3205 @see recvline
3206 @see sendline
3207 @see send_eol
3208 @qualifier TLS
3209 *///=========================================================================
3210 rsocket* eol(
3211 /// EoL sequence as an std::string object
3212 const std::string eol) noexcept {
3213 __eol = eol;
3214 return this;
3215 } // -x- rsocket* eol -x-
3216
3217 /*======================================================================*//**
3218 @brief
3219 Configure EoL adoption policy for the @ref recvline() and @ref recv_rline()
3220 methods. By default, @ref rsocket is configured with the EoL adoption policy
3221 enabled alongside an empty @ref eol() sequence, which results in the default
3222 operation being that the EoL sequence automatically gets detected and updated
3223 internally upon the first use of either the @ref recvline() or
3224 @ref recv_rline method.
3225
3226 The EoL adoption policy is only effective when the @ref eol() sequence is not
3227 defined (which is indicated by an empty EoL sequence string).
3228
3229 The EoL sequence is updated only when the EoL sequence string is empty, and
3230 when this EoL adoption policy is enabled.
3231 @returns The same rsocket object so as to facilitate stacking
3232 @see eol
3233 @see eol_index
3234 @see is_eol_adoption
3235 @see recv_rline
3236 @see recvline
3237 @see sendline
3238 @see send_eol
3239 @qualifier TLS
3240 *///=========================================================================
3241 rsocket* eol_adoption(
3242 /// TRUE = enable EoL adoption (default)@n
3243 /// FALSE = disable EoL adoption
3244 const bool flag) noexcept {
3245 __eol_adoption = flag;
3246 return this;
3247 } // -x- rsocket* eol_adoption -x-
3248
3249 /*======================================================================*//**
3250 @brief
3251 Returns a String containing the EoL character sequence that was consumed by
3252 the most recent successful call to the @ref recvline() or @ref recv_rline
3253 method ("successful" in this context means that the received line was
3254 terminated by a valid EoL character sequence; otherwise the
3255 previous/unmodified value is returned).
3256 @warning
3257 This method must not be used to determine whether the @ref recvline() method
3258 successfully consumed an EoL character sequence.&nbsp; The reason for this is
3259 that @ref recvline() doesn't update this string when it doesn't consume an
3260 EoL character sequence, hence interpreting its results can (and likely will)
3261 lead to false positives.
3262 @returns EoL character sequence
3263 @see eol
3264 @see eol_adoption
3265 @see recv_rline
3266 @see recvline
3267 @qualifier TLS
3268 *///=========================================================================
3269 const std::string eol_consumed_seq() noexcept { return __eol_consumed_seq; } // -x- std::string eol_consumed_seq -x-
3270
3271 /*======================================================================*//**
3272 @brief
3273 Configure EoL substitution policy for the @ref printf(), @ref printfline(),
3274 @ref vprintf(), and @ref vprintfline() methods. By default, @ref rsocket
3275 substitutes printf's @c \\n sequence with the EoL sequence (if defined), but
3276 this method can be used to disable this behaviour.
3277 @note
3278 The @c \\n sequence used in the printf format string normally coincides with
3279 the local Operating System's newline standard, which is very likely different
3280 from the @ref rsocket endpoint's newline standard, and/or the newline
3281 standard of the protocol being implemented. This policy setting makes it
3282 possible to control whether to use the configured EoL sequence when sending a
3283 formatted string to the endpoint.
3284 @returns The same rsocket object so as to facilitate stacking
3285 @see eol
3286 @see is_eol_fix_printf
3287 @see printf
3288 @see printfline
3289 @see vprintf
3290 @see vprintfline
3291 @qualifier TLS
3292 *///=========================================================================
3293 rsocket* eol_fix_printf(
3294 /// TRUE = enable EoL substitution (default)@n
3295 /// FALSE = disable EoL substitution
3296 const bool flag) noexcept {
3297 __eol_fix_printf = flag;
3298 return this;
3299 } // -x- rsocket* eol_fix_printf -x-
3300
3301 /*======================================================================*//**
3302 @brief
3303 Finds the first instance of the EoL sequence and returns its offset (which is
3304 effectively the same as the size of the text, not including the characters
3305 that the EoL sequence is comprised of).
3306
3307 @note
3308 This method is specialized primarily for internal use by the @ref recvline()
3309 and @ref recv_rline() methods, but is made available here in case there's a
3310 need to check in-memory text using this rsocket's EoL detection policy.
3311 @returns Size of EoL sequence
3312 @returns -1 if EoL sequence wasn't found
3313 @see eol
3314 @see eol_adoption
3315 @qualifier TLS
3316 *///=========================================================================
3317 const int eol_index(
3318 /// Buffer that probably contains at least one EoL sequence
3319 const std::string buffer,
3320 /// Size of string with EoL character sequence included (will be updated by this method)
3321 int* with_eol_size) noexcept {
3322
3323 // --------------------------------------------------------------------------
3324 // An EoL character sequence was specified, so a simple search will suffice.
3325 // --------------------------------------------------------------------------
3326 if (!__eol.empty()) {
3327//std::cout << "!__eol.empty() ------------------------------ " << randolf::rtools::to_hex(__eol) << std::endl;
3328 int pos = buffer.find(__eol);
3329 if (pos >= 0) {
3330 *with_eol_size = pos + __eol.size(); // Update size of EoL sequence
3331//std::cout << "------0------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3332 //return pos;
3333 } // -x- if pos -x-
3334//std::cout << "------1------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3335 if (!__eol_adoption) return pos; // Return the -1 error if we're not adopting different EoF sequences on-the-fly
3336 } // -x- if !__eol.empty() -x-
3337//std::cout << "__eol.empty() ------------------------------ String is empty" << std::endl;
3338
3339 // --------------------------------------------------------------------------
3340 // Automatic detection of EoL sequence, so a more flexible approach will be
3341 // required.
3342 //
3343 // Search for all four possible EoL sequences (as indicated by an empty EoL
3344 // character sequence string):
3345 //
3346 // CRLF: Common for text lines with internet protocols such as SMTP, POP3,
3347 // IMAP4, HTTP, FINGER, WHOIS, etc. Also: DOS and OS/2 EoL sequence.
3348 //
3349 // CR: MacOS EoL character.
3350 //
3351 // LF: UNIX/Linux EoL character.
3352 //
3353 // LFCR: Extremely rare, but I've encountered multiple instances where the
3354 // intended CRLF was reversed to LFCR, possibly due to a programming
3355 // error or an artifact of inappropriate/unintended endian conversion.
3356 // --------------------------------------------------------------------------
3357 int pos = buffer.find(__CR); // CR or CRLF
3358//std::cout << "------2------ <CR> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3359 if (pos == (buffer.size() - 1)) { // EoL sequence not found (this handles a specific empty-buffer edge case caused by SSL background chatter that's very rare but causes problems that are nearly impossible to debug when they do occur)
3360 *with_eol_size = pos;// + 1;
3361// *with_eol_size = 1;
3362//std::cout << "------3------ <!CR> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3363 return pos;
3364 } else if (pos >= 0) { // EoL sequence found
3365 *with_eol_size = pos + (buffer[pos + 1] == __LF ? 2 : 1); // 2=CRLF, 1=CR
3366// *with_eol_size = buffer[pos + 1] == __LF ? 2 : 1; // 2=CRLF, 1=CR
3367 if (__eol_adoption) {
3368 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
3369 __eol.resize(*with_eol_size - pos); // Truncate extra characters
3370 } // -x- if __eol_adoption -x-
3371//std::cout << "------4------ <CR/CRLF-adopted> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3372 return pos; // Either way, we're done
3373 } // -x- if pos -x-
3374
3375 pos = buffer.find(__LF); // LF or LFCR
3376 if (pos == (buffer.size() - 1)) { // EoL sequence not found (this handles a specific empty-buffer edge case caused by SSL background chatter that's very rare but causes problems that are nearly impossible to debug when they do occur)
3377 *with_eol_size = pos;// + 1;
3378// *with_eol_size = 1;
3379//std::cout << "------5------ <LF> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3380 return pos;
3381 } else if (pos >= 0) { // EoL sequence found
3382 *with_eol_size = pos + (buffer[pos + 1] == __CR ? 2 : 1); // 2=LFCR, 1=LF
3383// *with_eol_size = buffer[pos + 1] == __CR ? 2 : 1; // 2=LFCR, 1=LF
3384 if (__eol_adoption) {
3385 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
3386 __eol.resize(*with_eol_size - pos); // Truncate extra characters
3387 } // -x- if __eol_adoption -x-
3388//std::cout << "------6------ <LF/LFCR-adopted> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3389 return pos; // Either way, we're done
3390 } // -x- if pos -x-
3391
3392//std::cout << "------7------ <LF> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3393 return pos;
3394 } // -x- int eol_index -x-
3395
3396 /*======================================================================*//**
3397 @brief
3398 Find out if the stream is at its end and that this @ref rsocket's internal
3399 buffer (if one had been set up by the @ref recvline() method) is empty. This
3400 doesn't necessarily mean that the stream is closed; but rather that the
3401 endpoint just hasn't sent any more data (yet).
3402
3403 If the stream isn't open, then this method will always return @c true to
3404 implicitly indicate that there's no data pending to be received.
3405
3406 @pre
3407 You can optionally specify a timeout with the timeout parameter, which will
3408 cause this method to wait for the specified period of time for pending data.
3409
3410 It's better (more efficient) to use this method instead of the @c MSG_PEEK
3411 flag (with the various @c recv methods) to determine whether any data is
3412 waiting on the stream (e.g., data that's received by the sockets, but not by
3413 any @c recv methods yet) because this method is specialized in handling this
3414 particular condition and responds with an easy-to-use boolean flag.
3415
3416 @note
3417 EoS is an acronym for: End of Stream
3418
3419 @throws randolf::rex::xEBADF The underlying socket is not open
3420 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3421 part of the user address space
3422 @throws randolf::rex::xEINTR Interrupted by a signal
3423 @throws randolf::rex::xENOMEM Insufficient memory
3424 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3425 doesn't refer to a socket
3426 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
3427 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
3428 is a highly improbable chance that a timeout could still occur if the
3429 data is read by another thread before the `recv(..., MSG_PEEK)` call)
3430 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
3431 there's no new data
3432
3433 @returns TRUE = End of Stream (no buffered data, the stream @ref is_closed(),
3434 or there's no pending data, checked in this order)
3435 @returns FALSE = Pending data available
3436 @see is_closed
3437 @see is_open
3438 @see pending
3439 @see recv
3440 @qualifier TLS
3441 *///=========================================================================
3442 const bool eos(
3443 /// Number of milliseconds to wait@n
3444 /// 0 = no waiting (default)
3445 const int timeout = 0) {
3446
3447 // --------------------------------------------------------------------------
3448 // Check internal buffer first, if there is one, and return immediately if
3449 // the buffer is not empty.
3450 // --------------------------------------------------------------------------
3451 if (__buffer != nullptr && !__buffer->empty()) return false;
3452
3453 // --------------------------------------------------------------------------
3454 // If socket is closed, then just indicate EoS to be done with it. No need
3455 // to fall through to timeouts as this is not applicable.
3456 // --------------------------------------------------------------------------
3457 if (!__socket_open) return true;
3458
3459 // --------------------------------------------------------------------------
3460 // Start by checking whether OpenSSL has pending data, and if it doesn't (or
3461 // there's an error, which is also indicated by 0) then fall through to
3462 // checking the underlying socket using ioctl's FIONREAD...
3463 //
3464 // Use ioctl's FIONREAD to determine if any data is waiting to be read. If
3465 // ioctl returns non-zero, then an error occurred which always equates to
3466 // EoS (otherwise the FIONREAD operation would have been successful).
3467 // --------------------------------------------------------------------------
3468 if (__tls && SSL_has_pending(__tls_fd) == 1) return false;
3469 int n = 0; // Number of bytes waiting (default to 0 in case of ioctl error)
3470 ioctl(__socket_fd, FIONREAD, &n); // Ignore return code because "n" will be zero if there's an error
3471 if (n > 0) return false; // Indicate !EoS because 0 bytes remaining means EoS
3472
3473 // --------------------------------------------------------------------------
3474 // Timeout polling (if a timeout was specified).
3475 // --------------------------------------------------------------------------
3476 if (timeout != 0) {
3477 try {
3478 poll(POLLIN, timeout); // This is calling internally, and not ::poll (POSIX) directly
3479 return false; // EoS, because the "poll" method was triggered by new data
3480 } catch (const randolf::rex::xETIMEDOUT e) {
3481 return true; // EoS, because the "poll" method timed out while waiting for new data
3482 } // This is a specialized catch situation -- the ::recv function could still return an ETIMEDOUT error
3483 } // -x- if timeout -x-
3484
3485 return true; // EoS, because new data was not detected
3486 } // -x- bool eos -x-
3487
3488 /*======================================================================*//**
3489 @brief
3490 Find out what the specified IP address string's family (@c SO_DOMAIN) is.
3491 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
3492 address is not available on any local interface
3493 @throws randolf::rex::xEAI_NONAME If @c address is an empty string
3494 @returns @c AF_UNSPEC (if family couldn't be determined)
3495 @returns @c AF_INET (IPv4 address)
3496 @returns @c AF_INET6 (IPv6 address)
3497 @returns @c AF_UNIX (UNIX Domain address)
3498 @returns ...or other family as applicable
3499 @see socket_family()
3500 @see socket_protocol()
3501 @see socket_type()
3502 @qualifier TLS
3503 *///=========================================================================
3504 int static family(
3505 /// Address, similar to @ref bind() addressing, including non-standard "if="
3506 /// variant that names a network interface
3507 const std::string address,
3508 /// Preferred family to return first (used only with interface mode where the
3509 /// network interface is specified after the "if=" prefix); the default value
3510 /// of @c AF_UNSPEC will return the first family interface found
3511 const int preferred_family = AF_UNSPEC) {
3512
3513 // --------------------------------------------------------------------------
3514 // Simple checks first.
3515 // --------------------------------------------------------------------------
3516 if (address.size() == 0) randolf::rex::mk_exception("", EAI_NONAME);
3517 if (address.front() == '/') return AF_UNIX;
3518
3519 // --------------------------------------------------------------------------
3520 // if=<interface-name>: Same "interface" option that we support in bind()
3521 // --------------------------------------------------------------------------
3522 if (address.starts_with("if=")) {
3523 //std::cout << "address=" << address.substr(3).data() << std::endl; // Debug
3524 struct ifaddrs* ifaddr; // Chained interface addresses structure
3525 if (::getifaddrs(&ifaddr) == -1) { // Error occurred, so we're done here
3526 freeifaddrs(ifaddr); // Memory management
3527 randolf::rex::mk_exception("getifaddrs() error (prequisite to searching for " + address + ")", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
3528 } // -x- if getifaddrs -x
3529
3530 // --------------------------------------------------------------------------
3531 // Iterate through interface addresses. Each address should have a different
3532 // address family/domain, and is never AF_UNSPEC (this should never happen
3533 // because it wouldn't make sense, but if the network implementation was
3534 // broken and including an AF_UNSPEC family/domain, it would be useless for
3535 // the purposes of calling the ::socket() function so our code will just
3536 // ignore it anyway.
3537 // --------------------------------------------------------------------------
3538 int first_family = AF_UNSPEC; // We're using AF_UNSPEC (0) to indicate that there isn't a family/domain
3539 for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
3540 if (ifa->ifa_addr == nullptr) continue; // No address structure, so this isn't useful to us at all
3541 if (ifa->ifa_addr->sa_family == AF_UNSPEC) continue; // Skip AF_UNSPEC family/domain, as we can't use this
3542 if (address.compare(3, -1, ifa->ifa_name) != 0) continue; // Not the interface name we're looking for
3543
3544 // --------------------------------------------------------------------------
3545 // Return current address's family if preferred_family is not specified (if
3546 // it's set to AF_UNSPEC {0}), or if it matches the current address.
3547 // --------------------------------------------------------------------------
3548 //std::cout << "if=" << ifa->ifa_name << " family=" << ifa->ifa_addr->sa_family << std::endl; // Debug
3549 if (preferred_family == AF_UNSPEC || preferred_family == ifa->ifa_addr->sa_family) {
3550 first_family = ifa->ifa_addr->sa_family; // Arbitrarily re-using first_family variable
3551 //freeifaddrs(ifaddr); // Memory management
3552 //return first_family; // Return current family/domain (re-used first_family variable)
3553 break;
3554 } // -x- if preferred_family -x-
3555
3556 // --------------------------------------------------------------------------
3557 // Keep track of only the first_family/domain that we found; if we can't find
3558 // the preferred_family, then we'll be able to return the first one we found
3559 // once the current loop is exhausted without finding a preferred familiy
3560 // (this is, in effect, a fallback).
3561 // --------------------------------------------------------------------------
3562 if (first_family == AF_UNSPEC && ifa->ifa_addr->sa_family != AF_UNSPEC)
3563 first_family = ifa->ifa_addr->sa_family;
3564 //std::cout << " first_family=" << first_family << std::endl; // Debug
3565 } // -x- for *ifa -x-
3566
3567 // --------------------------------------------------------------------------
3568 // Return first_family/domain (or throw an exception if it wasn't found).
3569 // --------------------------------------------------------------------------
3570 freeifaddrs(ifaddr); // Memory management
3571 if (first_family == AF_UNSPEC) randolf::rex::mk_exception("Interface (" + address.substr(3) + ") doesn't exist" + (preferred_family == AF_UNSPEC ? "" : " or the preferred family/domain (" + std::to_string(preferred_family) + ") is not supported"), EADDRNOTAVAIL);
3572 return first_family;
3573
3574 } // -x- if /^if=/ -x-
3575
3576 // --------------------------------------------------------------------------
3577 // Attempt to detect IPv4 or IPv6 using a simple shortcut with ::inet_pton(),
3578 // which requires some additional memory allocation (that the simple checks
3579 // from earlier don't need).
3580 // --------------------------------------------------------------------------
3581 char buf[sizeof(struct in6_addr)]; // Binary address storage
3582 if (::inet_pton(AF_INET, address.data(), buf) == 1) return AF_INET;
3583 if (::inet_pton(AF_INET6, address.data(), buf) == 1) return AF_INET6;
3584
3585 // --------------------------------------------------------------------------
3586 // Throw xEAI_FAMILY exception because no family/domain was found.
3587 // --------------------------------------------------------------------------
3588 randolf::rex::mk_exception("No family/domain available for: " + address, EAI_FAMILY);
3589 } // -x- int family -x-
3590
3591 /*======================================================================*//**
3592 @brief
3593 Get peer name returns the address of the socket as a sockaddr_storage
3594 structure.
3595
3596 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
3597 that aids in the prevention of resource leaks).
3598
3599 @throws randolf::rex::xEBADF The underlying socket is not open
3600 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3601 part of the user address space
3602 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3603 valid for this socket's family (a.k.a., communication domain)
3604 @throws randolf::rex::xENOBUFS Insufficient memory
3605 @throws randolf::rex::xENOTCONN Underlying socket is not connected
3606 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3607 doesn't refer to a socket
3608
3609 @returns sockaddr_storage structure
3610 @qualifier POSIX
3611 @qualifier TLS
3612 *///=========================================================================
3613 const std::shared_ptr<sockaddr_storage> getpeername() {
3614 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3615 socklen_t len = sizeof(sockaddr_storage);
3616 __rc_check(::getpeername(__socket_fd, (struct sockaddr*)sa.get(), &len));
3617 return sa;
3618 } // -x- std::shared_ptr<sockaddr_storage> getpeername -x-
3619
3620 /*======================================================================*//**
3621 @brief
3622 Get peer name returns the address of the socket as a std::string object.
3623
3624 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3625 (e.g., because the @c family doesn't utilize or support an address
3626 {or the format isn't known}
3627 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3628 @throws randolf::rex::xEBADF The underlying socket is not open
3629 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3630 part of the user address space
3631 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3632 valid for this socket's family (a.k.a., communication domain)
3633 @throws randolf::rex::xENOBUFS Insufficient memory
3634 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3635 @throws randolf::rex::xENOTCONN Underlying socket is not connected
3636 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3637 doesn't refer to a socket
3638
3639 @returns string representation of peer name
3640 @qualifier TLS
3641 *///=========================================================================
3642 const std::string getpeername_ntop() {
3643 sockaddr_storage sa;
3644 socklen_t len = sizeof(sockaddr_storage);
3645 __rc_check(::getpeername(__socket_fd, (sockaddr*)&sa, &len));
3646 return inet_ntop(&sa);
3647 } // -x- std::string getpeername_ntop -x-
3648
3649 /*======================================================================*//**
3650 @brief
3651 Get specified "sockaddr_storage" structure's address as a "sockaddr"
3652 structure, for sockets in one of the supported families:
3653
3654 - AF_INET (IPv4)
3655 - AF_INET6 (IPv6)
3656 - AF_UNIX (Domain socket path)
3657 - AF_PACKET (Ethernet node/mac. address)
3658
3659 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3660 (e.g., because the @c family doesn't utilize or support an address
3661 {or the format isn't known}
3662
3663 @returns pointer to sockaddr structure within provided sockaddr_storage
3664 @see bind
3665 @see mk_sockaddr_storage
3666 @see recvfrom
3667 @see sendto
3668 @see sendzto
3669 @qualifier POSIX
3670 @qualifier TLS
3671 *///=========================================================================
3672 static sockaddr* getsockaddr(
3673 /// The @c sockaddr_storage structure wherein @c sockaddr will be referenced from
3674 const sockaddr_storage* sa) {
3675 switch (sa->ss_family) {
3676 case AF_INET: // IPv4 address
3677 return (sockaddr*)&(((struct sockaddr_in*)sa)->sin_addr);//->sin_addr);
3678 case AF_INET6: // IPv6 address
3679 return (sockaddr*)&(((struct sockaddr_in6*)sa)->sin6_addr);//->sin6_addr);
3680 case AF_UNIX: // UNIX (path) domain socket
3681 return (sockaddr*)&(((struct sockaddr_un*)sa)->sun_path);//->sun_path);
3682 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
3683 // case /*AF_LINK* /18: // Link layer interface (arp)
3684 // break;
3685 case AF_PACKET: // Packet (ethernet) address (packet capturing)
3686 return (sockaddr*)&(((struct sockaddr_ll*)sa)->sll_addr);//->sll_addr);
3687 } // -x- switch family -x-
3688 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY"); // Everything else
3689 } // -x- sockaddr* getsockaddr -x-
3690
3691 /*======================================================================*//**
3692 @brief
3693 Get socket name returns the address of the socket as a "sockaddr_storage"
3694 structure.
3695
3696 The resulting structure is wrapped in std::shared_ptr (a C++ smart pointer
3697 that aids in the prevention of resource leaks).
3698
3699 @throws randolf::rex::xEBADF The underlying socket is not open
3700 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3701 part of the user address space
3702 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3703 valid for this socket's family (a.k.a., communication domain)
3704 @throws randolf::rex::xENOBUFS Insufficient memory
3705 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3706 doesn't refer to a socket
3707
3708 @returns sockaddr_storage structure
3709 @qualifier POSIX
3710 @qualifier TLS
3711 *///=========================================================================
3712 const std::shared_ptr<sockaddr_storage> getsockname() {
3713 std::shared_ptr sa = std::make_shared<sockaddr_storage>();
3714 socklen_t len = sizeof(sockaddr_storage);
3715 __rc_check(::getsockname(__socket_fd, (struct sockaddr*)sa.get(), &len));
3716 return sa;
3717 } // -x- std::string getsockname -x-
3718
3719 /*======================================================================*//**
3720 @brief
3721 Get socket name returns the name of the socket as a std::string object.
3722
3723 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
3724 (e.g., because the @c family doesn't utilize or support an address
3725 {or the format isn't known}
3726 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
3727 @throws randolf::rex::xEBADF The underlying socket is not open
3728 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3729 part of the user address space
3730 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3731 valid for this socket's family (a.k.a., communication domain)
3732 @throws randolf::rex::xENOBUFS Insufficient memory
3733 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
3734 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3735 doesn't refer to a socket
3736
3737 @returns string representation of socket name
3738 @qualifier TLS
3739 *///=========================================================================
3740 const std::string getsockname_ntop() {
3741 sockaddr_storage sa;
3742 socklen_t len = sizeof(sockaddr_storage);
3743 __rc_check(::getsockname(__socket_fd, (sockaddr*)&sa, &len));
3744 return inet_ntop(&sa);
3745 } // -x- std::string getsockname_ntop -x-
3746
3747 /*======================================================================*//**
3748 @brief
3749 Get socket option details in the form of an integer.
3750
3751 Most options return an integer, with the remaining options returning a
3752 pointer to a structure wrapped in a std::shared_ptr (a C++ smart pointer that
3753 aids in the prevention of resource leaks); the primitive types int, u_int,
3754 and u_char are not wrapped in C++ smart pointers because returning them by
3755 value is more efficient since allocating memory for an entire structure isn't
3756 needed.
3757
3758 @post
3759 It is up to the developer to know which return type is needed according to
3760 the socket option, otherwise an exception will likely be thrown -- in some
3761 cases where the wrong type will seem to work, this is due to the wrong type
3762 providing a minimally sufficient amount of memory for the storage of the
3763 resulting structure.
3764
3765 @par Notes
3766 The returned values/structures are not marked as "const" because they may
3767 need to be modified for unforseen purposes. Modifying the returend values or
3768 structures is fine because they are intended to be independent and are
3769 expected to have no direct impact on the rsocket's internal variables and
3770 structures.
3771
3772 Templates in C++ aren't used here because they don't work properly for our
3773 needs due to neccesity to handle both fundamental types and structures; it
3774 turns out that mixing these is impossible when using the same function name,
3775 so this just doesn't work as well as we'd like it to. (We may try to work on
3776 this again in the future as time permits to provide an additional method for
3777 obtaining socket options, but with the intention of never removing this
3778 current set of methods so as to ensure backward compatibility in the future.)
3779
3780 @throws randolf::rex::xEBADF The underlying socket is not open
3781 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3782 part of the user address space
3783 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
3784 valid for this socket's family (a.k.a., communication domain)
3785 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
3786 is not supported
3787 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3788 doesn't refer to a socket
3789
3790 @returns socket option value
3791 @see setsockopt(const int, const int)
3792 @see setsockopt(const int, const int, const int)
3793 @qualifier TLS
3794 *///=========================================================================
3795 int getsockopt_int(
3796 /// The level at which the option resides; typically @c SOL_SOCKET
3797 const int level,
3798 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3799 const int option) {
3800 int value = 0;
3801 socklen_t slt{sizeof(value)};
3802 __rc_check(::getsockopt(__socket_fd, level, option, &value, &slt));
3803 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3804 return value;
3805 } // -x- int getsockopt_int -x-
3806
3807 /*======================================================================*//**
3808 @brief
3809 Get socket option details in the form of an unsigned integer.
3810 @copydetails getsockopt_int(const int, const int)
3811 @returns socket option value
3812 @see setsockopt(const int, const int, const u_int)
3813 @qualifier TLS
3814 *///=========================================================================
3815 u_int getsockopt_u_int(
3816 /// The level at which the option resides; typically @c SOL_SOCKET
3817 const int level,
3818 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3819 const int option) {
3820 u_int value = 0;
3821 socklen_t slt{sizeof(value)};
3822 __rc_check(::getsockopt(__socket_fd, level, option, &value, &slt));
3823 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3824 return value;
3825 } // -x- u_int getsockopt_u_int -x-
3826
3827 /*======================================================================*//**
3828 @brief
3829 Get socket option details in the form of an unsigned character.
3830 @copydetails getsockopt_int(const int, const int)
3831 @returns socket option value
3832 @see setsockopt(const int, const int, const u_char)
3833 @qualifier TLS
3834 *///=========================================================================
3835 u_char getsockopt_u_char(
3836 /// The level at which the option resides; typically @c SOL_SOCKET
3837 const int level,
3838 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3839 const int option) {
3840 u_char value = 0;
3841 socklen_t slt{sizeof(value)};
3842 __rc_check(::getsockopt(__socket_fd, level, option, &value, &slt));
3843 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
3844 return value;
3845 } // -x- u_char getsockopt_u_char -x-
3846
3847 /*======================================================================*//**
3848 @brief
3849 Get socket option details in the form of a structure.
3850 @copydetails getsockopt_int(const int, const int);
3851 @returns socket option structure wrapped in std::shared_ptr
3852 @see setsockopt(const int, const int, const linger&)
3853 @qualifier TLS
3854 *///=========================================================================
3855 std::shared_ptr<linger> getsockopt_linger(
3856 /// The level at which the option resides; typically @c SOL_SOCKET
3857 const int level,
3858 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3859 const int option) {
3860 std::shared_ptr value = std::make_shared<linger>();
3861 socklen_t slt{sizeof(value)};
3862 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3863 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3864 return value;
3865 } // -x- linger getsockopt_linger -x-
3866
3867 /*======================================================================*//**
3868 @copydoc getsockopt_linger(const int, const int)
3869 @see setsockopt(const int, const int, const timeval&)
3870 @qualifier TLS
3871 *///=========================================================================
3872 std::shared_ptr<timeval> getsockopt_timeval(
3873 /// The level at which the option resides; typically @c SOL_SOCKET
3874 const int level,
3875 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3876 const int option) {
3877 std::shared_ptr value = std::make_shared<timeval>();
3878 socklen_t slt{sizeof(value)};
3879 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3880 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3881 return value;
3882 } // -x- timeval getsockopt_timeval -x-
3883
3884 /*======================================================================*//**
3885 @copydoc getsockopt_linger(const int, const int)
3886 @see setsockopt(const int, const int, const in_addr&)
3887 @qualifier TLS
3888 *///=========================================================================
3889 std::shared_ptr<in_addr> getsockopt_in_addr(
3890 /// The level at which the option resides; typically @c SOL_SOCKET
3891 const int level,
3892 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3893 const int option) {
3894 std::shared_ptr value = std::make_shared<in_addr>();
3895 socklen_t slt{sizeof(value)};
3896 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3897 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3898 return value;
3899 } // -x- in_addr getsockopt_in_addr -x-
3900
3901 /*======================================================================*//**
3902 @copydoc getsockopt_linger(const int, const int)
3903 @see setsockopt(const int, const int, const ip_mreq&)
3904 @qualifier TLS
3905 *///=========================================================================
3906 std::shared_ptr<ip_mreq> getsockopt_ip_mreq(
3907 /// The level at which the option resides; typically @c SOL_SOCKET
3908 const int level,
3909 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3910 const int option) {
3911 std::shared_ptr value = std::make_shared<ip_mreq>();
3912 socklen_t slt{sizeof(value)};
3913 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3914 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3915 return value;
3916 } // -x- ip_mreq getsockopt_ip_mreq -x-
3917
3918 /*======================================================================*//**
3919 @copydoc getsockopt_linger(const int, const int)
3920 @see setsockopt(const int, const int, const ip_mreq_source&)
3921 @qualifier TLS
3922 *///=========================================================================
3923 std::shared_ptr<ip_mreq_source> getsockopt_ip_mreq_source(
3924 /// The level at which the option resides; typically @c SOL_SOCKET
3925 const int level,
3926 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3927 const int option) {
3928 std::shared_ptr value = std::make_shared<ip_mreq_source>();
3929 socklen_t slt{sizeof(value)};
3930 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3931 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3932 return value;
3933 } // -x- ip_mreq_source getsockopt_ip_mreq_source -x-
3934
3935 /*======================================================================*//**
3936 @copydoc getsockopt_linger(const int, const int)
3937 @see setsockopt(const int, const int, const icmp6_filter&)
3938 @qualifier TLS
3939 *///=========================================================================
3940 std::shared_ptr<icmp6_filter> getsockopt_icmp6_filter(
3941 /// The level at which the option resides; typically @c SOL_SOCKET
3942 const int level,
3943 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3944 const int option) {
3945 std::shared_ptr value = std::make_shared<icmp6_filter>();
3946 socklen_t slt{sizeof(value)};
3947 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3948 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3949 return value;
3950 } // -x- icmp6_filter getsockopt_icmp6_filter -x-
3951
3952 /*======================================================================*//**
3953 @copydoc getsockopt_linger(const int, const int)
3954 @see setsockopt(const int, const int, const sockaddr_in6&)
3955 @qualifier TLS
3956 *///=========================================================================
3957 std::shared_ptr<sockaddr_in6> getsockopt_sockaddr_in6(
3958 /// The level at which the option resides; typically @c SOL_SOCKET
3959 const int level,
3960 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3961 const int option) {
3962 std::shared_ptr value = std::make_shared<sockaddr_in6>();
3963 socklen_t slt{sizeof(value)};
3964 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3965 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3966 return value;
3967 } // -x- sockaddr_in6 getsockopt_sockaddr_in6 -x-
3968
3969 /*======================================================================*//**
3970 @copydoc getsockopt_linger(const int, const int)
3971 @see setsockopt(const int, const int, const ip6_mtuinfo&)
3972 @qualifier TLS
3973 *///=========================================================================
3974 std::shared_ptr<ip6_mtuinfo> getsockopt_ip6_mtuinfo(
3975 /// The level at which the option resides; typically @c SOL_SOCKET
3976 const int level,
3977 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3978 const int option) {
3979 std::shared_ptr value = std::make_shared<ip6_mtuinfo>();
3980 socklen_t slt{sizeof(value)};
3981 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3982 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
3983 return value;
3984 } // -x- ip6_mtuinfo getsockopt_ip6_mtuinfo -x-
3985
3986 /*======================================================================*//**
3987 @copydoc getsockopt_linger(const int, const int)
3988 @see setsockopt(const int, const int, const ipv6_mreq&)
3989 @qualifier TLS
3990 *///=========================================================================
3991 std::shared_ptr<ipv6_mreq> getsockopt_ipv6_mreq(
3992 /// The level at which the option resides; typically @c SOL_SOCKET
3993 const int level,
3994 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3995 const int option) {
3996 std::shared_ptr value = std::make_shared<ipv6_mreq>();
3997 socklen_t slt{sizeof(value)};
3998 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
3999 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4000 return value;
4001 } // -x- ipv6_mreq getsockopt_ipv6_mreq -x-
4002
4003 /*======================================================================*//**
4004 @copydoc getsockopt_linger(const int, const int)
4005 @see setsockopt(const int, const int, const group_req&)
4006 @qualifier TLS
4007 *///=========================================================================
4008 std::shared_ptr<group_req> getsockopt_group_req(
4009 /// The level at which the option resides; typically @c SOL_SOCKET
4010 const int level,
4011 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4012 const int option) {
4013 std::shared_ptr value = std::make_shared<group_req>();
4014 socklen_t slt{sizeof(value)};
4015 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4016 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4017 return value;
4018 } // -x- group_req getsockopt_group_req -x-
4019
4020 /*======================================================================*//**
4021 @copydoc getsockopt_linger(const int, const int)
4022 @see setsockopt(const int, const int, const group_source_req&)
4023 @qualifier TLS
4024 *///=========================================================================
4025 std::shared_ptr<group_source_req> getsockopt_group_source_req(
4026 /// The level at which the option resides; typically @c SOL_SOCKET
4027 const int level,
4028 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4029 const int option) {
4030 std::shared_ptr value = std::make_shared<group_source_req>();
4031 socklen_t slt{sizeof(value)};
4032 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4033 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4034 return value;
4035 } // -x- group_source_req getsockopt_group_source_req -x-
4036
4037 /*======================================================================*//**
4038 @copydoc getsockopt_linger(const int, const int)
4039 @see setsockopt(const int, const int)
4040 @see setsockopt(const int, const int, const int)
4041 @qualifier TLS
4042 *///=========================================================================
4043 template<class T> std::shared_ptr<T> getsockopt_other(
4044 /// The level at which the option resides; typically @c SOL_SOCKET
4045 const int level,
4046 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4047 const int option) {
4048 std::shared_ptr value = std::make_shared<T>();
4049 socklen_t slt{sizeof(value)};
4050 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4051 if (__debug) __debug_sockopt_other("getsockopt_other(socket{0x", level, option, socklen_t(sizeof(value)));
4052 return value;
4053 } // -x- T getsockopt_other -x-
4054
4055 /*======================================================================*//**
4056 @brief
4057 Get underlying socket's address as a std::string, for sockets in one of the
4058 supported families:
4059
4060 - AF_INET (IPv4)
4061 - AF_INET6 (IPv6)
4062 - AF_UNIX (Domain socket path)
4063 - AF_PACKET (Ethernet node/mac. address)
4064
4065 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
4066 (e.g., because the @c family doesn't utilize or support an address
4067 {or the format isn't known}
4068 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
4069 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
4070
4071 @returns string representation of underlying socket's address
4072 @see inet_ntop(sockaddr_storage*) static method
4073 @qualifier POSIX
4074 @qualifier TLS
4075 *///=========================================================================
4076// const std::string inet_ntop() { return inet_ntop((sockaddr_storage*)&__socket_addr); } // -x- std::string inet_ntop -x-
4077 const std::string inet_ntop() { return inet_ntop((sockaddr_storage*)__socket_addr); } // -x- std::string inet_ntop -x-
4078
4079 /*======================================================================*//**
4080 @brief
4081 Get specified "sockaddr_storage" structure's address as a std::string, for
4082 sockets in one of the supported families:
4083
4084 - AF_INET (IPv4)
4085 - AF_INET6 (IPv6)
4086 - AF_UNIX (Domain socket path)
4087 - AF_PACKET (Ethernet node/mac. address)
4088
4089 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
4090 (e.g., because the @c family doesn't utilize or support an address
4091 {or the format isn't known}
4092 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
4093 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
4094
4095 @returns string representation of underlying socket's address
4096 @see inet_ntop() non-static method
4097 @qualifier POSIX
4098 @qualifier TLS
4099 *///=========================================================================
4100 static const std::string inet_ntop(
4101 /// Source structure that [should] contain address data
4102 sockaddr_storage* sa) {
4103 std::string str;
4104 switch (sa->ss_family) {
4105 case AF_INET: // IPv4 address
4106 {
4107 char ntop[sizeof(sockaddr_in)]{0};
4108 const char* rc = ::inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), ntop, sizeof(ntop));
4109// TODO: if (rc == nullptr) _rc...
4110 str = std::string(ntop);
4111 }
4112 break;
4113 case AF_INET6: // IPv6 address
4114 { // Debug
4115 char ntop[sizeof(sockaddr_in6)]{0};
4116 const char* rc = ::inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), ntop, sizeof(ntop));
4117// TODO: if (rc == nullptr) _rc...
4118 str = std::string(ntop);
4119 }
4120 break;
4121 case AF_UNIX: // UNIX (path) domain socket
4122 str = std::string(((struct sockaddr_un *)sa)->sun_path);
4123 break;
4124 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
4125 // case /*AF_LINK* /18: // Link layer interface (arp)
4126 // break;
4127 case AF_PACKET: // Packet (ethernet) address (packet capturing)
4128 str = to_mac(((struct sockaddr_ll *)sa)->sll_addr);
4129 break;
4130 default: // Everything else
4131 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY");
4132 } // -x- switch family -x-
4133 return str;
4134 } // -x- std::string inet_ntop -x-
4135
4136 /*======================================================================*//**
4137 @brief
4138 Find out whether an internal read buffer was allocated (this is most likely
4139 triggered by an attempt to read a line of text).
4140 @note
4141 The buffer_size() methods report on how much memory was allocated for the
4142 internal read buffer or to set its size (in bytes).
4143 @returns TRUE = an internal read buffer was allocated
4144 @returns FALSE = an internal read buffer was not allocated
4145 @see buffer_size
4146 @see buffer_size(const size_t nbytes)
4147 @qualifier TLS
4148 *///=========================================================================
4149 const bool is_buffered() noexcept { return __buffer != nullptr; } // -x- bool is_buffered -x-
4150
4151 /*======================================================================*//**
4152 @brief
4153 Find out whether the underlying socket is not open (which may not be the same
4154 as specifically "closed" since a newly instantiated empty socket begins in a
4155 "not open" state despite the underlying socket not explicitly having been
4156 closed).
4157 @returns TRUE = not open
4158 @returns FALSE = open
4159 @see is_closed
4160 @see is_open
4161 @qualifier TLS
4162 *///=========================================================================
4163 const bool is_closed() noexcept { return !__socket_open; } // -x- bool is_closed -x-
4164
4165 /*======================================================================*//**
4166 @brief
4167 Find out whether the underlying socket is connected with/to an endpoint.
4168 @returns TRUE = open
4169 @returns FALSE = not open
4170 @see connect
4171 @see is_closed
4172 @see is_open
4173 @qualifier TLS
4174 *///=========================================================================
4175 const bool is_connected() noexcept { return __socket_connected; } // -x- bool is_connected -x-
4176
4177 /*======================================================================*//**
4178 @brief
4179 Find out whether the default byte order for this host is LSB (small endian).
4180 @note
4181 If you're trying to choose which endian type to use when designing a new
4182 internet protocol, then big endian is normally the better option. However,
4183 if your new protocol will only be used by hardware that all share the same
4184 endianness, then that endianness is probably the more optimal option since it
4185 will translate to an overall lesser consumption of CPU cycles by reducing or
4186 eliminating endianness conversions.
4187 @returns TRUE = LSB (little endian)
4188 @returns FALSE = MSB (big endian / network byte order)
4189 @see is_endian_msb
4190 @qualifier TLS
4191 *///=========================================================================
4192 const bool is_endian_lsb() noexcept { return !__endian_is_msb; } // -x- bool is_endian_lsb -x-
4193
4194 /*======================================================================*//**
4195 @brief
4196 Find out whether the default byte order for this host is MSB (big endian).
4197 @note
4198 Big endian is the standard known as "network byte order" that's also used in
4199 various header fields in internet packets.
4200 @n@n
4201 If you're trying to choose which endian type to use when designing a new
4202 internet protocol, then big endian is normally the better option. However,
4203 if your new protocol will only be used by hardware that all share the same
4204 endianness, then that endianness is probably the more optimal option since it
4205 will translate to an overall lesser consumption of CPU cycles by reducing or
4206 eliminating endianness conversions.
4207 @returns TRUE = MSB (big endian / network byte order)
4208 @returns FALSE = LSB (little endian)
4209 @see is_endian_lsb
4210 @qualifier TLS
4211 *///=========================================================================
4212 const bool is_endian_msb() noexcept { return __endian_is_msb; } // -x- bool is_endian_msb -x-
4213
4214 /*======================================================================*//**
4215 @brief
4216 Find out if the EoL adoption policy is enabled for the @ref recvline() method
4217 (see the @ref eol_adoption method to find out how the dynamically-detected
4218 EoL sequence gets adopted, and under what conditions).
4219 @returns TRUE = EoL adoption is enabled
4220 @returns FALSE = EoL adoption is disabled
4221 @see eol
4222 @see eol_adoption
4223 @see eol_index
4224 @see recvline
4225 @see sendline
4226 @see send_eol
4227 @qualifier TLS
4228 *///=========================================================================
4229 const bool is_eol_adoption() noexcept {
4230 return __eol_adoption;
4231 } // -x- bool is_eol_adoption -x-
4232
4233 /*======================================================================*//**
4234 @brief
4235 Find out if the EoL substitution policy is enabled for the @ref printf(),
4236 @ref printfline(), @ref vprintf(), and @ref vprintfline() methods.
4237 @returns TRUE = EoL substitution is enabled
4238 @returns FALSE = EoL substitution is disabled
4239 @see eol
4240 @see eol_fix_printf
4241 @see printf
4242 @see printfline
4243 @see sendline
4244 @see send_eol
4245 @see vprintf
4246 @see vprintfline
4247 @qualifier TLS
4248 *///=========================================================================
4249 const bool is_eol_fix_printf() noexcept {
4250 return __eol_fix_printf;
4251 } // -x- bool is_eol_fix_printf -x-
4252
4253 /*======================================================================*//**
4254 @brief
4255 Find out whether the underlying socket is open.
4256 @returns TRUE = open
4257 @returns FALSE = not open
4258 @see is_closed()
4259 @qualifier TLS
4260 *///=========================================================================
4261 const bool is_open() noexcept { return __socket_open; } // -x- bool is_open -x-
4262
4263 /*======================================================================*//**
4264 @brief
4265 Find out whether encrypted communications is enabled or disabled.
4266 @returns TRUE = encrypted communications is enabled
4267 @returns FALSE = encrypted communications is disabled
4268 @see tls(bool, TLS_FLAGS)
4269 @see is_tls_sni
4270 @qualifier TLS
4271 *///=========================================================================
4272 const bool is_tls() noexcept { return __tls; } // -x- bool is_tls -x-
4273
4274 /*======================================================================*//**
4275 @brief
4276 Find out whether TLS context is in TLS_CLIENT mode.
4277 @returns TRUE = TLS context is in TLS_CLIENT mode
4278 @returns FALSE = TLS context is in TLS_SERVER mode
4279 @see TLS_CLIENT
4280 @see tls()
4281 @qualifier TLS
4282 *///=========================================================================
4283 const bool is_tls_client_mode() noexcept { return !__tls_server_mode; } // -x- bool is_tls_client_mode -x-
4284
4285 /*======================================================================*//**
4286 @brief
4287 Find out whether egress from encryption (to unencrypted mode) is allowed.
4288 @returns TRUE = egress from encrypted communications is allowed
4289 @returns FALSE = egress from encrypted communications is not allowed
4290 @see TLS_NO_EGRESS
4291 @see tls()
4292 @qualifier TLS
4293 *///=========================================================================
4294 const bool is_tls_egress_okay() noexcept { return __tls_egress; } // -x- bool is_tls_egress_okay -x-
4295
4296 /*======================================================================*//**
4297 @brief
4298 Find out whether encrypted communications is exclusive.
4299 @returns TRUE = encrypted communications is exclusive
4300 @returns FALSE = encrypted communications is not exclusive
4301 @see tls()
4302 @qualifier TLS
4303 *///=========================================================================
4304 const bool is_tls_exclusive() noexcept { return __tls_exclusive; } // -x- bool is_tls_exclusive -x-
4305
4306 /*======================================================================*//**
4307 @brief
4308 Find out whether ingress to encryption (from unencrypted mode) is allowed.
4309 @returns TRUE = ingress to encrypted communications is allowed
4310 @returns FALSE = ingress to encrypted communications is not allowed
4311 @see TLS_NO_INGRESS
4312 @see tls()
4313 @qualifier TLS
4314 *///=========================================================================
4315 const bool is_tls_ingress_okay() noexcept { return __tls_ingress; } // -x- bool is_tls_ingress_okay -x-
4316
4317 /*======================================================================*//**
4318 @brief
4319 Find out whether TLS context is in TLS_SERVER mode.
4320 @returns TRUE = TLS context is in TLS_SERVER mode
4321 @returns FALSE = TLS context is in TLS_CLIENT mode
4322 @see TLS_SERVER
4323 @see tls()
4324 @qualifier TLS
4325 *///=========================================================================
4326 const bool is_tls_server_mode() noexcept { return __tls_server_mode; } // -x- bool is_tls_server_mode -x-
4327
4328 /*======================================================================*//**
4329 @brief
4330 Find out whether SNI (Server Name Identifier) is enabled (configured, which
4331 implies that an internal callback function was also set up).
4332 @returns TRUE = SNI is enabled
4333 @returns FALSE = SNI is disabled
4334 @see is_tls
4335 @see tls_sni()
4336 @qualifier TLS
4337 *///=========================================================================
4338 const bool is_tls_sni() noexcept { return __tls_sni != nullptr; } // -x- bool is_tls_sni -x-
4339
4340 /*======================================================================*//**
4341 @brief
4342 Find out whether SNI (Server Name Identifier) received an empty hostname or
4343 if no hostname was provided.
4344 @returns TRUE = SNI hostname was received and it was not empty
4345 @returns FALSE = SNI hostname was not received or it was empty
4346 @see is_tls_sni_match()
4347 @see name_sni()
4348 @qualifier TLS
4349 *///=========================================================================
4350 const bool is_tls_sni_has_name() noexcept { return __tls_sni_has_name; } // -x- bool is_tls_sni_has_name -x-
4351
4352 /*======================================================================*//**
4353 @brief
4354 Find out whether SNI (Server Name Identifier) was matched, which means that
4355 we're using one of the supplementary TLS certificates that are included in
4356 the associated @ref rsocket_sni object as separate TLS contexts.
4357
4358 When this method returns @c TRUE, it means the @c OpenSSL callback (which
4359 occurs during the TLS handshake process) completed the TLS handshake with one
4360 of the TLS certificates that's included in this @c rsocket's @ref rsocket_sni
4361 object instead of the primary TLS certificate assigned to this @c rsocket,
4362 and this @c rsocket is using the respective TLS context instead of the
4363 primary (default) one.
4364 @returns TRUE = SNI was matched
4365 @returns FALSE = SNI wasn't matched
4366 @see name_sni
4367 @see tls_sni
4368 @see tls_sni_has_name
4369 @qualifier TLS
4370 *///=========================================================================
4371 const bool is_tls_sni_match() noexcept { return __tls_sni_match; } // -x- bool is_tls_sni_match -x-
4372
4373 /*======================================================================*//**
4374 @brief
4375 Enable listening mode for this rsocket to prepare it to accept() new inbound
4376 connections.
4377
4378 The backlog defaults to SOMAXCONN (4096 on Linux, and 128 on older systems),
4379 which is common on most systems. If a higher value is supplied that exceeds
4380 @c kern.somaxconn, the kernel will automatically override the backlog with
4381 the value stored in @c kern.somaxconn without generating any errors or
4382 warnings.
4383
4384 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
4385 @throws randolf::rex::xEADDRINUSE No ephemeral ports are available for
4386 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
4387 but it really isn't)
4388 @throws randolf::rex::xEBADF The underlying socket is not open
4389 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4390 doesn't refer to a socket
4391 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
4392 @returns The same rsocket object so as to facilitate stacking
4393 @see accept()
4394 @see accept_sp()
4395 @see accept4()
4396 @see accept4_sp()
4397 @see backlog()
4398 @see bind()
4399 @qualifier POSIX
4400 @qualifier TLS
4401 *///=========================================================================
4402 rsocket* listen(
4403 /// Backlog queue size (0 = uses rsocket's default; see @ref backlog for more
4404 /// details about this); specifying a non-zero backlog also updates rocket's
4405 /// internal default (SOMAXCONN is 4096 on Linux, and 128 on older systems)
4406 int backlog = 0) {
4407 if (__debug) debug("listen(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4408 + ", " + std::to_string(backlog)
4409 + ");");
4410
4411 // --------------------------------------------------------------------------
4412 // Use rsocket's default backlog if not specified, otherwise update it
4413 // with the new value specified here.
4414 // --------------------------------------------------------------------------
4415 if (backlog = 0) backlog = __socket_backlog;
4416 else __socket_backlog = backlog;
4417
4418 // --------------------------------------------------------------------------
4419 // Configure underlying socket to queue up to "backlog" inbound connections.
4420 // --------------------------------------------------------------------------
4421 __rc_check(::listen(__socket_fd, backlog));
4422
4423 return this;
4424 } // -x- rsocket* listen -x-
4425
4426 /*======================================================================*//**
4427 @brief
4428 Convert an IPv4 address, IPv6 address, ethernet packet, or UNIX domain
4429 socket to a sockaddr_storage structure.
4430
4431 If service_name is an absolute path (that begins with a "/" charcter) and the
4432 family is set to AF_UNSPEC (the default), then the resulting family will be
4433 set to AF_UNIX.
4434
4435 @par Notes
4436 This method utilizes the results of getaddrinfo().
4437
4438 Other families like AF_LINK and AF_PACKET should work, but haven't been
4439 tested thoroughly. The additional support we provide for IPv4 and IPv6
4440 addresses is to copy the port number into the resulting structure (as a
4441 convenience).
4442
4443 @post
4444 The resulting sockaddr_storage structure is wrapped in std::shared_ptr (a C++
4445 smart pointer that aids in the prevention of resource leaks).
4446
4447 @par Threads
4448 This method is thread-safe.
4449
4450 @throws randolf::rex::xEAI_ADDRFAMILY If specified network host doesn't have
4451 any addresses in the specified address family
4452 @throws randolf::rex::xEAI_AGAIN Temporary failure code from DNS server (try
4453 again later)
4454 @throws randolf::rex::xEAI_BADFLAGS Invalid flags in @c hints.ai_flags (or
4455 `hints.ai_flags` included @c AI_CANONNAME with @c nullptr as @c name)
4456 @throws randolf::rex::xEAI_FAIL Permanent failure code from DNS server
4457 @throws randolf::rex::xEAI_FAMILY The specified family is not supported
4458 @throws randolf::rex::xEAI_MEMORY Out of memory
4459 @throws randolf::rex::xEAI_NONAME If node_name is nullptr or an empty string
4460 @throws randolf::rex::xEAI_SERVICE The specified service is not available
4461 for the specified socket type
4462 @throws randolf::rex::xEAI_SOCKTYPE The specified socket type is not
4463 supported
4464 @throws randolf::rex::xEAI_SYSTEM Other system error (use errno to determine
4465 what the error is, then run use @ref randolf::rex::rex::mk_exception
4466 to throw the correct exception)
4467
4468 @returns sockaddr_storage structure
4469 @see getsockaddr
4470 *///=========================================================================
4471// static std::shared_ptr<sockaddr_storage> mk_sockaddr_storage(
4472 static struct sockaddr_storage* mk_sockaddr_storage(
4473 /// IP address or UNIX domain socket address to convert
4474 const char* node_name,
4475 /// Port number (or service name used by some other families)
4476 const char* service_name = nullptr,
4477 /// Optional pointer to a helpful addrinfo structure
4478 const addrinfo* hints = nullptr) {
4479
4480 // --------------------------------------------------------------------------
4481 // Initial sanity checks.
4482 // --------------------------------------------------------------------------
4483 if (node_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME); // if (node_name == nullptr && service_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME);
4484
4485 // --------------------------------------------------------------------------
4486 // Handle any special cases.
4487 // --------------------------------------------------------------------------
4488 switch (hints == nullptr ? AF_UNSPEC : hints->ai_family) { // Default to AF_UNSPEC in the absence of hints
4489 case AF_UNSPEC:
4490 if (node_name[0] != '/') break;
4491 // Next entry MUST be "case AF_UNIX" for this fall-through to work
4492 case AF_UNIX:
4493 {
4494 // For some unknown reason, clang++ can't compile this line that
4495 // g++ has absolutely no trouble with at all:
4496 // std::shared_ptr sa = std::make_shared<sockaddr_storage>(AF_UNIX);
4497 // So, after wasting more than a year trying to figure out what the
4498 // hundreds of lines of cryptic errors meant, I eventually gave up
4499 // on clang++ for being such a dumbfuck, and used this work-around:
4500 struct sockaddr_storage* sa = (struct sockaddr_storage*)::malloc(sizeof(sockaddr_storage));
4501 sa->ss_family = AF_UNIX;
4502// std::shared_ptr sa = std::make_shared<sockaddr_storage>();
4503// sa->ss_family = AF_UNIX;
4504 std::strcpy(((struct sockaddr_un *)sa)->sun_path, node_name); // Copy path
4505 return sa;
4506 }
4507 break;
4508 } // -x- switch hints->family -x-
4509
4510 // --------------------------------------------------------------------------
4511 // Acquire addrinfo[] linked-list array.
4512 // --------------------------------------------------------------------------
4513 addrinfo* __addr_result = nullptr; // This is temporary
4514//std::cout << " Making socket structure... node_name=" << node_name << " ...hints: ai_family=" << hints->ai_family << " ai_socktype=" << hints->ai_socktype << " ai_flags=" << hints->ai_flags << std::endl;
4515 randolf::rex::mk_exception("", getaddrinfo(node_name,
4516 service_name,
4517 hints,
4518 &__addr_result));
4519
4520 // --------------------------------------------------------------------------
4521 // Find first valid addrinfo[] array by trying to open a socket with it.
4522 // --------------------------------------------------------------------------
4523 for (addrinfo* ar = __addr_result; ar != nullptr; ar = ar->ai_next) {
4524//std::cout << " ... ai_family=" << ar->ai_family << " ai_socktype=" << ar->ai_socktype << " ai_flags=" << ar->ai_flags << std::endl; // *******************
4525// ***************************** TODO: Make sure this loop is working properly
4526 } // -x- for ar -x-
4527//std::cout << " !!! ai_family=" << __addr_result->ai_family << std::endl; // *******************
4528
4529 // --------------------------------------------------------------------------
4530 // Copy the address to "sa" and return it. The "addr" data for IPv4 and IPv6
4531 // addresses include the TCP/UDP port number (service) in first two bytes as
4532 // as an unsigned integer (u_int16), followed immediately by the IP address.
4533 //
4534 // We're taking the first record only. We assume that hints{} is defined on
4535 // an as-needed basis.
4536 //
4537 // Note: AF_LINK and AF_PACKET are not widely supported, but we've made an
4538 // effort to accomodate their usage.
4539 // --------------------------------------------------------------------------
4540 struct sockaddr_storage* sa = (struct sockaddr_storage*)::malloc(sizeof(sockaddr_storage));
4541 //std::shared_ptr sa = std::make_shared<sockaddr_storage>();
4542 switch (__addr_result->ai_family) {
4543 case AF_INET: // IPv4 address
4544 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_in));
4545 break;
4546 case AF_INET6: // IPv6 address
4547 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_in6));
4548 break;
4549 /* // We're handling this earlier as a special case: TODO: Move special handling to here
4550 case AF_UNIX: // UNIX (path) domain socket
4551 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_un));
4552 break;
4553 */
4554 case /*AF_LINK*/18: // Link layer interface (arp)
4555 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_dl));
4556 break;
4557 case AF_PACKET: // Packet (ethernet) address (packet capturing)
4558 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_ll));
4559 break;
4560 default: // Everything else
4561 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_storage));
4562 } // -x- switch family -x-
4563 freeaddrinfo(__addr_result); // We don't need this anymore
4564 return sa;
4565 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
4566
4567 /*======================================================================*//**
4568 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
4569 *///=========================================================================
4570 static struct sockaddr_storage* mk_sockaddr_storage(
4571 /// IP address or UNIX domain socket address to convert
4572 const char* node_name,
4573 /// Port number
4574 const u_int16_t service_name,
4575 /// Optional pointer to a helpful addrinfo structure
4576 const addrinfo* hints = nullptr) {
4577 std::string port = std::to_string(service_name);
4578 return mk_sockaddr_storage(node_name, port.data(), hints);
4579 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
4580
4581 /*======================================================================*//**
4582 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
4583 *///=========================================================================
4584 static struct sockaddr_storage* mk_sockaddr_storage(
4585 /// IP address or UNIX domain socket address to convert
4586 const std::string node_name,
4587 /// Port number
4588 const u_int16_t service_name,
4589 /// Optional pointer to a helpful addrinfo structure
4590 const addrinfo* hints = nullptr) {
4591 std::string port = std::to_string(service_name);
4592 return mk_sockaddr_storage(node_name.data(), port.data(), hints);
4593 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
4594
4595 /*======================================================================*//**
4596 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
4597 *///=========================================================================
4598 static struct sockaddr_storage* mk_sockaddr_storage(
4599 /// IP address or UNIX domain socket address to convert
4600 const std::string node_name,
4601 /// Port number (or server name used by some other families)
4602 const std::string service_name,
4603 /// Optional pointer to a helpful addrinfo structure
4604 const addrinfo* hints = nullptr) {
4605 return mk_sockaddr_storage(node_name.data(), service_name.data(), hints);
4606 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
4607
4608 /*======================================================================*//**
4609 @brief
4610 Specify a name for this rsocket.
4611
4612 This is an arbitrary name that is entirely optional, and should be regarded
4613 as similar to the naming of threads.
4614 @returns The same rsocket object so as to facilitate stacking
4615 *///=========================================================================
4616 rsocket* name(
4617 /// Name to assign to this @c rsocket
4618 const std::string name) noexcept {
4619 // const std::lock_guard<std::mutex> lock(__name);
4620 __name = name;
4621 return this;
4622 } // -x- rsocket* name -x-
4623
4624 /*======================================================================*//**
4625 @brief
4626 Find out what this rsocket's name is.
4627
4628 The built-in SNI mechanism will overwrite this data to indicate the hostname
4629 that was specified by the TLS-encrypted endpoint that triggered an internal
4630 SNI callback -- use the @ref name_sni() method to find out which hostname is
4631 actually being used by TLS.
4632 @returns The name of this rsocket (or an empty @c std::string if this rsocket
4633 doesn't have a name)
4634 @see name_sni
4635 @see tls_sni
4636 *///=========================================================================
4637 std::string name() noexcept {
4638 // const std::lock_guard<std::mutex> lock(__name);
4639 return __name;
4640 } // -x- std::string name -x-
4641
4642 /*======================================================================*//**
4643 @brief
4644 Find out what this rsocket's actual TLS SNI hostname is.
4645
4646 This is the exact - or closest-matching (in the case of wildcards) - hostname
4647 associated with an actual TLS certificate that was provided in the configured
4648 @ref rsocket_sni object.
4649 @returns The hostname associated with the TLS certificate selected by SNI
4650 @see name
4651 @see tls_sni
4652 @see tls_sni_has_name
4653 @qualifier TLS
4654 *///=========================================================================
4655 std::string name_sni() noexcept {
4656 return __name_sni;
4657 } // -x- std::string name_sni -x-
4658
4659 /*======================================================================*//**
4660 @brief
4661 Get socket I/O statistics from internally-tracked socket I/O counters.
4662
4663 The number of bytes transmitted and received is tracked internally, so that
4664 the information can be used later in logging. These statistics are available
4665 at all times, but for logging purposes it makes the most sense to copy this
4666 information after the rsocket is closed, at which time the final statistics
4667 will continue to be available until the rsocket's destructor takes over.
4668
4669 @par Threads
4670 This method is threadsafe.
4671 @returns rsocket_io wrapped in a std::shared_ptr object to help ease memory
4672 management efforts
4673 @see net_io_final
4674 @see net_io_update
4675 @see printf
4676 @qualifier TLS
4677 *///=========================================================================
4678 std::shared_ptr<rsocket_io> net_io() noexcept {
4679 std::shared_ptr stats = std::make_shared<rsocket_io>();
4680 stats->bytes_rx = __bytes_rx;
4681 stats->bytes_tx = __bytes_tx;
4682 stats->crypt_rx = __crypt_rx;
4683 stats->crypt_tx = __crypt_tx;
4684 stats->is_final = false;
4685 return stats;
4686 } // -x- std::shared_ptr<rsocket_io> net_io -x-
4687
4688 /*======================================================================*//**
4689 @brief
4690 Get socket I/O statistics from internally-tracked socket I/O counters as a
4691 pre-formatted std::string object.
4692
4693 The number of bytes transmitted and received is tracked internally, so that
4694 the information can be used later in logging. These statistics are available
4695 at all times, but for logging purposes it makes the most sense to copy this
4696 information after the rsocket is closed, at which time the final statistics
4697 will continue to be available until the rsocket's destructor takes over.
4698
4699 @par Formatting
4700 The format string may contain any characters, with only instances of the
4701 following case-sensitive command sequences to be interpolated accordingly
4702 (invalid commands will be ignored and will remain in place, unmodified):
4703
4704 <table cellpadding=8 cellspacing=0 border=1>
4705 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
4706 <tr><td>$$</td><td>One dollar sign ("$")</td><td>@e N/A</td></tr>
4707 <tr><td>$aR</td><td>Total (all) bytes received</td><td>bytes_rx @c + crypt_rx</td></tr>
4708 <tr><td>$aT</td><td>Total (all) bytes transmitted</td><td>bytes_tx @c + crypt_tx</td></tr>
4709 <tr><td>$bR</td><td>Unencrypted bytes received</td><td>bytes_rx</td></tr>
4710 <tr><td>$bT</td><td>Unencrypted bytes transmitted</td><td>bytes_tx</td></tr>
4711 <tr><td>$cR</td><td>Encrypted bytes recevied</td><td>crypt_rx</td></tr>
4712 <tr><td>$cT</td><td>Encrypted bytes transmitted</td><td>crypt_tx</td></tr>
4713 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
4714 </table>
4715
4716 Why do we use dollar signs instead of percent symbols, like other formatting
4717 functions like printf() does? So that the format string won't be in conflict
4718 with any percent-prefixed commands in printf() and similar funcions. This
4719 means that a printf() format string can be put through a first pass here to
4720 get the needed statistics interpolated into the needed positions.
4721
4722 @par Threads
4723 This method is threadsafe.
4724 @returns An interpolated format string as an std::string object.
4725 @see net_io_final
4726 @see net_io_update
4727 @see printf
4728 @qualifier TLS
4729 *///=========================================================================
4730 std::string net_io(
4731 /// Format string
4732 const char* format,
4733 /// Length of format string (in bytes), or 0 to auto-detect length if format string is an ASCIIZ string
4734 size_t len = 0,
4735 /// Pointer to an @ref rsocket_io structure to read the statistics from (nullptr = use internal copy of current statistics)
4736 // TODO: Convert this to a static method and create a new method without the "len" parameter that just calls the static method with the pointer to the internal "addr" buffer (this will make it easier to call this method in a static fashion later without incurring any of the overhead of instantiation)
4737 rsocket_io* addr = nullptr) noexcept {
4738
4739 // --------------------------------------------------------------------------
4740 // Measure size of format string if an ASCIIZ string was indicated.
4741 // --------------------------------------------------------------------------
4742 if (len == 0) len = std::strlen(format);
4743
4744 // --------------------------------------------------------------------------
4745 // Internal variables.
4746 // --------------------------------------------------------------------------
4747 rsocket_io* data = addr == nullptr ? net_io().get() : addr; // Copy current statistics so we have a snapshot instead of a set of possibly-changing statistics to work with
4748 std::string stats; // Formatted result
4749 ulong val; // Value (to be inserted)
4750 char c; // Current character
4751 std::string cmd; // Accumulated command (may be subsituted)
4752 bool flag_commas = false; // Thousands separators flag
4753 bool flag_right = false; // Flush-right flag
4754
4755 // --------------------------------------------------------------------------
4756 // Process format string and build resulting formatted stats string.
4757 //
4758 // This is designed to be fast, and I'm taking huge shortcuts here since the
4759 // commands are all the same size (except for the first one, which is only
4760 // two dollar sign characters). Processing in this loop should be quite fast
4761 // compared to using library functions to search for dollar sign characters
4762 // since data needs to be copied anyway -- why run a loop over the text twice
4763 // when can get away with doing it faster by doing it only once? This is an
4764 // optimized approach, although it probably could be even faster by not using
4765 // std::string for temporary command storage (there are other ways to do this
4766 // but I think this should suffice for now since it isn't expected to be used
4767 // very often).
4768 // --------------------------------------------------------------------------
4769 for (int i = 0; i < len; i++) {
4770
4771 // --------------------------------------------------------------------------
4772 // First character (potentially).
4773 // --------------------------------------------------------------------------
4774 if ((c = format[i]) != '$') { // Not a dollar sign -- save it and move on
4775 stats.push_back(c);
4776 continue;
4777 } // -x- if !$ -x-
4778 cmd = c; // Start building up the command string
4779
4780 // --------------------------------------------------------------------------
4781 // Second character: Part 1 of 2
4782 //
4783 // TODO: Add support for "," commas, and "r" right-alignment.
4784 // --------------------------------------------------------------------------
4785 if (++i == len) continue; // End of format string, so we're done
4786 cmd.push_back(c = format[i]);
4787 if (c == '$') { // Consume the previous dollar sign so that $$ turns into $
4788 stats.push_back(c); // Add $ to stats string (we're only keeping one dollar sign)
4789 continue;
4790 } // -x- if $ -x-
4791 flag_commas = flag_right = false; // Reset these flags for a clean start
4792
4793 // --------------------------------------------------------------------------
4794 // Flag checks:
4795 // , = Include thousands separators (commas)
4796 // r = Flush right (leading spaces will be added)
4797 // --------------------------------------------------------------------------
4798 net_io_flags_loop:
4799 if (c == ',') {
4800 if (++i == len) continue; // End of format string, so we're done
4801 cmd.push_back(c = format[i]);
4802 flag_commas = true;
4803 goto net_io_flags_loop;
4804 } else if (c == 'r') {
4805 if (++i == len) continue; // End of format string, so we're done
4806 cmd.push_back(c = format[i]);
4807 flag_right = true;
4808 goto net_io_flags_loop;
4809 } // -x- if [,r] -x-
4810 // TODO: Do more
4811
4812 // --------------------------------------------------------------------------
4813 // Second character: Part 1 of 2
4814 // --------------------------------------------------------------------------
4815 if (c < 'a' || c > 'c') { // Not a, b, or c, so treat it as a regular character
4816 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4817 continue;
4818 } // -x- if !~[abc] -x-
4819
4820 // --------------------------------------------------------------------------
4821 // Third character.
4822 // --------------------------------------------------------------------------
4823 if (++i == len) continue; // End of format string, so we're done
4824 cmd.push_back(c = format[i]);
4825 if (c != 'R' && c != 'T') { // Not R or T, so treat it as a regular character
4826 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
4827 continue;
4828 } // -x- if !~[RT] -x-
4829
4830 // --------------------------------------------------------------------------
4831 // Command processing. If the command is valid, then it will be interpolated
4832 // with its expected result by replacing it with the resulting value.
4833 // --------------------------------------------------------------------------
4834 //std::cout << "[" << cmd << "]"; // Debug
4835 if (cmd.ends_with("aR")) val = data->bytes_rx + data->crypt_rx;
4836 else if (cmd.ends_with("aT")) val = data->bytes_tx + data->crypt_tx;
4837 else if (cmd.ends_with("bR")) val = data->bytes_rx ;
4838 else if (cmd.ends_with("bT")) val = data->bytes_tx ;
4839 else if (cmd.ends_with("cR")) val = data->crypt_rx;
4840 else if (cmd.ends_with("cT")) val = data->crypt_tx;
4841 else { stats.append(cmd); continue; } // This is wrong, so ignore it
4842
4843 // --------------------------------------------------------------------------
4844 // Re-use cmd to generate formatted value.
4845 // --------------------------------------------------------------------------
4846 cmd.clear();
4847 cmd.resize(21); // Maximum length without commas (plus character zero): 18446744073709551615
4848 cmd.resize(sprintf(cmd.data(), flag_right ? "%20lu" : "%lu", val)); // The ::sprintf function can't use cmd.data() here
4849 if (flag_commas) cmd = randolf::rtools::insert_commas(cmd.data()); // Insert commas (this makes the string longer)
4850
4851 // --------------------------------------------------------------------------
4852 // Convert to std::string and add to the stats string.
4853 // --------------------------------------------------------------------------
4854 stats.append(cmd);
4855
4856 } // -x- for i -x-
4857
4858 return stats;
4859 } // -x- std::string net_io -x-
4860
4861 /*======================================================================*//**
4862 @brief
4863 Where the destructor should save final I/O statistics before this rsocket's
4864 resources are completely freed/deallocated.
4865 @attention
4866 Developers should take care to check that the @ref rsocket_io::is_final flag
4867 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
4868 since there's no guarantee that the destructor will necessarily be executed
4869 in a timely manner (this flag is set last, after all other statistics are
4870 copied into the structure while in a mutex-locked state).
4871 @returns The same rsocket object so as to facilitate stacking
4872 @see ~rsocket
4873 @see net_io
4874 @see net_io_update
4875 @see printf
4876 @qualifier TLS
4877 *///=========================================================================
4878 rsocket* net_io_final(
4879 /// Pointer to @ref rsocket_io structure (nullptr = disabled)
4880 rsocket_io* addr) noexcept {
4881 __io_final_addr = addr;
4882 return this;
4883 } // -x- rsocket* net_io_final -x-
4884
4885 /*======================================================================*//**
4886 @brief
4887 Where the destructor should save current I/O statistics.
4888 @attention
4889 Statistics are copied into the structure while in a mutex-locked state, but
4890 the copy itself is not an overall atomic snapshot of the I/O statistics even
4891 though each statistic is copied atomically. This means that the actual
4892 statistics could be slightly different if updates occur independently (e.g.,
4893 due to activities in a different thread), but this likely won't matter since
4894 the anticipated use for these statistics is to display or otherwise present
4895 general statistical information to a user at regular intervals.
4896 @returns The same rsocket object so as to facilitate stacking
4897 @see ~rsocket
4898 @see net_io
4899 @see net_io_final
4900 @see printf
4901 @qualifier TLS
4902 *///=========================================================================
4903 rsocket* net_io_update(
4904 /// Pointer to @ref rsocket_io structure (nullptr = do nothing)
4905 rsocket_io* addr) noexcept {
4906
4907 // --------------------------------------------------------------------------
4908 // Copy statistics to final location. (We do this last in case there's an
4909 // issue with locking; there shouldn't be, but if there is then at least we
4910 // already we're not inadvertently hanging on to resources at this point that
4911 // need to be freed/closed anyway.)
4912 // --------------------------------------------------------------------------
4913 if (addr != nullptr) {
4914 addr->lock();
4915 addr->bytes_rx = __bytes_rx;
4916 addr->bytes_tx = __bytes_tx;
4917 addr->crypt_rx = __crypt_rx;
4918 addr->crypt_tx = __crypt_tx;
4919 addr->is_final = false;
4920 addr->unlock();
4921 } // -x- if addr -x-
4922
4923 return this;
4924 } // -x- rsocket* net_io_update -x-
4925
4926 /*======================================================================*//**
4927 @brief
4928 Return the number of bytes pending to be received, without actually receiving
4929 any data.
4930
4931 @note
4932 When using TLS, OpenSSL may not report all the pending data, but this is
4933 resolved here by also adding the total amount of pending raw data that is not
4934 currently being processed in the current record by OpenSSL, even when "read
4935 ahead" is enabled.
4936
4937 @throws randolf::rex::xEBADF The underlying socket is not open
4938 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4939 part of the user address space
4940 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
4941 @throws randolf::rex::xENOTTY The underlying socket is not associated with a
4942 character special device, or the specified operation does not apply
4943 to the kind of object that the file descriptor fd references (this
4944 exception will probably never occur unless the underlying socket
4945 handle was arbitrarily replaced with the type of handle that can
4946 cause this error to occur)
4947
4948 @returns Total number of bytes pending (0 = none)
4949 @see eos
4950 @see recv
4951 @qualifier TLS
4952 *///=========================================================================
4953 ulong pending() {
4954 if (__debug) debug("pending(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
4955 + ");");
4956
4957 // --------------------------------------------------------------------------
4958 // Get pending bytes from raw socket.
4959 // --------------------------------------------------------------------------
4960 ulong pending_bytes = 0; // BSD, glibc, etc., use ulong; older UNIX systems use int // TODO: Figure out how to detect what ioctl needs
4961 __rc_check(ioctl(__socket_fd, FIONREAD, &pending_bytes));
4962
4963 // --------------------------------------------------------------------------
4964 // Add pending bytes from OpenSSL if TLS is active, because this total isn't
4965 // included in the raw socket count since OpenSSL received these bytes.
4966 // --------------------------------------------------------------------------
4967 return __tls ? pending_bytes + SSL_pending(__tls_fd) : pending_bytes;
4968
4969 } // -x- ulong pending -x-
4970
4971 /*======================================================================*//**
4972 @brief
4973 Poll the underlying socket using the poll() method for data that's ready for
4974 receiving (default), etc.
4975
4976 @note
4977 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
4978 select(), and accept()/accept4() when using multiple sockets.
4979
4980 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4981 part of the user address space
4982 @throws randolf::rex::xEINTR Interrupted by a signal
4983 @throws randolf::rex::xENOMEM Insufficient memory
4984 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4985 doesn't refer to a socket
4986 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
4987 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
4988 is a highly improbable chance that a timeout could still occur if the
4989 data is read by another thread before the `recv(..., MSG_PEEK)` call)
4990 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
4991 there's no new data
4992
4993 @returns The resulting (short)pollfd.revents bitfield
4994 @returns 0 if @c timeout_behaviour is set to TIMEOUT_ZERO
4995 @see ppoll
4996 @see rsocket_mux
4997 @qualifier POSIX
4998 @qualifier TLS
4999 *///=========================================================================
5000 // TODO: Integrate SSL_poll (requires OpenSSL 3.3 or newer) with SSL_POLL_EVENT_R, SSL_POLL_EVENT_W, and SSL_POLL_EVENT_E conversion
5001 short poll(
5002 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
5003 const short events = POLLIN,
5004 /// Number of milliseconds to wait
5005 const int timeout = 0,
5006 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
5007 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
5008 if (__debug) debug("poll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5009 + ", pollfd.events=" + std::to_string(events)
5010 + ", timeout=" + std::to_string(timeout)
5011 + ");");
5012 struct pollfd fds{__socket_fd, events, 0};
5013 if (__rc_check(::poll(&fds, 1, timeout)) == 0) { // Timeout occurred when ::poll returns 0
5014 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
5015 else return 0;
5016 } // -x- if 0 -x-
5017 return fds.revents;
5018 } // -x- short poll -x-
5019
5020 /*======================================================================*//**
5021 @brief
5022 Get port number associated with underlying socket descriptor/handle.
5023
5024 Returns 0 if:
5025 - an emphemeral port number assignment (dynamic) is intended
5026 - the underlying socket details are not defined (e.g., in the case of an
5027 empty rsocket instantiation)
5028 - port numbers are not supported/utilized by the current family (e.g., not
5029 AF_INET {IPv4} or AF_INET6 {IPv6})
5030
5031 The port number can be set in most constructors, or via one of the socket()
5032 or bind() methods.
5033 @returns Port number (typically TCP and UDP, although some other families
5034 such as SCTP and DCCP also utilize port numbers)
5035 @see socket_family()
5036 @see socket_protocol()
5037 @see socket_type()
5038 @qualifier TLS
5039 *///=========================================================================
5040 const uint16_t port() noexcept {
5041 switch (__socket_addr->ss_family) {
5042 case AF_INET: // IPv4
5043// return ntohs(((sockaddr_in*)&__socket_addr)->sin_port);
5044 return ntohs(((sockaddr_in*)__socket_addr)->sin_port);
5045 case AF_INET6: // IPv6
5046// return ntohs(((sockaddr_in6*)&__socket_addr)->sin6_port);
5047 return ntohs(((sockaddr_in6*)__socket_addr)->sin6_port);
5048 default: // Everything else
5049 return 0;
5050 } // -x- switch __socket_addr->ss_family -x-
5051 } // -x- uint16_t port -x-
5052
5053 /*======================================================================*//**
5054 @brief
5055 Poll the underlying socket using the ppoll() method for data that's ready for
5056 receiving (default), etc.
5057
5058 @note
5059 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
5060 select(), and accept()/accept4() when using multiple sockets.
5061
5062 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5063 part of the user address space
5064 @throws randolf::rex::xEINTR Interrupted by a signal
5065 @throws randolf::rex::xENOMEM Insufficient memory
5066 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5067 doesn't refer to a socket
5068 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
5069 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
5070 is a highly improbable chance that a timeout could still occur if the
5071 data is read by another thread before the `recv(..., MSG_PEEK)` call)
5072 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5073 there's no new data
5074
5075 @returns The resulting (short)pollfd.revents bitfield
5076 @see poll
5077 @see rsocket_mux
5078 @qualifier POSIX
5079 @qualifier TLS
5080 *///=========================================================================
5081 // TODO: Integrate SSL_poll (requires OpenSSL 3.3 or newer) with SSL_POLL_EVENT_R, SSL_POLL_EVENT_W, and SSL_POLL_EVENT_E conversion
5082 short ppoll(
5083 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
5084 const short events = POLLIN,
5085 /// Timeout
5086 const struct timespec* tmo_p = nullptr,
5087 /// Signal mask
5088 const sigset_t* sigmask = nullptr,
5089 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
5090 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
5091 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5092 + ", pollfd.events=" + std::to_string(events)
5093 + ", timespec{tv_sec=" + std::to_string(tmo_p->tv_sec)
5094 + ", tv_nsec=" + std::to_string(tmo_p->tv_nsec) + "}"
5095 + ", sigset_t"
5096 + ");");
5097 struct pollfd fds{__socket_fd, events, 0};
5098 if (__rc_check(::ppoll(&fds, 1, tmo_p, sigmask)) == 0) { // Timeout occurred when ::ppoll returns 0
5099 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
5100 else return 0;
5101 } // -x- if 0 -x-
5102 return fds.revents;
5103 } // -x- short ppoll -x-
5104
5105 /*======================================================================*//**
5106 @brief
5107 Poll the underlying socket using the ppoll() method for data that's ready for
5108 receiving (default), etc.
5109
5110 @note
5111 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
5112 select(), and accept()/accept4() when using multiple sockets.
5113
5114 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5115 part of the user address space
5116 @throws randolf::rex::xEINTR Interrupted by a signal
5117 @throws randolf::rex::xENOMEM Insufficient memory
5118 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5119 doesn't refer to a socket
5120 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
5121 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
5122 is a highly improbable chance that a timeout could still occur if the
5123 data is read by another thread before the `recv(..., MSG_PEEK)` call)
5124 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5125 there's no new data
5126
5127 @returns The resulting (short)pollfd.revents bitfield
5128 @see ppoll
5129 @see rsocket_mux
5130 @qualifier TLS
5131 *///=========================================================================
5132 // TODO: Integrate SSL_poll (requires OpenSSL 3.3 or newer) with SSL_POLL_EVENT_R, SSL_POLL_EVENT_W, and SSL_POLL_EVENT_E conversion
5133 short ppoll(
5134 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
5135 const short events = POLLIN,
5136 /// Timeout in seconds
5137 const long tv_sec = 0,
5138 /// Timeout in nanoseconds
5139 const long tv_nsec = 0,
5140 /// Signal mask
5141 const sigset_t* sigmask = nullptr,
5142 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
5143 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
5144 struct timespec tmo_p{tv_sec, tv_nsec};
5145 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5146 + ", pollfd.events=" + std::to_string(events)
5147 + ", timespec{tv_sec=" + std::to_string(tmo_p.tv_sec)
5148 + ", tv_nsec=" + std::to_string(tmo_p.tv_nsec) + "}"
5149 + ", sigset_t"
5150 + ");");
5151 struct pollfd fds{__socket_fd, events, 0};
5152 if (__rc_check(::ppoll(&fds, 1, &tmo_p, sigmask)) == 0) { // Timeout occurred when ::ppoll returns 0
5153 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
5154 else return 0;
5155 } // -x- if 0 -x-
5156 return fds.revents;
5157 } // -x- short ppoll -x-
5158
5159 /*======================================================================*//**
5160 @brief
5161 Send a formatted string to the @ref rsocket endpoint.
5162
5163 The @c format is described in the documentation for the POSIX or Standard C
5164 Library @c printf() function.
5165 @throws randolf::rex::xEBADF The underlying socket is not open
5166 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
5167 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
5168 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
5169 @throws randolf::rex::xENOMEM Insufficient memory
5170 @returns The same rsocket object so as to facilitate stacking
5171 @see eol_fix_printf
5172 @see is_eol_fix_printf
5173 @see net_io
5174 @see printfline
5175 @see vprintf
5176 @see vprintfline
5177 @qualifier POSIX
5178 @qualifier TLS
5179 *///=========================================================================
5180 rsocket* printf(
5181 /// Format string to use
5182 const char* format,
5183 /// Variadic arguments
5184 ...) {
5185 char* buf = nullptr;
5186 ::va_list args;
5187 ::va_start(args, format);
5188 int rc = ::vasprintf(&buf, format, args);
5189 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
5190 if (rc < 0 && buf != nullptr) ::free(buf);
5191 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
5192 if (__eol_fix_printf && !__eol.empty()) {
5193 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
5194 ::free(buf);
5195 __send(str.data(), str.length());
5196 } else {
5197 try {
5198 __send(buf, rc);
5199 ::free(buf);
5200 } catch (std::exception& e) { // Free buf then re-throw the exception
5201 ::free(buf); // Prevent memory leak when an exception is thrown
5202 throw;
5203 }
5204 } // -x- if __eol_fix_printf -x-
5205 return this;
5206 } // -x- rsocket* printf -x-
5207
5208 /*======================================================================*//**
5209 @brief
5210 Send a formatted string to the @ref rsocket endpoint, and append an EoL
5211 sequence.
5212
5213 The @c format is described in the documentation for the POSIX or Standard C
5214 Library @c printf() function.
5215 @throws randolf::rex::xEBADF The underlying socket is not open
5216 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
5217 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
5218 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
5219 @throws randolf::rex::xENOMEM Insufficient memory
5220 @returns The same rsocket object so as to facilitate stacking
5221 @see eol
5222 @see eol_fix_printf
5223 @see is_eol_fix_printf
5224 @see net_io
5225 @see printf
5226 @see sendline
5227 @see vprintf
5228 @see vprintfline
5229 @qualifier TLS
5230 *///=========================================================================
5231 rsocket* printfline(
5232 /// Format string to use
5233 const char* format,
5234 /// Variadic arguments
5235 ...) {
5236 char* buf = nullptr;
5237 ::va_list args;
5238 ::va_start(args, format);
5239 int rc = ::vasprintf(&buf, format, args);
5240 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
5241 if (rc < 0 && buf != nullptr) ::free(buf);
5242 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
5243 if (__eol_fix_printf && !__eol.empty()) {
5244 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
5245 .append(__eol);
5246 ::free(buf);
5247 __send(str.data(), str.length());
5248 } else {
5249 try {
5250 __sendline(buf, rc);
5251 ::free(buf);
5252 } catch (std::exception& e) { // Free buf then re-throw the exception
5253 ::free(buf); // Prevent memory leak when an exception is thrown
5254 throw;
5255 }
5256 } // -x- if __eol_fix_printf -x-
5257 return this;
5258 } // -x- rsocket* printfline -x-
5259
5260 /*======================================================================*//**
5261 @brief
5262 Receive data from the endpoint into a @c std::vector<char> that is allocated
5263 on-the-fly.
5264
5265 If nbytes is 0, then the internal @ref buffer_size() will be used as the
5266 default, which can also be changed from its compiled-in default of 1024 by
5267 using one of the buffer_size() methods.
5268
5269 @par Notes
5270 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5271 reception, but it's important to note that it does implement temporary
5272 blocking while waiting for data.
5273
5274 @throws randolf::rex::xEBADF The underlying socket is not open
5275 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5276 connections
5277 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5278 part of the user address space
5279 @throws randolf::rex::xEINTR Interrupted by a signal
5280 @throws randolf::rex::xEINVAL Invalid argument passed
5281 @throws randolf::rex::xENOMEM Insufficient memory
5282 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5283 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5284 doesn't refer to a socket
5285 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5286 there's no new data
5287
5288 @returns appropriately-sized vector of characters
5289 @see recv(std::vector<char>, const int)
5290 @see recvz(const size_t, const int)
5291 @see send
5292 @see send(const std::vector<char>, const int)
5293 @qualifier TLS
5294 *///=========================================================================
5295 std::vector<char> recv(
5296 /// Maximum number of bytes to receive
5297 const size_t nbytes = 0,
5298 /// MSG_OOB@n
5299 /// MSG_PEEK@n
5300 /// MSG_WAITALL@n
5301 /// MSG_DONTWAIT@n
5302 /// MSG_CMSG_CLOEXEC
5303 const int posix_flags = 0) {
5304 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5305 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5306 + ", <buf>"
5307 + ", " + std::to_string(buf_size)
5308 + ", " + std::to_string(posix_flags)
5309 + ");");
5310 std::vector<char> buf(buf_size);
5311 buf.resize(__recv(buf.data(), buf.size(), posix_flags));
5312 return buf;
5313 } // -x- std::vector<char> recv -x-
5314
5315 /*======================================================================*//**
5316 @brief
5317 Receive data from the endpoint into the @c std::vector object supplied in the
5318 @c buf parameter.
5319
5320 The maximum number of bytes that can be received won't exceed the number of
5321 bytes that the supplied @c std::vector<char> was initialized or resized to.
5322
5323 @pre
5324 For @c std::vector it's important that the @c resize() method is used to
5325 pre-allocate the underlying char[] array, instead of the unfortunately-named
5326 @c reserve() method that doesn't pre-allocate, to avoid causing segmentation
5327 faults or other undefined behaviours.
5328
5329 @par Notes
5330 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5331 reception, but it's important to note that it does implement temporary
5332 blocking while waiting for data.
5333
5334 @throws randolf::rex::xEBADF The underlying socket is not open
5335 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5336 connections
5337 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5338 part of the user address space
5339 @throws randolf::rex::xEINTR Interrupted by a signal
5340 @throws randolf::rex::xEINVAL Invalid argument passed
5341 @throws randolf::rex::xENOMEM Insufficient memory
5342 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5343 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5344 doesn't refer to a socket
5345 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5346 there's no new data
5347
5348 @returns The same array that was specified in the @c buf parameter
5349 @see recv(const size_t, const int)
5350 @see recvz(const size_t, const int)
5351 @see send(const std::vector<char>, const int)
5352 @qualifier POSIX
5353 @qualifier TLS
5354 *///=========================================================================
5355 std::vector<char> recv(
5356 /// Target std::vector<char> to receive data into
5357 std::vector<char> buf,
5358 /// MSG_OOB@n
5359 /// MSG_PEEK@n
5360 /// MSG_WAITALL@n
5361 /// MSG_DONTWAIT@n
5362 /// MSG_CMSG_CLOEXEC
5363 const int posix_flags = 0) {
5364 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5365 + ", <buf>"
5366 + ", " + std::to_string(buf.size())
5367 + ", " + std::to_string(posix_flags)
5368 + ");");
5369 buf.resize(__recv(buf.data(), buf.size(), posix_flags));
5370 return buf;
5371 } // -x- std::vector<char> recv -x-
5372
5373 /*======================================================================*//**
5374 @brief
5375 Receive data from the endpoint into a @c std::string object that is allocated
5376 on-the-fly.
5377
5378 If nbytes is 0, then the internal @ref buffer_size() will be used as the
5379 default, which can also be changed from its compiled-in default of 1024 by
5380 using one of the buffer_size() methods.
5381
5382 @par Notes
5383 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5384 reception, but it's important to note that it does implement temporary
5385 blocking while waiting for data.
5386
5387 @throws randolf::rex::xEBADF The underlying socket is not open
5388 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5389 connections
5390 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5391 part of the user address space
5392 @throws randolf::rex::xEINTR Interrupted by a signal
5393 @throws randolf::rex::xEINVAL Invalid argument passed
5394 @throws randolf::rex::xENOMEM Insufficient memory
5395 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5396 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5397 doesn't refer to a socket
5398 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5399 there's no new data
5400
5401 @returns appropriately-sized vector of characters
5402 @see recv
5403 @see recv(std::vector<char>, const int)
5404 @see recvz(const size_t, const int)
5405 @see send(const std::string, const int)
5406 @qualifier TLS
5407 *///=========================================================================
5408 std::string recv_as_string(
5409 /// Maximum number of bytes to receive
5410 const size_t nbytes = 0,
5411 /// MSG_OOB@n
5412 /// MSG_PEEK@n
5413 /// MSG_WAITALL@n
5414 /// MSG_DONTWAIT@n
5415 /// MSG_CMSG_CLOEXEC
5416 const int posix_flags = 0) {
5417 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5418 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5419 + ", <buf>"
5420 + ", " + std::to_string(buf_size)
5421 + ", " + std::to_string(posix_flags)
5422 + ");");
5423 std::string buf;
5424 buf.resize(buf_size); // Pre-fill anticipated string size
5425 buf.resize(__recv(buf.data(), buf.size(), posix_flags)); // Shorten string
5426 return buf;
5427 } // -x- std::string recv -x-
5428
5429 /*======================================================================*//**
5430 @brief
5431 Receive an ASCIIZ string from the endpoint, including the NULL terminator.
5432
5433 @throws randolf::rex::xEBADF The underlying socket is not open
5434 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5435 connections
5436 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5437 part of the user address space
5438 @throws randolf::rex::xEINTR Interrupted by a signal
5439 @throws randolf::rex::xEINVAL Invalid argument passed
5440 @throws randolf::rex::xENOMEM Insufficient memory
5441 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5442 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5443 doesn't refer to a socket
5444 @throws randolf::rex::xERANGE if no NULL terminator is detected (this may
5445 occur before the underlying ASCIIZ string char* array is allocated)
5446 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5447 there's no new data
5448
5449 @returns Pointer to ASCIIZ string (will need to be deleted by caller)
5450 @see send_asciiz(const char*, const int)
5451 @qualifier TLS
5452 *///=========================================================================
5453 char* recv_asciiz(
5454 /// Maximum number of bytes to receive
5455 const size_t nbytes = 0,
5456 /// MSG_OOB@n
5457 /// MSG_PEEK@n
5458 /// MSG_WAITALL@n
5459 /// MSG_DONTWAIT@n
5460 /// MSG_CMSG_CLOEXEC
5461 const int posix_flags = 0) {
5462 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
5463 if (__debug) debug("recv_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5464 + ", " + std::to_string(buf_size)
5465 + ", " + std::to_string(posix_flags)
5466 + ");");
5467
5468 // --------------------------------------------------------------------------
5469 // Calculate size of buffer (includes NULL terminator since we'll also be
5470 // receiving this from the endpoint).
5471 // --------------------------------------------------------------------------
5472 char buf[buf_size];
5473
5474 // --------------------------------------------------------------------------
5475 // Reduce buffer size to what is actually read (remember: we don't actually
5476 // know where the EoL sequence is yet, or if there even is one).
5477 // --------------------------------------------------------------------------
5478 int max = __recv(&buf, buf_size, posix_flags | MSG_PEEK); // Look-ahead at socket stream (buffer was inflated to include EoL sequence)
5479 int len = -1;
5480 for (int i = 0; i < max; i++) {
5481 if (buf[i] == 0) {
5482 len = i;
5483 break;
5484 } // -x- if v[i] -x-
5485 } // -x- for i -x-
5486 if (len < 0) throw randolf::rex::xERANGE("ERANGE: NULL terminator not found");
5487
5488 // --------------------------------------------------------------------------
5489 // I'd love to use std::shared_ptr<char*> for this next part, but it leads
5490 // to intermittent segmentation faults and/or "malloc(): corrupted top size
5491 // occurs" errors outside of this method. So, my conclusion is that char*
5492 // (and char[], as I've tried with this too) are not properly supported by:
5493 // std::shared_ptr<char*> v = std::make_shared<char*>(new char[len + 1]);
5494 //
5495 // Using std::string() is really not what we want because there is confusion
5496 // concerning the NULL terminator -- with char* strings, it's crystal clear
5497 // that there must be a NULL terminator (since a length isn't tracked). Now,
5498 // if you want an std::string, just initialize as follows:
5499 //
5500 // char* temp_string = r.recv_asciiz();
5501 // std::string mystr = std::string(temp_string);
5502 // delete temp_string;
5503 // --------------------------------------------------------------------------
5504 char* v = new char[len + 1];
5505 int rc = __recv(v, len + 1, posix_flags); // Consume with NULL terminator from socket stream
5506// TODO: Free "v" in a catch-and-rethrow block (check other calls to __recv and __send too)
5507 return v;
5508
5509 } // -x- char* recv_asciiz -x-
5510
5511 /*======================================================================*//**
5512 @brief
5513 Receive one byte (unsigned 8-bit byte) of data from the endpoint.
5514 @par Notes
5515 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5516 reception, but it's important to note that it does implement temporary
5517 blocking while waiting for data.
5518
5519 @throws randolf::rex::xEBADF The underlying socket is not open
5520 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5521 connections
5522 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5523 part of the user address space
5524 @throws randolf::rex::xEINTR Interrupted by a signal
5525 @throws randolf::rex::xEINVAL Invalid argument passed
5526 @throws randolf::rex::xENOMEM Insufficient memory
5527 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5528 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5529 doesn't refer to a socket
5530 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5531 there's no new data
5532
5533 @returns one unsigned character
5534 @see recv(std::vector<char>, const int)
5535 @see recvz(const size_t, const int)
5536 @see recv_char
5537 @see send_byte
5538 @see send_char
5539 @qualifier TLS
5540 *///=========================================================================
5541 u_char recv_byte(
5542 /// MSG_OOB@n
5543 /// MSG_PEEK@n
5544 /// MSG_WAITALL@n
5545 /// MSG_DONTWAIT@n
5546 /// MSG_CMSG_CLOEXEC
5547 const int posix_flags = 0) {
5548 char buf(0);
5549 if (__debug) debug("recv_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5550 + ", <buf>"
5551 + ", " + std::to_string(sizeof(buf))
5552 + ", " + std::to_string(posix_flags)
5553 + ");");
5554 int rc = __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5555 return buf;
5556 } // -x- byte recv_byte -x-
5557
5558 /*======================================================================*//**
5559 @brief
5560 Receive one character (signed 8-bit byte) of data from the endpoint.
5561 @par Notes
5562 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5563 reception, but it's important to note that it does implement temporary
5564 blocking while waiting for data.
5565
5566 @throws randolf::rex::xEBADF The underlying socket is not open
5567 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5568 connections
5569 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5570 part of the user address space
5571 @throws randolf::rex::xEINTR Interrupted by a signal
5572 @throws randolf::rex::xEINVAL Invalid argument passed
5573 @throws randolf::rex::xENOMEM Insufficient memory
5574 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5575 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5576 doesn't refer to a socket
5577 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5578 there's no new data
5579
5580 @returns one signed character
5581 @see recv(std::vector<char>, const int)
5582 @see recvz(const size_t, const int)
5583 @see recv_byte
5584 @see send_byte
5585 @see send_char
5586 @qualifier TLS
5587 *///=========================================================================
5588 char recv_char(
5589 /// MSG_OOB@n
5590 /// MSG_PEEK@n
5591 /// MSG_WAITALL@n
5592 /// MSG_DONTWAIT@n
5593 /// MSG_CMSG_CLOEXEC
5594 const int posix_flags = 0) {
5595 char buf(0);
5596 if (__debug) debug("recv_char(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5597 + ", <buf>"
5598 + ", " + std::to_string(sizeof(buf))
5599 + ", " + std::to_string(posix_flags)
5600 + ");");
5601 int rc = __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5602 // How to detect disconnected stream when telnet user presses CTRL-C?
5603 //std::cout << "rc=" << std::to_string(rc) << std::endl; // Debug (to be removed)
5604// TODO: Investigate checking platform's char size and moving signing bit accordingly
5605 return buf;
5606 } // -x- char recv_char -x-
5607
5608 /*======================================================================*//**
5609 @brief
5610 Receive a line of data from the endpoint, into a new @ref randolf::rline
5611 object, with the EoL character(s) isolated. This is meant for multiline
5612 ASCII and UTF-8 text, and while it will also work with binary data that
5613 doesn't include any EoL sequence characters (ASCII `10` and `13`),
5614 @ref recv(), @ref recvz(), and other methods are much better-suited to
5615 receive binary data.
5616
5617 This is essentially a wrapper around what recvline() does, but returns both
5618 the line of text and the EoL sequence together in a new @ref randolf::rline
5619 object.
5620 @note
5621 For additional details on the other parameters, please see the @ref recvline
5622 method's documentation for the remaining details.
5623 @warning
5624 If you're using a customzied EoL sequence, then it's important to note that
5625 it probably won't be recognized by the @ref randolf::rline class, but do also
5626 check that documentation to confirm this.
5627
5628 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
5629 @throws randolf::rex::xEBADF The underlying socket is not open
5630 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5631 connections
5632 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5633 part of the user address space
5634 @throws randolf::rex::xEINTR Interrupted by a signal
5635 @throws randolf::rex::xEINVAL Invalid argument passed
5636 @throws randolf::rex::xENOMEM Insufficient memory
5637 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5638 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5639 doesn't refer to a socket
5640 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
5641 no EoL character sequence is detected after recvline's buffer became
5642 full (whatever data is waiting may still be received using any of the
5643 recv_ methods {with or without the @c MSG_PEEK flag set}, including
5644 @c recvline() with a larger buffer {see the @c nbytes parameter})
5645 @throws randolf::rex::xERANGE if the timeout parameter is below 0
5646 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5647 there's no new data
5648
5649 @returns One line of text as a randolf::rline object.
5650 @see discard
5651 @see eol()
5652 @see eol_consumed_seq()
5653 @see randolf::rline
5654 @see recvline
5655 @see send_rline(const randolf::rline, const int)
5656 @see sendline(const std::string, const int)
5657 @see timeout
5658 @see timeout_recvline
5659 @see timeout_recvline(long)
5660 @qualifier TLS
5661 *///=========================================================================
5662 randolf::rline recv_rline(
5663 /// Maximum number of bytes to receive (including EoL character sequence)
5664 const size_t nbytes = 0,
5665 /// MSG_OOB@n
5666 /// MSG_PEEK@n
5667 /// MSG_WAITALL@n
5668 /// MSG_DONTWAIT@n
5669 /// MSG_CMSG_CLOEXEC
5670 const int posix_flags = 0,
5671 /// Line timeout (in seconds)@n
5672 /// 0 = no timeout (default), unless it was configured by way of the
5673 /// @ref timeout_recvline(long) method
5674 long timeout = 0,
5675 /// Configuration parameters
5676 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
5677 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
5678 // + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF; TODO: Add an option for this
5679 // Internally, the length of the @ref eol() sequence is added to the buffer size
5680 // so that the maximum line length without EoL characters matches the maximum
5681 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
5682 if (__debug) debug("recv_rline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5683 + ", " + std::to_string(buf_size)
5684 + ", " + std::to_string(posix_flags)
5685 + ");");
5686 std::string line = __recvline(buf_size, posix_flags, timeout, recvline_flags);
5687 return randolf::rline(line, __eol_consumed_seq);
5688 } // -x- randolf::rline recv_rline -x-
5689
5690 /*======================================================================*//**
5691 @brief
5692 Receive a line of data from the endpoint assigned into the specifieid
5693 @ref randolf::rline object, with the EoL character(s) isolated. This is
5694 meant for multiline ASCII and UTF-8 text, and while it will also work with
5695 binary data that doesn't include any EoL sequence characters (ASCII `10` and
5696 `13`), @ref recv(), @ref recvz(), and other methods are much better-suited to
5697 receive binary data.
5698
5699 This is essentially a wrapper around what recvline() does, but returns both
5700 the line of text and the EoL sequence together, assigned into the existing
5701 @ref randolf::rline object.
5702 @note
5703 For additional details on the other parameters, please see the @ref recvline
5704 method's documentation for the remaining details.
5705 @warning
5706 If you're using a customzied EoL sequence, then it's important to note that
5707 it probably won't be recognized by the @ref randolf::rline class, but do also
5708 check that documentation to confirm this.
5709 @post
5710 When providing @c nullptr for the line parameter, a new @ref randolf::rline
5711 object will be instantiated that will then need to be deleted after it's no
5712 longer needed.
5713
5714 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
5715 @throws randolf::rex::xEBADF The underlying socket is not open
5716 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5717 connections
5718 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5719 part of the user address space
5720 @throws randolf::rex::xEINTR Interrupted by a signal
5721 @throws randolf::rex::xEINVAL Invalid argument passed
5722 @throws randolf::rex::xENOMEM Insufficient memory
5723 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5724 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5725 doesn't refer to a socket
5726 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
5727 no EoL character sequence is detected after recvline's buffer became
5728 full (whatever data is waiting may still be received using any of the
5729 recv_ methods {with or without the @c MSG_PEEK flag set}, including
5730 @c recvline() with a larger buffer {see the @c nbytes parameter})
5731 @throws randolf::rex::xERANGE if the timeout parameter is below 0
5732 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5733 there's no new data
5734
5735 @returns One line of text as a randolf::rline object.
5736 @see discard
5737 @see eol()
5738 @see eol_consumed_seq()
5739 @see randolf::rline
5740 @see recvline
5741 @see send_rline(const randolf::rline, const int)
5742 @see sendline(const std::string, const int)
5743 @see timeout
5744 @see timeout_recvline
5745 @see timeout_recvline(long)
5746 @qualifier TLS
5747 *///=========================================================================
5748 randolf::rline* recv_rline(
5749 /// Pointer to the pre-instantiated randolf::rline object to assign into@n
5750 /// nullptr = instantiate a new randolf::rline object internally, with this
5751 /// rsocket's current EoL sequence set to override the default
5752 randolf::rline* line,
5753 /// Maximum number of bytes to receive (including EoL character sequence)
5754 const size_t nbytes = 0,
5755 /// MSG_OOB@n
5756 /// MSG_PEEK@n
5757 /// MSG_WAITALL@n
5758 /// MSG_DONTWAIT@n
5759 /// MSG_CMSG_CLOEXEC
5760 const int posix_flags = 0,
5761 /// Line timeout (in seconds)@n
5762 /// 0 = no timeout (default), unless it was configured by way of the
5763 /// @ref timeout_recvline(long) method
5764 long timeout = 0,
5765 /// Configuration parameters
5766 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
5767 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
5768 // + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF; TODO: Add an option for this
5769 // Internally, the length of the @ref eol() sequence is added to the buffer size
5770 // so that the maximum line length without EoL characters matches the maximum
5771 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
5772 if (__debug) debug("recv_rline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5773 + ", " + std::to_string(buf_size)
5774 + ", " + std::to_string(posix_flags)
5775 + ");");
5776
5777 // --------------------------------------------------------------------------
5778 // Instantiate new object if nullptr was provided.
5779 // --------------------------------------------------------------------------
5780 if (line == nullptr) line = new randolf::rline("", __eol);
5781
5782 // --------------------------------------------------------------------------
5783 // Append received line of data to line, and return it.
5784 // --------------------------------------------------------------------------
5785 line->assign(__recvline(buf_size, posix_flags, timeout, recvline_flags) + __eol_consumed_seq);
5786
5787 return line;
5788 } // -x- randolf::rline* recv_rline -x-
5789
5790 /*======================================================================*//**
5791 @brief
5792 Receive a data structure from the endpoint.
5793 @post
5794 MSB/LSB considerations are important for any integers within your structure,
5795 so be sure to sanitize them with htons(), ntohs(), and related methods prior
5796 to sending, and then after receiving. This way, your data will be protected
5797 against corruption resulting from byte order differences when communicating
5798 between hardware architectures that differ in MSB and LSB byte ordering.
5799 @par Notes
5800 The @c MSG_WAITALL flag is particularly useful for preventing premature data
5801 reception, but it's important to note that it does implement temporary
5802 blocking while waiting for data.
5803
5804 @throws randolf::rex::xEBADF The underlying socket is not open
5805 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5806 connections
5807 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5808 part of the user address space
5809 @throws randolf::rex::xEINTR Interrupted by a signal
5810 @throws randolf::rex::xEINVAL Invalid argument passed
5811 @throws randolf::rex::xENOMEM Insufficient memory
5812 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5813 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5814 doesn't refer to a socket
5815 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5816 there's no new data
5817
5818 @returns Data structure
5819 @see send_struct
5820 @qualifier TLS
5821 *///=========================================================================
5822 template <typename T> T recv_struct(
5823 /// MSG_OOB@n
5824 /// MSG_PEEK@n
5825 /// MSG_WAITALL@n
5826 /// MSG_DONTWAIT@n
5827 /// MSG_CMSG_CLOEXEC
5828 const int posix_flags = 0) {
5829 T buf{0};
5830 if (__debug) debug("recv_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5831 + ", <buf>"
5832 + ", " + std::to_string(sizeof(buf))
5833 + ", " + std::to_string(posix_flags)
5834 + ");");
5835 __recv(&buf, sizeof(buf), posix_flags);
5836 return buf;
5837 } // -x- T recv_struct -x-
5838
5839 /*======================================================================*//**
5840 @brief
5841 Receive one 16-bit unsigned integer of data in LSB (little endian) order from
5842 the endpoint.
5843 @par Notes
5844 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5845 reception, which implements temporary blocking while waiting for data.
5846
5847 @throws randolf::rex::xEBADF The underlying socket is not open
5848 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5849 connections
5850 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5851 part of the user address space
5852 @throws randolf::rex::xEINTR Interrupted by a signal
5853 @throws randolf::rex::xEINVAL Invalid argument passed
5854 @throws randolf::rex::xENOMEM Insufficient memory
5855 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5856 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5857 doesn't refer to a socket
5858 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5859 there's no new data
5860
5861 @returns uint16_t (converted to local endianness)
5862 @see recv_uint16_msb
5863 @see send_uint16_lsb
5864 @qualifier TLS
5865 *///=========================================================================
5866 uint16_t recv_uint16_lsb(
5867 /// MSG_OOB@n
5868 /// MSG_PEEK@n
5869 /// MSG_WAITALL@n
5870 /// MSG_DONTWAIT@n
5871 /// MSG_CMSG_CLOEXEC
5872 const int posix_flags = 0) {
5873 uint16_t buf(0);
5874 if (__debug) debug("recv_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5875 + ", <buf>"
5876 + ", " + std::to_string(sizeof(buf))
5877 + ", " + std::to_string(posix_flags)
5878 + ");");
5879 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5880 return !__endian_is_msb ? buf : ntohs(buf);
5881 } // -x- uint16_t recv_uint16_lsb -x-
5882
5883 /*======================================================================*//**
5884 @brief
5885 Receive one 16-bit unsigned integer of data in MSB (big endian) order from
5886 the endpoint.
5887 @par Notes
5888 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5889 reception, which implements temporary blocking while waiting for data.
5890
5891 @throws randolf::rex::xEBADF The underlying socket is not open
5892 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5893 connections
5894 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5895 part of the user address space
5896 @throws randolf::rex::xEINTR Interrupted by a signal
5897 @throws randolf::rex::xEINVAL Invalid argument passed
5898 @throws randolf::rex::xENOMEM Insufficient memory
5899 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5900 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5901 doesn't refer to a socket
5902 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5903 there's no new data
5904
5905 @returns uint16_t (converted to local endianness)
5906 @see recv_uint16_lsb
5907 @see send_uint16_msb
5908 @qualifier TLS
5909 *///=========================================================================
5910 uint16_t recv_uint16_msb(
5911 /// MSG_OOB@n
5912 /// MSG_PEEK@n
5913 /// MSG_WAITALL@n
5914 /// MSG_DONTWAIT@n
5915 /// MSG_CMSG_CLOEXEC
5916 const int posix_flags = 0) {
5917 uint16_t buf(0);
5918 if (__debug) debug("recv_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5919 + ", <buf>"
5920 + ", " + std::to_string(sizeof(buf))
5921 + ", " + std::to_string(posix_flags)
5922 + ");");
5923 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5924 return __endian_is_msb ? buf : htons(buf);
5925 } // -x- uint16_t recv_uint16_msb -x-
5926
5927 /*======================================================================*//**
5928 @brief
5929 Receive one 32-bit unsigned integer of data in LSB (little endian) order from
5930 the endpoint.
5931 @par Notes
5932 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5933 reception, which implements temporary blocking while waiting for data.
5934
5935 @throws randolf::rex::xEBADF The underlying socket is not open
5936 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5937 connections
5938 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5939 part of the user address space
5940 @throws randolf::rex::xEINTR Interrupted by a signal
5941 @throws randolf::rex::xEINVAL Invalid argument passed
5942 @throws randolf::rex::xENOMEM Insufficient memory
5943 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5944 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5945 doesn't refer to a socket
5946 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5947 there's no new data
5948
5949 @returns uint32_t (converted to local endianness)
5950 @see recv_uint32_msb
5951 @see send_uint32_lsb
5952 @qualifier TLS
5953 *///=========================================================================
5954 uint32_t recv_uint32_lsb(
5955 /// MSG_OOB@n
5956 /// MSG_PEEK@n
5957 /// MSG_WAITALL@n
5958 /// MSG_DONTWAIT@n
5959 /// MSG_CMSG_CLOEXEC
5960 const int posix_flags = 0) {
5961 uint32_t buf(0);
5962 if (__debug) debug("recv_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5963 + ", <buf>"
5964 + ", " + std::to_string(sizeof(buf))
5965 + ", " + std::to_string(posix_flags)
5966 + ");");
5967 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
5968 return !__endian_is_msb ? buf : htonl(buf);
5969 } // -x- uint32_t recv_uint32_lsb -x-
5970
5971 /*======================================================================*//**
5972 @brief
5973 Receive one 32-bit unsigned integer of data in MSB (big endian) order from
5974 the endpoint.
5975 @par Notes
5976 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
5977 reception, which implements temporary blocking while waiting for data.
5978
5979 @throws randolf::rex::xEBADF The underlying socket is not open
5980 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
5981 connections
5982 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5983 part of the user address space
5984 @throws randolf::rex::xEINTR Interrupted by a signal
5985 @throws randolf::rex::xEINVAL Invalid argument passed
5986 @throws randolf::rex::xENOMEM Insufficient memory
5987 @throws randolf::rex::xENOTCONN Underlying socket is not connected
5988 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5989 doesn't refer to a socket
5990 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5991 there's no new data
5992
5993 @returns uint32_t (converted to local endianness)
5994 @see recv_uint32_lsb
5995 @see send_uint32_msb
5996 @qualifier TLS
5997 *///=========================================================================
5998 uint32_t recv_uint32_msb(
5999 /// MSG_OOB@n
6000 /// MSG_PEEK@n
6001 /// MSG_WAITALL@n
6002 /// MSG_DONTWAIT@n
6003 /// MSG_CMSG_CLOEXEC
6004 const int posix_flags = 0) {
6005 uint32_t buf(0);
6006 if (__debug) debug("recv_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6007 + ", <buf>"
6008 + ", " + std::to_string(sizeof(buf))
6009 + ", " + std::to_string(posix_flags)
6010 + ");");
6011 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6012 return __endian_is_msb ? buf : ntohl(buf);
6013 } // -x- uint32_t recv_uint32_msb -x-
6014
6015 /*======================================================================*//**
6016 @brief
6017 Receive one 64-bit unsigned integer of data in LSB (little endian) order from
6018 the endpoint.
6019 @par Notes
6020 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
6021 reception, which implements temporary blocking while waiting for data.
6022
6023 @throws randolf::rex::xEBADF The underlying socket is not open
6024 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6025 connections
6026 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6027 part of the user address space
6028 @throws randolf::rex::xEINTR Interrupted by a signal
6029 @throws randolf::rex::xEINVAL Invalid argument passed
6030 @throws randolf::rex::xENOMEM Insufficient memory
6031 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6032 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6033 doesn't refer to a socket
6034 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6035 there's no new data
6036
6037 @returns uint64_t (converted to local endianness)
6038 @see recv_uint64_msb
6039 @see send_uint64_lsb
6040 @qualifier TLS
6041 *///=========================================================================
6042 uint64_t recv_uint64_lsb(
6043 /// MSG_OOB@n
6044 /// MSG_PEEK@n
6045 /// MSG_WAITALL@n
6046 /// MSG_DONTWAIT@n
6047 /// MSG_CMSG_CLOEXEC
6048 const int posix_flags = 0) {
6049 uint64_t buf(0);
6050 if (__debug) debug("recv_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6051 + ", <buf>"
6052 + ", " + std::to_string(sizeof(buf))
6053 + ", " + std::to_string(posix_flags)
6054 + ");");
6055 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6056 return !__endian_is_msb ? buf : ((((uint64_t)ntohl((buf) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((buf) >> 32)));
6057 } // -x- uint64_t recv_uint64_lsb -x-
6058
6059 /*======================================================================*//**
6060 @brief
6061 Receive one 64-bit unsigned integer of data in MSB (big endian) order from
6062 the endpoint.
6063 @par Notes
6064 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
6065 reception, which implements temporary blocking while waiting for data.
6066
6067 @throws randolf::rex::xEBADF The underlying socket is not open
6068 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6069 connections
6070 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6071 part of the user address space
6072 @throws randolf::rex::xEINTR Interrupted by a signal
6073 @throws randolf::rex::xEINVAL Invalid argument passed
6074 @throws randolf::rex::xENOMEM Insufficient memory
6075 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6076 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6077 doesn't refer to a socket
6078 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6079 there's no new data
6080
6081 @returns uint64_t (converted to local endianness)
6082 @see recv_uint64_lsb
6083 @see send_uint64_msb
6084 @qualifier TLS
6085 *///=========================================================================
6086 uint64_t recv_uint64_msb(
6087 /// MSG_OOB@n
6088 /// MSG_PEEK@n
6089 /// MSG_WAITALL@n
6090 /// MSG_DONTWAIT@n
6091 /// MSG_CMSG_CLOEXEC
6092 const int posix_flags = 0) {
6093 uint64_t buf(0);
6094 if (__debug) debug("recv_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6095 + ", <buf>"
6096 + ", " + std::to_string(sizeof(buf))
6097 + ", " + std::to_string(posix_flags)
6098 + ");");
6099 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6100 return __endian_is_msb ? buf : ((((uint64_t)htonl((buf) & 0xffffffffUL)) << 32) | htonl((uint32_t)((buf) >> 32)));
6101 } // -x- uint64_t recv_uint64_msb -x-
6102
6103 /*======================================================================*//**
6104 @brief
6105 Receive data from a specific endpoint.
6106
6107 @par Notes
6108 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6109 reception, but it's important to note that it does implement temporary
6110 blocking while waiting for data.
6111
6112 @warning
6113 This method is not compatible with TLS.
6114
6115 @throws randolf::rex::xEBADF The underlying socket is not open
6116 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6117 connections
6118 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6119 part of the user address space
6120 @throws randolf::rex::xEINTR Interrupted by a signal
6121 @throws randolf::rex::xEINVAL Invalid argument passed
6122 @throws randolf::rex::xENOMEM Insufficient memory
6123 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6124 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6125 doesn't refer to a socket
6126 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6127 there's no new data
6128
6129 @returns Appropriately-sized vector of characters
6130 @see sendto
6131 @qualifier POSIX
6132 *///=========================================================================
6133 std::vector<char> recvfrom(
6134 /// Maximum number of bytes to receive
6135 const size_t nbytes,
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,
6142 /// Target endpoint address structure
6143 struct sockaddr *from,
6144 /// Size of target endpoint structure
6145 socklen_t fromlen = sizeof(sockaddr)) {
6146 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6147 if (__debug) debug("recvfrom(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6148 + ", <buf>"
6149 + ", " + std::to_string(buf_size)
6150 + ", " + std::to_string(posix_flags)
6151 + ", <sockaddr>"
6152 + ", " + std::to_string(fromlen)
6153 + ");");
6154 std::vector<char> v(buf_size);
6155 v.resize(__track_bytes_rx(__rc_check(::recvfrom(__socket_fd, v.data(), v.size(), posix_flags, from, &fromlen))));
6156 return v;
6157 } // -x- std::vector<char> recvfrom -x-
6158
6159 private:
6160 /*======================================================================*//**
6161 @brief
6162 This is an internal function that:
6163 1. receives data from the endpoint:
6164 - via underlying socket when TLS is not enabled
6165 - via the OpenSSL socket API when TLS is enabled
6166 2. checks for a socket I/O error (and throws an exception)
6167 3. tracks number of bytes received (if there were no errors)
6168
6169 @note
6170 The ring buffer will only be used if there is one. The @ref __recvline
6171 method is one method that instantiates the internal ring buffer, and there
6172 could be others in the future (although this is not strongly anticipated
6173 aside from a method that allows the software developer to explicitly choose
6174 to have a buffer active for all socket-read operations).
6175
6176 @throws randolf::rex::xEAGAIN The underlying socket timed out
6177 @throws randolf::rex::xEBADF The underlying socket is not open
6178 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6179 connections
6180 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6181 part of the user address space
6182 @throws randolf::rex::xEINTR Interrupted by a signal
6183 @throws randolf::rex::xEINVAL Invalid argument passed
6184 @throws randolf::rex::xENOMEM Insufficient memory
6185 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6186 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6187 doesn't refer to a socket
6188 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6189 there's no new data
6190
6191 @returns Number of bytes that were successfully received
6192 @see __send
6193 @qualifier TLS
6194 *///=========================================================================
6195 int __recv(
6196 /// Pointer to data
6197 void* data,
6198 /// Maximum number of bytes to receive into @c data array
6199 const int len,
6200 /// Flags (ignored with encrypted streams, except for the MSG_PEEK flag)
6201 const int posix_flags = 0) {
6202
6203 // --------------------------------------------------------------------------
6204 // When internal buffering is not set up (which is the default operation), we
6205 // simply pass data directly.
6206 // --------------------------------------------------------------------------
6207 if (__buffer == nullptr) {
6208 return __tls ? (posix_flags & MSG_PEEK ? __rc_check_tls(SSL_peek(__tls_fd, data, len))
6209 : __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, data, len))))
6210 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, data, len, posix_flags)));
6211 } // -x- if !__buffer -x-
6212
6213 // --------------------------------------------------------------------------
6214 // If the amount of data contained in the internal buffer is sufficient to
6215 // satisfy the amount of data that "len" represents, then all that's needed
6216 // is to return "len" bytes from the ring buffer without having to receive
6217 // more data from the socket.
6218 // --------------------------------------------------------------------------
6219 if (len <= __buffer->get_utilized()) {
6220 if (posix_flags & MSG_PEEK) __buffer->peek_to_array( (size_t)len, (char*)data); // Copy character(s) from ring buffer because MSG_PEEK flag is set
6221 else __buffer->remove_to_array((size_t)len, (char*)data); // Consume character(s) from ring buffer
6222 return len;
6223 } // -x- if len -x-
6224
6225 // --------------------------------------------------------------------------
6226 // Consume data from socket to make up for OpenSSL's inability to read beyond
6227 // one packet of data in its SSL_peek() and SSL_read() methods, and also some
6228 // inconsistencies with certain raw (unencrypted) socket implementations that
6229 // occasionally refuse to read beyond one packet of data (at least on a first
6230 // attempt to read with or without the MSG_PEEK flag).
6231 //
6232 // Fill internal buffer. It may be more than len is set to, but this is okay
6233 // because we're committed to using internal buffering now anyway (it's far
6234 // more likely that a protocol that requires the use of recvline() even once
6235 // is going to be using it repeatedly, but as long as the ring buffer is
6236 // large enough to cover the maximum length of a line of text, including its
6237 // EoL sequence, then this will always succeed).
6238 // --------------------------------------------------------------------------
6239 __buffer->defragment(true);
6240 __buffer_bam = __buffer->data_bam(__buffer_bam); // Update ring buffer setup (will be allocated automatically if "nullptr" default)
6241 size_t available = __buffer->get_available();
6242 int n = 0; // Number of bytes
6243 if (__tls) n = __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, __buffer_bam->avail1, std::min(__buffer_bam->avail1_size, (size_t)len)))); // Not using SSL_peek() at all here because we need to consume this data as we stuff it into the internal buffer
6244 else n = __track_bytes_rx(__rc_check( ::recv(__socket_fd, __buffer_bam->avail1, std::min(__buffer_bam->avail1_size, (size_t)len), posix_flags & (~MSG_PEEK)))); // Mask the MSG_PEEK flag because we need to consume this data as we stuff it into the internal buffer
6245 __buffer->set_head(n, false); // Add number of bytes received to the ring buffer after receiving directly into the available portion of the ring buffer
6246
6247 // --------------------------------------------------------------------------
6248 // Copy or move data depending on whether POSIX flag MSG_PEEK was set.
6249 // --------------------------------------------------------------------------
6250 n = std::min(len, (int)__buffer->get_utilized()); // Work with the lesser number of bytes
6251 if (n > 0) { // At least 1 byte is available
6252 if (posix_flags & MSG_PEEK) __buffer->peek_to_array( (size_t)n, (char*)data); // Copy character(s) from ring buffer because MSG_PEEK flag is set
6253 else __buffer->remove_to_array((size_t)n, (char*)data); // Consume character(s) from ring buffer
6254 } // -x- if n -x-
6255 return n; // Return the number of bytes that were copied/moved
6256
6257 } // -x- int __recv -x-
6258
6259 /*======================================================================*//**
6260 @brief
6261 This is an internal function that:
6262 1. receives a line of data from the endpoint:
6263 - via underlying socket when TLS is not enabled
6264 - via the OpenSSL socket API when TLS is enabled
6265 2. checks for a socket I/O error (and throws an exception)
6266 3. tracks number of bytes received (if there were no errors)
6267 4. isolates the EoL sequence from the line of data, and records it for
6268 later reference (for those cases where it's needed)
6269
6270 @note
6271 The two exceptions that need to be handled are @ref randolf::rex::xEOVERFLOW
6272 (when the endpoint sends oversized lines) and @ref randolf::rex::xETIMEDOUT
6273 (when the endpoint takes too long to respond) for a robust usage.
6274
6275 @throws randolf::rex::xEBADF The underlying socket is not open
6276 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6277 connections
6278 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6279 part of the user address space
6280 @throws randolf::rex::xEINTR Interrupted by a signal
6281 @throws randolf::rex::xEINVAL Invalid argument passed
6282 @throws randolf::rex::xENOMEM Insufficient memory
6283 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6284 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6285 doesn't refer to a socket
6286 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
6287 no EoL character sequence is detected after recvline's buffer became
6288 full (whatever data is waiting may still be received using any of the
6289 recv_ methods {with or without the @c MSG_PEEK flag set}, including
6290 @c recvline() with a larger buffer {see the @c nbytes parameter})
6291 @throws randolf::rex::xERANGE if the timeout parameter is below 0
6292 @throws randolf::rex::xETIMEDOUT If a @c timeout occurs
6293 @see __sendline
6294 @qualifier TLS
6295 *///=========================================================================
6296 std::string __recvline(
6297 /// Maximum number of bytes to receive (including EoL character sequence)
6298 const int buf_size,
6299 /// MSG_OOB@n
6300 /// MSG_PEEK (with some limitations)@n
6301 /// MSG_WAITALL@n
6302 /// MSG_DONTWAIT (ineffective when @c timeout is not 0)@n
6303 /// MSG_CMSG_CLOEXEC
6304 const int posix_flags,
6305 /// Line timeout (in seconds)@n
6306 /// 0 = no timeout (default), unless it was configured by way of the
6307 /// @ref timeout_recvline(long) method
6308 const ulong timeout,
6309 /// Configuration parameters
6310 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
6311
6312 // --------------------------------------------------------------------------
6313 // Internal variables.
6314 // --------------------------------------------------------------------------
6315 std::string v; // Internal buffer (we use std::string to avoid conversion steps at return)
6316 v.resize(buf_size); // Pre-allocate data so we can write to it directly
6317
6318 // --------------------------------------------------------------------------
6319 // If an internal buffer, which is used by __recv, hasn't been instantiated,
6320 // then instantiate it and save the pointer to it for future use by __recv().
6321 //
6322 // Buffering becomes necessary with TLS connection because SSL_peek fails to
6323 // return data that spans multiple packets. What triggers this behaviour is
6324 // the command "stty -icanon && openssl s_client host_name tcp_port" followed
6325 // by manual interactive typing some text then pressing the "[Enter]" key.
6326 //
6327 // The ::malloc(__buffer_size) won't be leaked because it will be freed in
6328 // in this rsocket's destructor (if it isn't freed elsewhere before this).
6329 // --------------------------------------------------------------------------
6330 if (__buffer == nullptr) __buffer = new rring(__buffer_size);
6331
6332 // --------------------------------------------------------------------------
6333 // Calculate timeout_target for easy comparisons with time(0) in the loop,
6334 // and other internal variables.
6335 // --------------------------------------------------------------------------
6336 ulong timeout_target = timeout == 0 ? (__recvline_timeout == 0 ? ULONG_MAX : __recvline_timeout + time(0)) : timeout + time(0);
6337 int br = 0; // Number of Bytes Received
6338 int len = -1;
6339 int len_with_eol = 0;
6340
6341 // --------------------------------------------------------------------------
6342 // Take advantage of RAII in C++ to ensure that the non-recvline timeout
6343 // settings will be restored upon returning normally or throwing exceptions.
6344 // --------------------------------------------------------------------------
6345 RAII_timeout raii(this, timeout);
6346
6347 // --------------------------------------------------------------------------
6348 // Line-reading loop.
6349 // --------------------------------------------------------------------------
6350 do {
6351
6352 // --------------------------------------------------------------------------
6353 // Use exceptions to handle timeouts and generate randolf::rex::xETIMEDOUT,
6354 // which is helpful because timeout errors vary on different Operating
6355 // Systems, systems configurations, and in different socket implementations.
6356 // --------------------------------------------------------------------------
6357 try {
6358
6359 // --------------------------------------------------------------------------
6360 // Attempt to read data, but without consuming it. The reason for this is
6361 // that we need to scan the data for an EoL sequence, but if there isn't one
6362 // then we need to try again while waiting for more data. (The current data
6363 // will be buffered by __recv so we will actually be waiting for more data to
6364 // to arrive from the endpoint.)
6365 // --------------------------------------------------------------------------
6366 br = __recv(v.data(), buf_size, posix_flags | MSG_PEEK); // Look-ahead at socket stream (with buffer size that was inflated earlier to include EoL sequence)
6367
6368 } catch (const randolf::rex::xEAGAIN e) { // Socket timeout derived from EAGAIN error
6369 throw randolf::rex::xETIMEDOUT("randolf::rex::xEAGAIN");
6370 } catch (const randolf::rex::xEWOULDBLOCK e) { // Socket timeout derived from EWOULDBLOCK error
6371 throw randolf::rex::xETIMEDOUT("randolf::rex::xEWOULDBLOCK");
6372 } catch (const randolf::rex::xTLS_SYSCALL e) { // Socket timeout derived from EWOULDBLOCK error
6373 throw randolf::rex::xETIMEDOUT("randolf::rex::xTLS_SYSCALL");
6374 } catch (const std::exception e) {
6375 std::cout << "Unknown error " << e.what() << std::endl;
6376 throw;
6377 }
6378
6379 // --------------------------------------------------------------------------
6380 // If EoL sequence found, save the consumed EoL sequence and return the line.
6381 // --------------------------------------------------------------------------
6382 if ((len = eol_index(v, &len_with_eol)) >= 0) { // EoL found, so we're done now (-1 = no EoL found)
6383 if (!(posix_flags & MSG_PEEK))
6384 __recv(v.data(), len_with_eol, posix_flags & (~MSG_PEEK)); // Consume data if not in MSG_PEEK mode / __recv will throw exception if something goes wrong so there's no need for an additional __rc_check call from here
6385 //discard(len_with_eol);
6386 __eol_consumed_seq = v.substr(len, len_with_eol - len); // Save EoL sequence
6387 v.resize(len); // Truncate string without the EoL sequence
6388 return v;
6389 } // -x- if len -x-
6390
6391 // --------------------------------------------------------------------------
6392 // If EoF wasn't found and the maximum line length has been reached, then we
6393 // need to end this loop so that we can handle any recvline_flags and/or
6394 // throw an xEOVERFLOW exception.
6395 // --------------------------------------------------------------------------
6396 } while (br != buf_size); // Loop again if buffer isn't full yet
6397
6398 // --------------------------------------------------------------------------
6399 // If a timeout occurred, then throw the randolf::rex::xETIMEDOUT exception.
6400 // --------------------------------------------------------------------------
6401 if (timeout != 0)
6402 if (time(0) > timeout_target)
6403 throw randolf::rex::xETIMEDOUT("recvline timed out"); // We reached the line timeout target
6404
6405 // ==========================================================================
6406 // We've exited the loop, and now we're deciding how to handle a full buffer
6407 // that doesn't have an EoF, and obviously indicates line-overflow situation.
6408 // ==========================================================================
6409
6410 // --------------------------------------------------------------------------
6411 // Caller wants partial data instead of throwing an exception, even if it has
6412 // no EoL sequence.
6413 // --------------------------------------------------------------------------
6414 if (recvline_flags & RECVLINE_PARTIAL) {
6415 __eol_consumed_seq = ""; // Indicate that there's no EoL sequence
6416 if (!(posix_flags & MSG_PEEK)) __recv(v.data(), br, posix_flags & (~MSG_PEEK)); // Consume data if not in MSG_PEEK mode / __recv will throw exception if something goes wrong so there's no need for an additional __rc_check call from here
6417 v.resize(br); // Truncate string to "br" (bytes received)
6418 return v;
6419 } // -x- if RECVLINE_PARTIAL -x-
6420
6421 // --------------------------------------------------------------------------
6422 // Generate xEOVERFLOW exception.
6423 //
6424 // TODO: Throw a different exception for no EoL when the buffer isn't full.
6425 // --------------------------------------------------------------------------
6426 if (!(recvline_flags & RECVLINE_NO_DISCARD_ON_OVERFLOW) && !(posix_flags & MSG_PEEK))
6427 discard_line(); // Consume all that remains, or stop after first EoL sequence encountered
6428 //std::cout << "discard_line=" << discard_line() << std::endl;
6429 throw randolf::rex::xEOVERFLOW("recvline overflow (line too long or missing EoL sequence)");
6430
6431 } // -x- std::string __recvline -x-
6432
6433 public:
6434 /*======================================================================*//**
6435 @brief
6436 Receive a line of data from the endpoint, with the EoL character(s) removed.
6437 While this is meant for ASCII and UTF-8 text, it will also work with binary
6438 data that doesn't include EoL character sequences as non-line-ending data
6439 (when receiving binary data, the @ref recv() and @ref recvz() methods tend to
6440 be better-suited).
6441
6442 If @c nbytes is 0, then the internal buffer_size will be used as the default,
6443 which can also be changed from its compiled-in default of 1024 by using one
6444 of the buffer_size() methods. The total number of bytes received, including
6445 the EoL sequence, will not exceed the total number of byte specified with the
6446 @c nbytes parameter.
6447 @warning
6448 For @c nbytes the EoL character sequence size should be included, especially
6449 for shorter lines -- if the EoL character sequence is detected automatically
6450 on-the-fly (which is the default), then provisioning 2 characters is needed
6451 since an EoL sequence could be 1 or 2 characters long (line lengths shorter
6452 than 3 characters could be particularly problematic when detecting on-the-fly
6453 EoL character sequences). In such a case, a better approach is to allow for
6454 2 additional characters and then test the length of the returned string to
6455 ensure it doesn't exceed whatever the required maximum is (and throw the same
6456 @ref randolf::rex::xEOVERFLOW exception if the length is exceeded).
6457
6458 For more information about which characters an EoL sequence is comprised of,
6459 and how our specialized support for multiple EoL sequences works, see the
6460 documentation for the various @ref eol methods.
6461
6462 @pre
6463 When setting the recvline timeout (either with the @c timeout parameter with
6464 this method, or by using the @ref timeout_recvline(long) method), you may
6465 also need to set the socket timeout beforehand using the @ref timeout()
6466 method, because @c recvline doesn't do this...
6467 @n
6468 <blockquote>
6469 @ref timeout_recvline sets the overall total timeout for an entire line to
6470 be entered
6471 @n
6472 @ref timeout sets the timeout between individual characters received (such
6473 as keystrokes from a live end-user)
6474 </blockquote>
6475 @n
6476 If your socket timeout is longer than then recvline timeout, this will have
6477 the effect of rendering the recvling timeout as ineffective.
6478
6479 The @c timeout parameter can be used to override the total overall timeout
6480 for receiving a line, which is useful for specific situations where a
6481 specific timeout is desired for particular prompts, during certain data entry
6482 stages, etc.
6483
6484 @post
6485 When a line is not found (@c randolf::rex::xEAGAIN), or a line is too long
6486 because there's too much data (longer than a line) without an EoL sequence
6487 (@c randolf::rex::xOVERFLOW), then that data will not be discarded or
6488 consumed, and will remain ready for receiving (@ref recv) or for discarding
6489 @ref discard).
6490
6491 @par Notes
6492 If you're searching for a readline() method for socket I/O, then this is most
6493 likely what you're wanting/needing.
6494
6495 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
6496 @throws randolf::rex::xEBADF The underlying socket is not open
6497 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6498 connections
6499 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6500 part of the user address space
6501 @throws randolf::rex::xEINTR Interrupted by a signal
6502 @throws randolf::rex::xEINVAL Invalid argument passed
6503 @throws randolf::rex::xENOMEM Insufficient memory
6504 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6505 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6506 doesn't refer to a socket
6507 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
6508 no EoL character sequence is detected after recvline's buffer became
6509 full (whatever data is waiting may still be received using any of the
6510 recv_ methods {with or without the @c MSG_PEEK flag set}, including
6511 @c recvline() with a larger buffer {see the @c nbytes parameter})
6512 @throws randolf::rex::xERANGE if the timeout parameter is below 0
6513 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6514 there's no new data
6515
6516 @returns One line of text as a std::string object.
6517 @see discard
6518 @see eol()
6519 @see eol_consumed_seq()
6520 @see recv_rline
6521 @see sendline(const std::string, const int)
6522 @see timeout
6523 @see timeout_recvline
6524 @see timeout_recvline(long)
6525 @qualifier TLS
6526 *///=========================================================================
6527 std::string recvline(
6528 /// Maximum number of bytes to receive (including EoL character sequence)@n
6529 /// 0 = use internal @ref buffer_size()
6530 const size_t nbytes = 0,
6531 /// MSG_OOB@n
6532 /// MSG_PEEK@n
6533 /// MSG_WAITALL@n
6534 /// MSG_DONTWAIT@n
6535 /// MSG_CMSG_CLOEXEC
6536 const int posix_flags = 0,
6537 /// Line timeout (in seconds)@n
6538 /// 0 = no timeout (default) or whatever @ref timeout was set to, unless it
6539 /// was configured by way of the @ref timeout_recvline(long) method
6540 long timeout = 0,
6541 /// Configuration parameters
6542 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
6543 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
6544 // + (__eol.size() == 0 ? 2 : __eol.size()); // If 0, then set this to 2 bytes for automatic detection of CRLF; TODO: Add an option for this
6545 // Internally, the length of the @ref eol() sequence is added to the buffer size
6546 // so that the maximum line length without EoL characters matches the maximum
6547 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
6548 if (__debug) debug("recvline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6549 + ", " + std::to_string(buf_size)
6550 + ", " + std::to_string(posix_flags)
6551 + ");");
6552 return __recvline(buf_size, posix_flags, timeout, recvline_flags);
6553 } // -x- std::string recvline -x-
6554
6555 /*======================================================================*//**
6556 @brief
6557 Receive data in the form of a "msghdr" structure.
6558 @warning
6559 This method is not compatible with TLS.
6560
6561 @throws randolf::rex::xEBADF The underlying socket is not open
6562 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6563 connections
6564 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6565 part of the user address space
6566 @throws randolf::rex::xEINTR Interrupted by a signal
6567 @throws randolf::rex::xEINVAL Invalid argument passed
6568 @throws randolf::rex::xENOMEM Insufficient memory
6569 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6570 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6571 doesn't refer to a socket
6572 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6573 there's no new data
6574
6575 @returns pointer to "msghdr" structure
6576 @see sendmsg
6577 @qualifier POSIX
6578 *///=========================================================================
6579 msghdr* recvmsg(
6580 /// Pointer to "msghdr" structure (or nullptr for automatic allocation)
6581 msghdr* msg,
6582 /// MSG_OOB@n
6583 /// MSG_PEEK@n
6584 /// MSG_WAITALL@n
6585 /// MSG_DONTWAIT@n
6586 /// MSG_CMSG_CLOEXEC
6587 const int posix_flags = 0) {
6588 if (__debug) debug("recvmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6589 + ", <msghdr>"
6590 + ", " + std::to_string(posix_flags)
6591 + ");");
6592 if (msg == nullptr) msg = new msghdr{0};
6593 __track_bytes_rx(__rc_check(::recvmsg(__socket_fd, msg, posix_flags)));
6594 return msg;
6595 } // -x- msghdr* recvmsg -x-
6596
6597 /*======================================================================*//**
6598 @brief
6599 Receive data in the form of an "mmsghdr" structure.
6600 @warning
6601 This method is not compatible with TLS.
6602
6603 @throws randolf::rex::xEBADF The underlying socket is not open
6604 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6605 connections
6606 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6607 part of the user address space
6608 @throws randolf::rex::xEINTR Interrupted by a signal
6609 @throws randolf::rex::xEINVAL Invalid argument passed
6610 @throws randolf::rex::xENOMEM Insufficient memory
6611 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6612 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6613 doesn't refer to a socket
6614 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6615 there's no new data
6616
6617 @returns pointer to "mmsghdr" structure
6618 @see sendmmsg
6619 @qualifier POSIX
6620 *///=========================================================================
6621 mmsghdr* recvmmsg(
6622 /// Pointer to "mmsghdr" structure (or nullptr for automatic allocation)
6623 struct mmsghdr* mmsg,
6624 /// Size of target endpoint structure
6625 const unsigned int vlen = sizeof(mmsghdr),
6626 /// MSG_DONTROUTE@n
6627 /// MSG_DONTWAIT@n
6628 /// MSG_EOR@n
6629 /// MSG_NOSIGNAL@n
6630 /// MSG_OOB
6631 const int posix_flags = 0,
6632 /// Timeout
6633 struct timespec* timeout = {0}) {
6634 if (__debug) debug("recvmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6635 + ", <mmsghdr>"
6636 + ", " + std::to_string(vlen)
6637 + ", " + std::to_string(posix_flags)
6638 + ", timeout{tv_sec=" + std::to_string(timeout->tv_sec)
6639 + ", tv_nsec=" + std::to_string(timeout->tv_nsec) + "}"
6640 + ");");
6641 __track_bytes_rx(__rc_check(::recvmmsg(__socket_fd, mmsg, vlen, posix_flags, timeout)));
6642 return mmsg;
6643 } // -x- mmsghdr* recvmmsg -x-
6644
6645 /*======================================================================*//**
6646 @brief
6647 Receive data from the endpoint, and add a 0 (null) onto the end. This is
6648 useful when using the resulting std::vector<char> as an ASCIIZ string.
6649
6650 If nbytes is 0, then the internal buffer_size will be used as the default,
6651 which can also be changed from its compiled-in default of 1024 by using one
6652 of the buffer_size() methods.
6653
6654 @post
6655 The resulting std::vector size is always inflated by 1. This means that
6656 relying on a comparison against 0 will result in an infinite loop:
6657 @code{.cpp}
6658 for (std::vector<char> v = r.recvz(); v.size > 0; v = recvz()) { ... }
6659 @endcode
6660 So, you'll need to compare against 1 instead of 0 to compensate fot the
6661 inflated size due to the addition of null character 0:
6662 @code{.cpp}
6663 for (std::vector<char> v = r.recvz(); v.size > 1; v = recvz()) { ... }
6664 @endcode
6665
6666 @warning
6667 The resulting size of std::vector<char> will be <tt>nbytes + 1</tt> which
6668 ensures that any string processing functions or presentation libraries - such
6669 as @c printf() - expecting an ASCIIZ string buffer will stop at null (0), and
6670 will reliably output no more than the maximum size specified.
6671 @n@n
6672 The way to think of this is that @c nbytes specifies the maximum number of
6673 bytes (a.k.a., characters) to recieve over the underlying socket, and the
6674 final 0 (null) being added is not included in the maximum specified by the
6675 @c nbytes parameter.
6676
6677 @par Notes
6678 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6679 reception, but it's important to note that it does implement temporary
6680 blocking while waiting for data.
6681
6682 @throws randolf::rex::xEBADF The underlying socket is not open
6683 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6684 connections
6685 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6686 part of the user address space
6687 @throws randolf::rex::xEINTR Interrupted by a signal
6688 @throws randolf::rex::xEINVAL Invalid argument passed
6689 @throws randolf::rex::xENOMEM Insufficient memory
6690 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6691 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6692 doesn't refer to a socket
6693 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6694 there's no new data
6695
6696 @returns appropriately-sized vector of characters + 1 (includes additional
6697 null terminator character 0)
6698 @see recv(const size_t, const int)
6699 @see recv(std::vector<char>, const int)
6700 @see sendz
6701 @qualifier TLS
6702 *///=========================================================================
6703 std::vector<char> recvz(
6704 /// Maximum number of bytes to receive
6705 const size_t nbytes = 0,
6706 /// MSG_OOB@n
6707 /// MSG_PEEK@n
6708 /// MSG_WAITALL@n
6709 /// MSG_DONTWAIT@n
6710 /// MSG_CMSG_CLOEXEC
6711 const int posix_flags = 0) {
6712 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6713 if (__debug) debug("recvz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6714 + ", <buf>"
6715 + ", " + std::to_string(buf_size)
6716 + ", " + std::to_string(posix_flags)
6717 + ");");
6718 std::vector<char> v(buf_size); // TODO: This may need to be changed to std::vector<char>*
6719 v.resize(__recv(v.data(), v.size(), posix_flags) + 1); // Add 1 to include room for final null character 0
6720 v[v.size() - 1] = (char)0; // Set final element to 0 (null)
6721 return v;
6722 } // -x- std::vector<char> recvz -x-
6723
6724 private:
6725 /*======================================================================*//**
6726 @brief
6727 This is an internal function that:
6728 1. sends data to the endpoint:
6729 - via underlying socket when TLS is not enabled
6730 - via the OpenSSL socket API when TLS is enabled
6731 2. checks for a socket I/O error (and throws an exception)
6732 3. tracks number of bytes transmitted (if there were no errors)
6733
6734 @par Threads
6735 This method is threadsafe because the underlying POSIX send() and OpenSSL's
6736 SSL_write() functions are threadsafe.
6737
6738 @throws randolf::rex::xEBADF The underlying socket is not open
6739 @throws randolf::rex::xECONNRESET Connect reset by peer
6740 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6741 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6742 part of the user address space
6743 @throws randolf::rex::xEINTR Interrupted by a signal
6744 @throws randolf::rex::xEINVAL Invalid argument passed
6745 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6746 occur, but the POSIX sockets documentation lists it as one of the
6747 errors that can be returned, perhaps because some incorrectly
6748 implemented TCP/IP stacks return this error?)
6749 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6750 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6751 and 65,527 bytes for IPv6)
6752 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6753 network congestion (or, less commonly, insufficient memory)
6754 @throws randolf::rex::xENOMEM Insufficient memory
6755 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6756 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6757 doesn't refer to a socket
6758 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6759 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6760 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6761 isn't set)
6762 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6763 there's no new data
6764 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6765 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6766 but it really isn't)
6767
6768 @returns Number of bytes that were successfully transmitted
6769 @see __recv
6770 @qualifier TLS
6771 *///=========================================================================
6772 int __send(
6773 /// Pointer to data
6774 const void* data,
6775 /// Length of message data
6776 const size_t len,
6777 /// Flags (ignored with encrypted streams)
6778 const int posix_flags = 0) {
6779 if (__tls) return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data, len)));
6780 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data, len, posix_flags)));
6781 } // -x- int __send -x-
6782
6783 /*======================================================================*//**
6784 @brief
6785 This is an internal function that:
6786 1. sends data to the endpoint:
6787 - via underlying socket when TLS is not enabled
6788 - via the OpenSSL socket API when TLS is enabled
6789 2. checks for a socket I/O error (and throws an exception)
6790 3. tracks number of bytes transmitted (if there were no errors)
6791
6792 @par Threads
6793 This method is threadsafe because the underlying POSIX send() and OpenSSL's
6794 SSL_write() functions are threadsafe.
6795
6796 @throws randolf::rex::xEBADF The underlying socket is not open
6797 @throws randolf::rex::xECONNRESET Connect reset by peer
6798 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6799 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6800 part of the user address space
6801 @throws randolf::rex::xEINTR Interrupted by a signal
6802 @throws randolf::rex::xEINVAL Invalid argument passed
6803 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6804 occur, but the POSIX sockets documentation lists it as one of the
6805 errors that can be returned, perhaps because some incorrectly
6806 implemented TCP/IP stacks return this error?)
6807 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6808 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6809 and 65,527 bytes for IPv6)
6810 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6811 network congestion (or, less commonly, insufficient memory)
6812 @throws randolf::rex::xENOMEM Insufficient memory
6813 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6814 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6815 doesn't refer to a socket
6816 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6817 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6818 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6819 isn't set)
6820 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6821 there's no new data
6822 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6823 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6824 but it really isn't)
6825
6826 @returns Number of bytes that were successfully transmitted
6827 @see __recvline
6828 @qualifier TLS
6829 *///=========================================================================
6830 int __sendline(
6831 /// Pointer to data
6832 const void* data,
6833 /// Number of bytes to send
6834 const size_t len,
6835 /// Flags (ignored with encrypted streams)
6836 const int posix_flags = 0) {
6837 if (__tls) { // Encrypted
6838 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data , len )))
6839 + __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.data(), __eol_out.length())));
6840 } else { // Not encrypted
6841 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data , len, posix_flags | MSG_MORE))) // MSG_MORE prevents extra packets from being sent
6842 + __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.data(), __eol_out.length(), posix_flags )));
6843 } // -x- if __tls -x-
6844 } // -x- int __sendline -x-
6845
6846 /*======================================================================*//**
6847 @brief
6848 This is an internal function that:
6849 1. sends data to the endpoint:
6850 - via underlying socket when TLS is not enabled
6851 - via the OpenSSL socket API when TLS is enabled
6852 2. checks for a socket I/O error (and throws an exception)
6853 3. tracks number of bytes transmitted (if there were no errors)
6854
6855 @par Threads
6856 This method is threadsafe because the underlying POSIX send() and OpenSSL's
6857 SSL_write() functions are threadsafe.
6858
6859 @throws randolf::rex::xEBADF The underlying socket is not open
6860 @throws randolf::rex::xECONNRESET Connect reset by peer
6861 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6862 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6863 part of the user address space
6864 @throws randolf::rex::xEINTR Interrupted by a signal
6865 @throws randolf::rex::xEINVAL Invalid argument passed
6866 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6867 occur, but the POSIX sockets documentation lists it as one of the
6868 errors that can be returned, perhaps because some incorrectly
6869 implemented TCP/IP stacks return this error?)
6870 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6871 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6872 and 65,527 bytes for IPv6)
6873 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6874 network congestion (or, less commonly, insufficient memory)
6875 @throws randolf::rex::xENOMEM Insufficient memory
6876 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6877 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6878 doesn't refer to a socket
6879 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6880 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6881 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6882 isn't set)
6883 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6884 there's no new data
6885 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6886 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6887 but it really isn't)
6888
6889 @returns Number of bytes that were successfully transmitted
6890 @qualifier TLS
6891 *///=========================================================================
6892 int __send_eol(
6893 /// Flags (ignored with encrypted streams)
6894 const int posix_flags = 0) {
6895 if (__tls) { // Encrypted
6896 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.data(), __eol_out.length())));
6897 } else { // Not encrypted
6898 return __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.data(), __eol_out.length(), posix_flags)));
6899 } // -x- if __tls -x-
6900 } // -x- int __send_eol -x-
6901 // TODO: Create a recv_eol() method that some people will probably consider to be evil (ha ha!)
6902 // recv_eol() will need to be able to auto-detect the CRLF sequence on-the-fly (if necessary) just like recvline() does
6903
6904 public:
6905 /*======================================================================*//**
6906 @brief
6907 Send data in the form of a std::string to the 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 @see recv
6943 @qualifier TLS
6944 *///=========================================================================
6945 rsocket* send(
6946 /// Data to send
6947 const std::string msg,
6948 /// MSG_DONTROUTE@n
6949 /// MSG_DONTWAIT@n
6950 /// MSG_EOR@n
6951 /// MSG_MORE@n
6952 /// MSG_NOSIGNAL@n
6953 /// MSG_OOB
6954 const int posix_flags = 0) {
6955 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6956 + ", <std::string>"
6957 + ", " + std::to_string(msg.length())
6958 + ", " + std::to_string(posix_flags)
6959 + ");");
6960 __send(msg.data(), msg.length(), posix_flags);
6961 return this;
6962 } // -x- rsocket* send -x-
6963
6964 /*======================================================================*//**
6965 @brief
6966 Send data in the form of a std::vector<char> to the endpoint.
6967 @par Threads
6968 This method is threadsafe.
6969
6970 @throws randolf::rex::xEBADF The underlying socket is not open
6971 @throws randolf::rex::xECONNRESET Connect reset by peer
6972 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
6973 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6974 part of the user address space
6975 @throws randolf::rex::xEINTR Interrupted by a signal
6976 @throws randolf::rex::xEINVAL Invalid argument passed
6977 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
6978 occur, but the POSIX sockets documentation lists it as one of the
6979 errors that can be returned, perhaps because some incorrectly
6980 implemented TCP/IP stacks return this error?)
6981 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
6982 sent atomically (the maximum size is typically 65,507 bytes for IPv4
6983 and 65,527 bytes for IPv6)
6984 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
6985 network congestion (or, less commonly, insufficient memory)
6986 @throws randolf::rex::xENOMEM Insufficient memory
6987 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6988 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6989 doesn't refer to a socket
6990 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
6991 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
6992 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
6993 isn't set)
6994 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6995 there's no new data
6996 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
6997 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
6998 but it really isn't)
6999
7000 @returns The same rsocket object so as to facilitate stacking
7001 @see recv(std::vector<char>, const int)
7002 @qualifier TLS
7003 *///=========================================================================
7004 rsocket* send(
7005 /// Data to send
7006 const std::vector<char> msg,
7007 /// MSG_DONTROUTE@n
7008 /// MSG_DONTWAIT@n
7009 /// MSG_EOR@n
7010 /// MSG_MORE@n
7011 /// MSG_NOSIGNAL@n
7012 /// MSG_OOB
7013 const int posix_flags = 0) {
7014 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7015 + ", <std::vector>"
7016 + ", " + std::to_string(msg.size())
7017 + ", " + std::to_string(posix_flags)
7018 + ");");
7019 __send(msg.data(), msg.size(), posix_flags);
7020 return this;
7021 } // -x- rsocket* send -x-
7022
7023 /*======================================================================*//**
7024 @brief
7025 Send data in the form of a C-string to the endpoint.
7026 @par Threads
7027 This method is threadsafe.
7028
7029 @throws randolf::rex::xEBADF The underlying socket is not open
7030 @throws randolf::rex::xECONNRESET Connect reset by peer
7031 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7032 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7033 part of the user address space
7034 @throws randolf::rex::xEINTR Interrupted by a signal
7035 @throws randolf::rex::xEINVAL Invalid argument passed
7036 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7037 occur, but the POSIX sockets documentation lists it as one of the
7038 errors that can be returned, perhaps because some incorrectly
7039 implemented TCP/IP stacks return this error?)
7040 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7041 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7042 and 65,527 bytes for IPv6)
7043 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7044 network congestion (or, less commonly, insufficient memory)
7045 @throws randolf::rex::xENOMEM Insufficient memory
7046 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7047 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7048 doesn't refer to a socket
7049 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7050 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7051 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7052 isn't set)
7053 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7054 there's no new data
7055 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7056 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7057 but it really isn't)
7058
7059 @returns The same rsocket object so as to facilitate stacking
7060 @see recv(const size_t, const int)
7061 @qualifier POSIX
7062 @qualifier TLS
7063 *///=========================================================================
7064 rsocket* send(
7065 /// Pointer to data to send
7066 const char* msg,
7067 /// Number of bytes to send, or 0 to auto-detect length if message is an ASCIIZ string
7068 size_t len = 0,
7069 /// MSG_DONTROUTE@n
7070 /// MSG_DONTWAIT@n
7071 /// MSG_EOR@n
7072 /// MSG_MORE@n
7073 /// MSG_NOSIGNAL@n
7074 /// MSG_OOB
7075 const int posix_flags = 0) {
7076 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7077 + ", <msg>"
7078 + ", " + std::to_string(len)
7079 + ", " + std::to_string(posix_flags)
7080 + ");");
7081
7082 // --------------------------------------------------------------------------
7083 // Measure size of format string if an ASCIIZ string was indicated.
7084 // --------------------------------------------------------------------------
7085 if (len == 0) len = std::strlen(msg);
7086
7087 // --------------------------------------------------------------------------
7088 // Send string.
7089 // --------------------------------------------------------------------------
7090 __send(msg, len, posix_flags);
7091 return this;
7092 } // -x- rsocket* send -x-
7093
7094 /*======================================================================*//**
7095 @brief
7096 Send data in the form of an ASCIIZ string to the endpoint, including the
7097 terminating NULL character.
7098 @par Threads
7099 This method is threadsafe.
7100
7101 @throws randolf::rex::xEBADF The underlying socket is not open
7102 @throws randolf::rex::xECONNRESET Connect reset by peer
7103 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7104 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7105 part of the user address space
7106 @throws randolf::rex::xEINTR Interrupted by a signal
7107 @throws randolf::rex::xEINVAL Invalid argument passed
7108 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7109 occur, but the POSIX sockets documentation lists it as one of the
7110 errors that can be returned, perhaps because some incorrectly
7111 implemented TCP/IP stacks return this error?)
7112 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7113 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7114 and 65,527 bytes for IPv6)
7115 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7116 network congestion (or, less commonly, insufficient memory)
7117 @throws randolf::rex::xENOMEM Insufficient memory
7118 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7119 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7120 doesn't refer to a socket
7121 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7122 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7123 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7124 isn't set)
7125 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7126 there's no new data
7127 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7128 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7129 but it really isn't)
7130
7131 @returns The same rsocket object so as to facilitate stacking
7132 @see recv_asciiz(const size_t, const int)
7133 @see sendz(const char*, const int) which doesn't transmit the terminating
7134 NULL character
7135 @qualifier TLS
7136 *///=========================================================================
7137 rsocket* send_asciiz(
7138 /// Pointer to data to send
7139 const char* msg,
7140 /// MSG_DONTROUTE@n
7141 /// MSG_DONTWAIT@n
7142 /// MSG_EOR@n
7143 /// MSG_MORE@n
7144 /// MSG_NOSIGNAL@n
7145 /// MSG_OOB
7146 const int posix_flags = 0) {
7147 if (__debug) debug("send_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7148 + ", <msg>"
7149 + ", " + std::to_string(std::strlen(msg) + 1)
7150 + ", " + std::to_string(posix_flags)
7151 + ");");
7152 __send(msg, std::strlen(msg) + 1, posix_flags);
7153 return this;
7154 } // -x- rsocket* send_asciiz -x-
7155
7156 /*======================================================================*//**
7157 @brief
7158 Send one 8-bit byte (one unsigned character) of data to the endpoint.
7159 @par Threads
7160 This method is threadsafe.
7161
7162 @throws randolf::rex::xEBADF The underlying socket is not open
7163 @throws randolf::rex::xECONNRESET Connect reset by peer
7164 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7165 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7166 part of the user address space
7167 @throws randolf::rex::xEINTR Interrupted by a signal
7168 @throws randolf::rex::xEINVAL Invalid argument passed
7169 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7170 occur, but the POSIX sockets documentation lists it as one of the
7171 errors that can be returned, perhaps because some incorrectly
7172 implemented TCP/IP stacks return this error?)
7173 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7174 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7175 and 65,527 bytes for IPv6)
7176 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7177 network congestion (or, less commonly, insufficient memory)
7178 @throws randolf::rex::xENOMEM Insufficient memory
7179 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7180 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7181 doesn't refer to a socket
7182 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7183 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7184 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7185 isn't set)
7186 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7187 there's no new data
7188 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7189 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7190 but it really isn't)
7191
7192 @returns The same rsocket object so as to facilitate stacking
7193 @see recv_byte
7194 @see recv_char
7195 @see send_char
7196 @qualifier TLS
7197 *///=========================================================================
7198 rsocket* send_byte(
7199 /// Data to send
7200 const u_char value,
7201 /// MSG_DONTROUTE@n
7202 /// MSG_DONTWAIT@n
7203 /// MSG_EOR@n
7204 /// MSG_MORE@n
7205 /// MSG_NOSIGNAL@n
7206 /// MSG_OOB
7207 const int posix_flags = 0) {
7208 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7209 + ", <value>"
7210 + ", " + std::to_string(sizeof(value))
7211 + ", " + std::to_string(posix_flags)
7212 + ");");
7213 __send(&value, sizeof(value), posix_flags);
7214 return this;
7215 } // -x- rsocket* send_byte -x-
7216
7217 /*======================================================================*//**
7218 @brief
7219 Send one signed character (one 8-bit byte) of data to the endpoint.
7220 @copydoc send_byte
7221 @returns The same rsocket object so as to facilitate stacking
7222 @see recv_byte
7223 @see recv_char
7224 @see send_byte
7225 @qualifier TLS
7226 *///=========================================================================
7227 rsocket* send_char(
7228 /// Data to send
7229 const char value,
7230 /// MSG_DONTROUTE@n
7231 /// MSG_DONTWAIT@n
7232 /// MSG_EOR@n
7233 /// MSG_MORE@n
7234 /// MSG_NOSIGNAL@n
7235 /// MSG_OOB
7236 const int posix_flags = 0) {
7237 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7238 + ", <value>"
7239 + ", " + std::to_string(sizeof(value))
7240 + ", " + std::to_string(posix_flags)
7241 + ");");
7242 __send(&value, sizeof(value), posix_flags);
7243 return this;
7244 } // -x- rsocket* send_char -x-
7245
7246 /*======================================================================*//**
7247 @brief
7248 Send the EoL sequence to the endpoint.
7249 @par Threads
7250 This method is threadsafe.
7251
7252 @throws randolf::rex::xEBADF The underlying socket is not open
7253 @throws randolf::rex::xECONNRESET Connect reset by peer
7254 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7255 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7256 part of the user address space
7257 @throws randolf::rex::xEINTR Interrupted by a signal
7258 @throws randolf::rex::xEINVAL Invalid argument passed
7259 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7260 occur, but the POSIX sockets documentation lists it as one of the
7261 errors that can be returned, perhaps because some incorrectly
7262 implemented TCP/IP stacks return this error?)
7263 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7264 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7265 and 65,527 bytes for IPv6)
7266 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7267 network congestion (or, less commonly, insufficient memory)
7268 @throws randolf::rex::xENOMEM Insufficient memory
7269 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7270 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7271 doesn't refer to a socket
7272 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7273 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7274 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7275 isn't set)
7276 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7277 there's no new data
7278 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7279 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7280 but it really isn't)
7281
7282 @returns The same rsocket object so as to facilitate stacking
7283 @see recvline
7284 @see recv_rline
7285 @see sendline
7286 @see send_rline
7287 @qualifier TLS
7288 *///=========================================================================
7289 rsocket* send_eol(
7290 /// MSG_DONTROUTE@n
7291 /// MSG_DONTWAIT@n
7292 /// MSG_EOR@n
7293 /// MSG_MORE@n
7294 /// MSG_NOSIGNAL@n
7295 /// MSG_OOB
7296 const int posix_flags = 0) {
7297 if (__debug) debug("send_eol(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7298 + ", " + std::to_string(posix_flags)
7299 + ");");
7300 __send_eol(posix_flags);
7301 return this;
7302 } // -x- rsocket* send_eol -x-
7303
7304 /*======================================================================*//**
7305 @brief
7306 Send data in the form of a @ref randolf::rline to the endpoint, with an EoL
7307 sequence appended.
7308 @par Threads
7309 This method is threadsafe.
7310
7311 @throws randolf::rex::xEBADF The underlying socket is not open
7312 @throws randolf::rex::xECONNRESET Connect reset by peer
7313 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7314 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7315 part of the user address space
7316 @throws randolf::rex::xEINTR Interrupted by a signal
7317 @throws randolf::rex::xEINVAL Invalid argument passed
7318 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7319 occur, but the POSIX sockets documentation lists it as one of the
7320 errors that can be returned, perhaps because some incorrectly
7321 implemented TCP/IP stacks return this error?)
7322 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7323 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7324 and 65,527 bytes for IPv6)
7325 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7326 network congestion (or, less commonly, insufficient memory)
7327 @throws randolf::rex::xENOMEM Insufficient memory
7328 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7329 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7330 doesn't refer to a socket
7331 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7332 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7333 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7334 isn't set)
7335 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7336 there's no new data
7337 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7338 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7339 but it really isn't)
7340
7341 @returns The same rsocket object so as to facilitate stacking
7342 @see eol
7343 @see printfline
7344 @see recv_rline
7345 @see recvline
7346 @see sendline
7347 @see vprintfline
7348 @qualifier TLS
7349 *///=========================================================================
7350 rsocket* send_rline(
7351 /// Data to send
7352 randolf::rline line = randolf::rline(),
7353 /// MSG_DONTROUTE@n
7354 /// MSG_DONTWAIT@n
7355 /// MSG_EOR@n
7356 /// MSG_MORE@n
7357 /// MSG_NOSIGNAL@n
7358 /// MSG_OOB
7359 const int posix_flags = 0) {
7360 if (__debug) debug("sendline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7361 + ", <randolf::rline>"
7362 + ", " + std::to_string(line.length())
7363 + ", " + std::to_string(posix_flags)
7364 + ");");
7365 __sendline(line.data(), line.length(), posix_flags);
7366 return this;
7367 } // -x- rsocket* send_rline -x-
7368
7369 /*======================================================================*//**
7370 @brief
7371 Send a data structure to the endpoint.
7372 @pre
7373 MSB/LSB considerations are important for any integers within your structure,
7374 so be sure to sanitize them with htons(), ntohs(), and related methods prior
7375 to sending, and then after receiving. This way, your data will be protected
7376 against corruption resulting from byte order differences when communicating
7377 between hardware architectures that differ in MSB and LSB byte ordering.
7378 @par Threads
7379 This method is threadsafe.
7380
7381 @throws randolf::rex::xEBADF The underlying socket is not open
7382 @throws randolf::rex::xECONNRESET Connect reset by peer
7383 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7384 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7385 part of the user address space
7386 @throws randolf::rex::xEINTR Interrupted by a signal
7387 @throws randolf::rex::xEINVAL Invalid argument passed
7388 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7389 occur, but the POSIX sockets documentation lists it as one of the
7390 errors that can be returned, perhaps because some incorrectly
7391 implemented TCP/IP stacks return this error?)
7392 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7393 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7394 and 65,527 bytes for IPv6)
7395 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7396 network congestion (or, less commonly, insufficient memory)
7397 @throws randolf::rex::xENOMEM Insufficient memory
7398 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7399 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7400 doesn't refer to a socket
7401 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7402 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7403 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7404 isn't set)
7405 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7406 there's no new data
7407 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7408 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7409 but it really isn't)
7410
7411 @returns The same rsocket object so as to facilitate stacking
7412 @see recv_struct
7413 @qualifier TLS
7414 *///=========================================================================
7415 template <typename T> rsocket* send_struct(
7416 /// Data to send
7417 const T value,
7418 /// MSG_DONTROUTE@n
7419 /// MSG_DONTWAIT@n
7420 /// MSG_EOR@n
7421 /// MSG_MORE@n
7422 /// MSG_NOSIGNAL@n
7423 /// MSG_OOB
7424 const int posix_flags = 0) {
7425 if (__debug) debug("send_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7426 + ", <value>"
7427 + ", " + std::to_string(sizeof(value))
7428 + ", " + std::to_string(posix_flags)
7429 + ");");
7430 __send(&value, sizeof(value), posix_flags);
7431 return this;
7432 } // -x- rsocket* send_struct -x-
7433
7434 /*======================================================================*//**
7435 @brief
7436 Send one 16-bit unsigned integer of data in LSB (little endian) order to the
7437 endpoint.
7438 @par Threads
7439 This method is threadsafe.
7440
7441 @throws randolf::rex::xEBADF The underlying socket is not open
7442 @throws randolf::rex::xECONNRESET Connect reset by peer
7443 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7444 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7445 part of the user address space
7446 @throws randolf::rex::xEINTR Interrupted by a signal
7447 @throws randolf::rex::xEINVAL Invalid argument passed
7448 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7449 occur, but the POSIX sockets documentation lists it as one of the
7450 errors that can be returned, perhaps because some incorrectly
7451 implemented TCP/IP stacks return this error?)
7452 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7453 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7454 and 65,527 bytes for IPv6)
7455 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7456 network congestion (or, less commonly, insufficient memory)
7457 @throws randolf::rex::xENOMEM Insufficient memory
7458 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7459 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7460 doesn't refer to a socket
7461 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7462 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7463 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7464 isn't set)
7465 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7466 there's no new data
7467 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7468 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7469 but it really isn't)
7470
7471 @returns The same rsocket object so as to facilitate stacking
7472 @see recv_uint16_lsb
7473 @see send_uint16_msb
7474 @qualifier TLS
7475 *///=========================================================================
7476 rsocket* send_uint16_lsb(
7477 /// Data to send
7478 const uint16_t value,
7479 /// MSG_DONTROUTE@n
7480 /// MSG_DONTWAIT@n
7481 /// MSG_EOR@n
7482 /// MSG_MORE@n
7483 /// MSG_NOSIGNAL@n
7484 /// MSG_OOB
7485 const int posix_flags = 0) {
7486 uint16_t buf = !__endian_is_msb ? value : ntohs(value);
7487 if (__debug) debug("send_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7488 + ", " + std::to_string(value)
7489 + ", " + std::to_string(sizeof(buf))
7490 + ", " + std::to_string(posix_flags)
7491 + ");");
7492 __send(&buf, sizeof(buf), posix_flags);
7493 return this;
7494 } // -x- rsocket* send_uint16_lsb -x-
7495
7496 /*======================================================================*//**
7497 @brief
7498 Send one 16-bit integer of data in MSB (big endian) order to the endpoint.
7499 @par Threads
7500 This method is threadsafe.
7501
7502 @throws randolf::rex::xEBADF The underlying socket is not open
7503 @throws randolf::rex::xECONNRESET Connect reset by peer
7504 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7505 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7506 part of the user address space
7507 @throws randolf::rex::xEINTR Interrupted by a signal
7508 @throws randolf::rex::xEINVAL Invalid argument passed
7509 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7510 occur, but the POSIX sockets documentation lists it as one of the
7511 errors that can be returned, perhaps because some incorrectly
7512 implemented TCP/IP stacks return this error?)
7513 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7514 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7515 and 65,527 bytes for IPv6)
7516 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7517 network congestion (or, less commonly, insufficient memory)
7518 @throws randolf::rex::xENOMEM Insufficient memory
7519 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7520 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7521 doesn't refer to a socket
7522 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7523 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7524 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7525 isn't set)
7526 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7527 there's no new data
7528 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7529 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7530 but it really isn't)
7531
7532 @returns The same rsocket object so as to facilitate stacking
7533 @see recv_uint16_msb
7534 @see send_uint16_lsb
7535 @qualifier TLS
7536 *///=========================================================================
7537 rsocket* send_uint16_msb(
7538 /// Data to send
7539 const uint16_t value,
7540 /// MSG_DONTROUTE@n
7541 /// MSG_DONTWAIT@n
7542 /// MSG_EOR@n
7543 /// MSG_MORE@n
7544 /// MSG_NOSIGNAL@n
7545 /// MSG_OOB
7546 const int posix_flags = 0) {
7547 int16_t buf = __endian_is_msb ? value : htons(value);
7548 if (__debug) debug("send_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7549 + ", " + std::to_string(value)
7550 + ", " + std::to_string(sizeof(buf))
7551 + ", " + std::to_string(posix_flags)
7552 + ");");
7553 __send(&buf, sizeof(buf), posix_flags);
7554 return this;
7555 } // -x- rsocket* send_int16_msb -x-
7556
7557 /*======================================================================*//**
7558 @brief
7559 Send one 32-bit unsigned integer of data in LSB (little endian) order to the
7560 endpoint.
7561 @par Threads
7562 This method is threadsafe.
7563
7564 @throws randolf::rex::xEBADF The underlying socket is not open
7565 @throws randolf::rex::xECONNRESET Connect reset by peer
7566 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7567 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7568 part of the user address space
7569 @throws randolf::rex::xEINTR Interrupted by a signal
7570 @throws randolf::rex::xEINVAL Invalid argument passed
7571 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7572 occur, but the POSIX sockets documentation lists it as one of the
7573 errors that can be returned, perhaps because some incorrectly
7574 implemented TCP/IP stacks return this error?)
7575 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7576 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7577 and 65,527 bytes for IPv6)
7578 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7579 network congestion (or, less commonly, insufficient memory)
7580 @throws randolf::rex::xENOMEM Insufficient memory
7581 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7582 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7583 doesn't refer to a socket
7584 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7585 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7586 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7587 isn't set)
7588 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7589 there's no new data
7590 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7591 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7592 but it really isn't)
7593
7594 @returns The same rsocket object so as to facilitate stacking
7595 @see recv_uint32_lsb
7596 @see send_uint32_msb
7597 @qualifier TLS
7598 *///=========================================================================
7599 rsocket* send_uint32_lsb(
7600 /// Data to send
7601 const uint32_t value,
7602 /// MSG_DONTROUTE@n
7603 /// MSG_DONTWAIT@n
7604 /// MSG_EOR@n
7605 /// MSG_MORE@n
7606 /// MSG_NOSIGNAL@n
7607 /// MSG_OOB
7608 const int posix_flags = 0) {
7609 uint32_t buf = !__endian_is_msb ? value : ntohl(value);
7610 if (__debug) debug("send_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7611 + ", " + std::to_string(value)
7612 + ", " + std::to_string(sizeof(buf))
7613 + ", " + std::to_string(posix_flags)
7614 + ");");
7615 __send(&buf, sizeof(buf), posix_flags);
7616 return this;
7617 } // -x- rsocket* send_uint32_lsb -x-
7618
7619 /*======================================================================*//**
7620 @brief
7621 Send one 32-bit unsigned integer of data in MSB (big endian) order to the
7622 endpoint.
7623 @par Threads
7624 This method is threadsafe.
7625
7626 @throws randolf::rex::xEBADF The underlying socket is not open
7627 @throws randolf::rex::xECONNRESET Connect reset by peer
7628 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7629 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7630 part of the user address space
7631 @throws randolf::rex::xEINTR Interrupted by a signal
7632 @throws randolf::rex::xEINVAL Invalid argument passed
7633 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7634 occur, but the POSIX sockets documentation lists it as one of the
7635 errors that can be returned, perhaps because some incorrectly
7636 implemented TCP/IP stacks return this error?)
7637 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7638 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7639 and 65,527 bytes for IPv6)
7640 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7641 network congestion (or, less commonly, insufficient memory)
7642 @throws randolf::rex::xENOMEM Insufficient memory
7643 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7644 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7645 doesn't refer to a socket
7646 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7647 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7648 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7649 isn't set)
7650 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7651 there's no new data
7652 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7653 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7654 but it really isn't)
7655
7656 @returns The same rsocket object so as to facilitate stacking
7657 @see recv_uint32_msb
7658 @see send_uint32_lsb
7659 @qualifier TLS
7660 *///=========================================================================
7661 rsocket* send_uint32_msb(
7662 /// Data to send
7663 const uint32_t value,
7664 /// MSG_DONTROUTE@n
7665 /// MSG_DONTWAIT@n
7666 /// MSG_EOR@n
7667 /// MSG_MORE@n
7668 /// MSG_NOSIGNAL@n
7669 /// MSG_OOB
7670 const int posix_flags = 0) {
7671 uint32_t buf = __endian_is_msb ? value : htonl(value);
7672 if (__debug) debug("send_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7673 + ", " + std::to_string(value)
7674 + ", " + std::to_string(sizeof(buf))
7675 + ", " + std::to_string(posix_flags)
7676 + ");");
7677 __send(&buf, sizeof(buf), posix_flags);
7678 return this;
7679 } // -x- rsocket* send_uint32_msb -x-
7680
7681 /*======================================================================*//**
7682 @brief
7683 Send one 64-bit unsigned integer of data in LSB (little endian) order to the
7684 endpoint.
7685 @par Threads
7686 This method is threadsafe.
7687
7688 @throws randolf::rex::xEBADF The underlying socket is not open
7689 @throws randolf::rex::xECONNRESET Connect reset by peer
7690 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7691 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7692 part of the user address space
7693 @throws randolf::rex::xEINTR Interrupted by a signal
7694 @throws randolf::rex::xEINVAL Invalid argument passed
7695 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7696 occur, but the POSIX sockets documentation lists it as one of the
7697 errors that can be returned, perhaps because some incorrectly
7698 implemented TCP/IP stacks return this error?)
7699 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7700 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7701 and 65,527 bytes for IPv6)
7702 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7703 network congestion (or, less commonly, insufficient memory)
7704 @throws randolf::rex::xENOMEM Insufficient memory
7705 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7706 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7707 doesn't refer to a socket
7708 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7709 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7710 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7711 isn't set)
7712 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7713 there's no new data
7714 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7715 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7716 but it really isn't)
7717
7718 @returns The same rsocket object so as to facilitate stacking
7719 @see recv_uint64_lsb
7720 @see send_uint64_msb
7721 @qualifier TLS
7722 *///=========================================================================
7723 rsocket* send_uint64_lsb(
7724 /// Data to send
7725 const uint64_t value,
7726 /// MSG_DONTROUTE@n
7727 /// MSG_DONTWAIT@n
7728 /// MSG_EOR@n
7729 /// MSG_MORE@n
7730 /// MSG_NOSIGNAL@n
7731 /// MSG_OOB
7732 const int posix_flags = 0) {
7733 uint64_t buf = !__endian_is_msb ? value : ((((uint64_t)ntohl((value) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((value) >> 32)));
7734 if (__debug) debug("send_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7735 + ", " + std::to_string(value)
7736 + ", " + std::to_string(sizeof(buf))
7737 + ", " + std::to_string(posix_flags)
7738 + ");");
7739 __send(&buf, sizeof(buf), posix_flags);
7740 return this;
7741 } // -x- rsocket* send_uint64_lsb -x-
7742
7743 /*======================================================================*//**
7744 @brief
7745 Send one 64-bit unsigned integer of data in MSB (big endian) order to the
7746 endpoint.
7747 @par Threads
7748 This method is threadsafe.
7749
7750 @throws randolf::rex::xEBADF The underlying socket is not open
7751 @throws randolf::rex::xECONNRESET Connect reset by peer
7752 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7753 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7754 part of the user address space
7755 @throws randolf::rex::xEINTR Interrupted by a signal
7756 @throws randolf::rex::xEINVAL Invalid argument passed
7757 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7758 occur, but the POSIX sockets documentation lists it as one of the
7759 errors that can be returned, perhaps because some incorrectly
7760 implemented TCP/IP stacks return this error?)
7761 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7762 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7763 and 65,527 bytes for IPv6)
7764 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7765 network congestion (or, less commonly, insufficient memory)
7766 @throws randolf::rex::xENOMEM Insufficient memory
7767 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7768 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7769 doesn't refer to a socket
7770 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7771 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7772 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7773 isn't set)
7774 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7775 there's no new data
7776 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7777 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7778 but it really isn't)
7779
7780 @returns The same rsocket object so as to facilitate stacking
7781 @see recv_uint64_msb
7782 @see send_uint64_lsb
7783 @qualifier TLS
7784 *///=========================================================================
7785 rsocket* send_uint64_msb(
7786 /// Data to send
7787 const uint64_t value,
7788 /// MSG_DONTROUTE@n
7789 /// MSG_DONTWAIT@n
7790 /// MSG_EOR@n
7791 /// MSG_MORE@n
7792 /// MSG_NOSIGNAL@n
7793 /// MSG_OOB
7794 const int posix_flags = 0) {
7795 uint64_t buf = __endian_is_msb ? value : ((((uint64_t)htonl((value) & 0xffffffffUL)) << 32) | htonl((uint32_t)((value) >> 32)));
7796 if (__debug) debug("send_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7797 + ", " + std::to_string(value)
7798 + ", " + std::to_string(sizeof(buf))
7799 + ", " + std::to_string(posix_flags)
7800 + ");");
7801 __send(&buf, sizeof(buf), posix_flags);
7802 return this;
7803 } // -x- rsocket* send_uint64_msb -x-
7804
7805 /*======================================================================*//**
7806 @brief
7807 Send data in the form of a std::string to the endpoint, with an EoL sequence
7808 appended.
7809 @par Threads
7810 This method is threadsafe.
7811
7812 @throws randolf::rex::xEBADF The underlying socket is not open
7813 @throws randolf::rex::xECONNRESET Connect reset by peer
7814 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7815 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7816 part of the user address space
7817 @throws randolf::rex::xEINTR Interrupted by a signal
7818 @throws randolf::rex::xEINVAL Invalid argument passed
7819 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7820 occur, but the POSIX sockets documentation lists it as one of the
7821 errors that can be returned, perhaps because some incorrectly
7822 implemented TCP/IP stacks return this error?)
7823 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7824 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7825 and 65,527 bytes for IPv6)
7826 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7827 network congestion (or, less commonly, insufficient memory)
7828 @throws randolf::rex::xENOMEM Insufficient memory
7829 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7830 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7831 doesn't refer to a socket
7832 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7833 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7834 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7835 isn't set)
7836 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7837 there's no new data
7838 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7839 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7840 but it really isn't)
7841
7842 @returns The same rsocket object so as to facilitate stacking
7843 @see eol
7844 @see printfline
7845 @see recvline
7846 @see send_rline
7847 @see vprintfline
7848 @qualifier TLS
7849 *///=========================================================================
7850 rsocket* sendline(
7851 /// Data to send
7852 const std::string msg = std::string(),
7853 /// MSG_DONTROUTE@n
7854 /// MSG_DONTWAIT@n
7855 /// MSG_EOR@n
7856 /// MSG_MORE@n
7857 /// MSG_NOSIGNAL@n
7858 /// MSG_OOB
7859 const int posix_flags = 0) {
7860 if (__debug) debug("sendline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7861 + ", <std::string>"
7862 + ", " + std::to_string(msg.length())
7863 + "+" + std::to_string(__eol_out.length())
7864 + ", " + std::to_string(posix_flags)
7865 + ");");
7866 __sendline(msg.data(), msg.length(), posix_flags);
7867 return this;
7868 } // -x- rsocket* sendline -x-
7869
7870 /*======================================================================*//**
7871 @brief
7872 Send data in the form of a "msghdr" structure to a specific endpoint.
7873 @warning
7874 This method is not compatible with TLS.
7875 @par Threads
7876 This method is threadsafe.
7877
7878 @throws randolf::rex::xEBADF The underlying socket is not open
7879 @throws randolf::rex::xECONNRESET Connect reset by peer
7880 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7881 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7882 part of the user address space
7883 @throws randolf::rex::xEINTR Interrupted by a signal
7884 @throws randolf::rex::xEINVAL Invalid argument passed
7885 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7886 occur, but the POSIX sockets documentation lists it as one of the
7887 errors that can be returned, perhaps because some incorrectly
7888 implemented TCP/IP stacks return this error?)
7889 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7890 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7891 and 65,527 bytes for IPv6)
7892 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7893 network congestion (or, less commonly, insufficient memory)
7894 @throws randolf::rex::xENOMEM Insufficient memory
7895 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7896 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7897 doesn't refer to a socket
7898 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7899 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7900 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7901 isn't set)
7902 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7903 there's no new data
7904 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7905 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7906 but it really isn't)
7907
7908 @returns The same rsocket object so as to facilitate stacking
7909 @see recvmsg
7910 @qualifier POSIX
7911 *///=========================================================================
7912 rsocket* sendmsg(
7913 /// Pointer to data to send
7914 const struct msghdr* msg,
7915 /// MSG_DONTROUTE@n
7916 /// MSG_DONTWAIT@n
7917 /// MSG_EOR@n
7918 /// MSG_MORE@n
7919 /// MSG_NOSIGNAL@n
7920 /// MSG_OOB
7921 const int posix_flags = 0) {
7922 if (__debug) debug("sendmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7923 + ", <msghdr>"
7924 + ", " + std::to_string(posix_flags)
7925 + ");");
7926 __track_bytes_tx(__rc_check(::sendmsg(__socket_fd, msg, posix_flags)));
7927 return this;
7928 } // -x- rsocket* sendmsg -x-
7929
7930 /*======================================================================*//**
7931 @brief
7932 Send data in the form of a "mmsghdr" structure to a specific endpoint.
7933 @warning
7934 This method is not compatible with TLS.
7935 @par Threads
7936 This method is threadsafe.
7937
7938 @throws randolf::rex::xEBADF The underlying socket is not open
7939 @throws randolf::rex::xECONNRESET Connect reset by peer
7940 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7941 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7942 part of the user address space
7943 @throws randolf::rex::xEINTR Interrupted by a signal
7944 @throws randolf::rex::xEINVAL Invalid argument passed
7945 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7946 occur, but the POSIX sockets documentation lists it as one of the
7947 errors that can be returned, perhaps because some incorrectly
7948 implemented TCP/IP stacks return this error?)
7949 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7950 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7951 and 65,527 bytes for IPv6)
7952 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7953 network congestion (or, less commonly, insufficient memory)
7954 @throws randolf::rex::xENOMEM Insufficient memory
7955 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7956 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7957 doesn't refer to a socket
7958 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7959 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7960 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7961 isn't set)
7962 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7963 there's no new data
7964 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7965 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7966 but it really isn't)
7967
7968 @returns The same rsocket object so as to facilitate stacking
7969 @see recvmmsg
7970 @qualifier POSIX
7971 *///=========================================================================
7972 rsocket* sendmmsg(
7973 /// Pointer to data to send
7974 struct mmsghdr* mmsg,
7975 /// Size of target endpoint structure
7976 const unsigned int vlen = sizeof(mmsghdr),
7977 /// MSG_DONTROUTE@n
7978 /// MSG_DONTWAIT@n
7979 /// MSG_EOR@n
7980 /// MSG_MORE@n
7981 /// MSG_NOSIGNAL@n
7982 /// MSG_OOB
7983 const int posix_flags = 0) {
7984 if (__debug) debug("sendmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7985 + ", <mmsghdr>"
7986 + ", " + std::to_string(vlen)
7987 + ", " + std::to_string(posix_flags)
7988 + ");");
7989 __track_bytes_tx(__rc_check(::sendmmsg(__socket_fd, mmsg, vlen, posix_flags)));
7990 return this;
7991 } // -x- rsocket* sendmsg -x-
7992
7993 /*======================================================================*//**
7994 @brief
7995 Send data in the form of a std::string to a specific endpoint.
7996 @warning
7997 This method is not compatible with TLS.
7998 @par Threads
7999 This method is threadsafe.
8000
8001 @throws randolf::rex::xEBADF The underlying socket is not open
8002 @throws randolf::rex::xECONNRESET Connect reset by peer
8003 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8004 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8005 part of the user address space
8006 @throws randolf::rex::xEINTR Interrupted by a signal
8007 @throws randolf::rex::xEINVAL Invalid argument passed
8008 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8009 occur, but the POSIX sockets documentation lists it as one of the
8010 errors that can be returned, perhaps because some incorrectly
8011 implemented TCP/IP stacks return this error?)
8012 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8013 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8014 and 65,527 bytes for IPv6)
8015 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8016 network congestion (or, less commonly, insufficient memory)
8017 @throws randolf::rex::xENOMEM Insufficient memory
8018 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8019 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8020 doesn't refer to a socket
8021 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8022 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8023 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8024 isn't set)
8025 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8026 there's no new data
8027 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8028 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8029 but it really isn't)
8030
8031 @returns The same rsocket object so as to facilitate stacking
8032 @see recvfrom
8033 @qualifier POSIX
8034 *///=========================================================================
8035 rsocket* sendto(
8036 /// Data to send
8037 const std::string msg,
8038 /// MSG_DONTROUTE@n
8039 /// MSG_DONTWAIT@n
8040 /// MSG_EOR@n
8041 /// MSG_MORE@n
8042 /// MSG_NOSIGNAL@n
8043 /// MSG_OOB
8044 const int posix_flags,
8045 /// Target endpoint address structure
8046 const struct sockaddr *to,
8047 /// Size of target endpoint structure
8048 socklen_t tolen = sizeof(sockaddr)) {
8049 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8050 + ", <std::string>"
8051 + ", " + std::to_string(posix_flags)
8052 + ", <sockaddr>"
8053 + ", " + std::to_string(tolen)
8054 + ");");
8055 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg.data(), msg.length(), posix_flags, to, tolen)));
8056 return this;
8057 } // -x- rsocket* sendto -x- // TODO: Create easier-to-use variants
8058
8059 /*======================================================================*//**
8060 @brief
8061 Send data in the form of a C-string to a specific endpoint.
8062 @warning
8063 This method is not compatible with TLS.
8064 @par Threads
8065 This method is threadsafe.
8066
8067 @throws randolf::rex::xEBADF The underlying socket is not open
8068 @throws randolf::rex::xECONNRESET Connect reset by peer
8069 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8070 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8071 part of the user address space
8072 @throws randolf::rex::xEINTR Interrupted by a signal
8073 @throws randolf::rex::xEINVAL Invalid argument passed
8074 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8075 occur, but the POSIX sockets documentation lists it as one of the
8076 errors that can be returned, perhaps because some incorrectly
8077 implemented TCP/IP stacks return this error?)
8078 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8079 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8080 and 65,527 bytes for IPv6)
8081 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8082 network congestion (or, less commonly, insufficient memory)
8083 @throws randolf::rex::xENOMEM Insufficient memory
8084 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8085 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8086 doesn't refer to a socket
8087 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8088 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8089 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8090 isn't set)
8091 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8092 there's no new data
8093 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8094 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8095 but it really isn't)
8096
8097 @returns The same rsocket object so as to facilitate stacking
8098 @see recvfrom
8099 @qualifier POSIX
8100 *///=========================================================================
8101 rsocket* sendto(
8102 /// Pointer to data to send
8103 const char* msg,
8104 /// Number of bytes to send
8105 const size_t len,
8106 /// MSG_DONTROUTE@n
8107 /// MSG_DONTWAIT@n
8108 /// MSG_EOR@n
8109 /// MSG_MORE@n
8110 /// MSG_NOSIGNAL@n
8111 /// MSG_OOB
8112 const int posix_flags,
8113 /// Target endpoint address structure
8114 const struct sockaddr *to,
8115 /// Size of target endpoint structure
8116 socklen_t tolen = sizeof(sockaddr)) {
8117 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8118 + ", <msg>"
8119 + ", " + std::to_string(std::strlen(msg))
8120 + ", " + std::to_string(posix_flags)
8121 + ", <sockaddr>"
8122 + ", " + std::to_string(tolen)
8123 + ");");
8124 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, len, posix_flags, to, tolen)));
8125 return this;
8126 } // -x- rsocket* sendto -x-
8127
8128 /*======================================================================*//**
8129 @brief
8130 Send data in the form of an ASCIIZ string to the endpoint. The terminating
8131 NULL character won't be transmitted.
8132 @par Threads
8133 This method is threadsafe.
8134
8135 @throws randolf::rex::xEBADF The underlying socket is not open
8136 @throws randolf::rex::xECONNRESET Connect reset by peer
8137 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8138 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8139 part of the user address space
8140 @throws randolf::rex::xEINTR Interrupted by a signal
8141 @throws randolf::rex::xEINVAL Invalid argument passed
8142 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8143 occur, but the POSIX sockets documentation lists it as one of the
8144 errors that can be returned, perhaps because some incorrectly
8145 implemented TCP/IP stacks return this error?)
8146 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8147 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8148 and 65,527 bytes for IPv6)
8149 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8150 network congestion (or, less commonly, insufficient memory)
8151 @throws randolf::rex::xENOMEM Insufficient memory
8152 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8153 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8154 doesn't refer to a socket
8155 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8156 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8157 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8158 isn't set)
8159 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8160 there's no new data
8161 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8162 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8163 but it really isn't)
8164
8165 @returns The same rsocket object so as to facilitate stacking
8166 @see recvz(const size_t, const int)
8167 @see send_asciiz(const char*, const int) which also transmits the terminating
8168 NULL character
8169 @qualifier TLS
8170 *///=========================================================================
8171 rsocket* sendz(
8172 /// Pointer to data to send
8173 const char* msg,
8174 /// MSG_DONTROUTE@n
8175 /// MSG_DONTWAIT@n
8176 /// MSG_EOR@n
8177 /// MSG_MORE@n
8178 /// MSG_NOSIGNAL@n
8179 /// MSG_OOB
8180 const int posix_flags = 0) {
8181 if (__debug) debug("sendz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8182 + ", <msg>"
8183 + ", " + std::to_string(std::strlen(msg))
8184 + ", " + std::to_string(posix_flags)
8185 + ");");
8186 __send(msg, std::strlen(msg), posix_flags);
8187 return this;
8188 } // -x- rsocket* sendz -x-
8189
8190 /*======================================================================*//**
8191 @brief
8192 Send data in the form of an ASCIIZ string to a specific endpoint. The
8193 terminating NULL character won't be transmitted.
8194 @warning
8195 This method is not compatible with TLS.
8196 @par Threads
8197 This method is threadsafe.
8198
8199 @throws randolf::rex::xEBADF The underlying socket is not open
8200 @throws randolf::rex::xECONNRESET Connect reset by peer
8201 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8202 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8203 part of the user address space
8204 @throws randolf::rex::xEINTR Interrupted by a signal
8205 @throws randolf::rex::xEINVAL Invalid argument passed
8206 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8207 occur, but the POSIX sockets documentation lists it as one of the
8208 errors that can be returned, perhaps because some incorrectly
8209 implemented TCP/IP stacks return this error?)
8210 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8211 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8212 and 65,527 bytes for IPv6)
8213 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8214 network congestion (or, less commonly, insufficient memory)
8215 @throws randolf::rex::xENOMEM Insufficient memory
8216 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8217 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8218 doesn't refer to a socket
8219 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8220 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8221 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8222 isn't set)
8223 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8224 there's no new data
8225 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8226 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8227 but it really isn't)
8228
8229 @see recvfrom
8230 @see sendto
8231 @see sendz
8232 @returns The same rsocket object so as to facilitate stacking
8233 *///=========================================================================
8234 rsocket* sendzto(
8235 /// Pointer to data to send
8236 const char* msg,
8237 /// MSG_DONTROUTE@n
8238 /// MSG_DONTWAIT@n
8239 /// MSG_EOR@n
8240 /// MSG_MORE@n
8241 /// MSG_NOSIGNAL@n
8242 /// MSG_OOB
8243 const int posix_flags,
8244 /// Target endpoint address structure
8245 const struct sockaddr *to,
8246 /// Size of target endpoint structure
8247 socklen_t tolen = sizeof(sockaddr)) {
8248 if (__debug) debug("sendzto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8249 + ", <msg>"
8250 + ", " + std::to_string(std::strlen(msg))
8251 + ", " + std::to_string(posix_flags)
8252 + ", <sockaddr>"
8253 + ", " + std::to_string(tolen)
8254 + ");");
8255 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, std::strlen(msg), posix_flags, to, tolen)));
8256 return this;
8257 } // -x- rsocket* sendzto -x-
8258
8259 /*======================================================================*//**
8260 @brief
8261 Set socket option with a zero-length value, which is a special case usage
8262 that will probably never be needed, but is included here for future use where
8263 the call to @c setsockopt() would include specifying @c option_value's length
8264 in the final parameter @c option_len as zero.
8265
8266 (Internally, we still populate the pointer for @c option_value to that of an
8267 internal `static const int` {which does have the value of 0} so as to avoid
8268 triggering an unexpected error where this parameter can't be @c nullptr).
8269
8270 @par Notes
8271 These setsockopt() methods take an integer or character value directly, or a
8272 pointer to a structure, and then rsocket handles the remaining tedious
8273 technical details behind-the-scenes for you when calling the underlying
8274 socket's setsockopt() function.
8275
8276 @throws randolf::rex::xEBADF The underlying socket is not open
8277 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8278 part of the user address space
8279 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
8280 valid for this socket's family (a.k.a., communication domain)
8281 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
8282 is not supported
8283 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8284 doesn't refer to a socket
8285
8286 @returns The same rsocket object so as to facilitate stacking
8287 @see getsockopt_int
8288 @qualifier POSIX
8289 @qualifier TLS
8290 *///=========================================================================
8291 rsocket* setsockopt(
8292 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8293 /// also be @c IPPROTO_TCP, etc.
8294 const int level,
8295 /// The name of the option, such as @c TCP_CORK, etc.
8296 const int option) {
8297 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option);
8298 static const int value = 0;
8299 __rc_check(::setsockopt(__socket_fd, level, option, &value, 0));
8300 return this;
8301 } // -x- rsocket* setsockopt -x-
8302
8303 /*======================================================================*//**
8304 @brief
8305 Set socket option to the specific `integer`.
8306
8307 @par Notes
8308 These setsockopt() methods take an integer or character value directly, or a
8309 pointer to a structure, and then rsocket handles the remaining tedious
8310 technical details behind-the-scenes for you when calling the underlying
8311 socket's setsockopt() function.
8312
8313 @throws randolf::rex::xEBADF The underlying socket is not open
8314 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8315 part of the user address space
8316 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
8317 valid for this socket's family (a.k.a., communication domain)
8318 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
8319 is not supported
8320 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8321 doesn't refer to a socket
8322
8323 @returns The same rsocket object so as to facilitate stacking
8324 @see getsockopt_int(const int, const int)
8325 @qualifier POSIX
8326 @qualifier TLS
8327 *///=========================================================================
8328 rsocket* setsockopt(
8329 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8330 /// also be @c IPPROTO_TCP, etc.
8331 const int level,
8332 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8333 const int option,
8334 /// The value that this socket option will be set to
8335 const int value) {
8336 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
8337 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8338 return this;
8339 } // -x- rsocket* setsockopt -x-
8340
8341 /*======================================================================*//**
8342 @brief
8343 Set socket option to the specific `unsigned integer`.
8344 @copydetails setsockopt(const int, const int, const int)
8345
8346 @pre
8347 For any values that require a u_int, you'll need to explicitly cast this type
8348 when specifying the value directly; for example: (u_int)32768
8349
8350 @throws randolf::rex::xEBADF The underlying socket is not open
8351 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8352 part of the user address space
8353 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
8354 valid for this socket's family (a.k.a., communication domain)
8355 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
8356 is not supported
8357 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8358 doesn't refer to a socket
8359
8360 @returns The same rsocket object so as to facilitate stacking
8361 @see getsockopt_u_int(const int, const int)
8362 @qualifier TLS
8363 *///=========================================================================
8364 rsocket* setsockopt(
8365 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8366 /// also be @c IPPROTO_TCP, etc.
8367 const int level,
8368 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8369 const int option,
8370 /// The value that this socket option will be set to
8371 const u_int value) {
8372 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
8373 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8374 return this;
8375 } // -x- rsocket* setsockopt -x-
8376
8377 /*======================================================================*//**
8378 @brief
8379 Set socket option to the specific `unsigned character`.
8380 @copydetails setsockopt(const int, const int, const int)
8381 @see getsockopt_u_char(const int, const int)
8382 @qualifier TLS
8383 *///=========================================================================
8384 rsocket* setsockopt(
8385 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8386 /// also be @c IPPROTO_TCP, etc.
8387 const int level,
8388 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8389 const int option,
8390 /// The value that this socket option will be set to
8391 const u_char value) {
8392 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
8393 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8394 return this;
8395 } // -x- rsocket* setsockopt -x-
8396
8397 /*======================================================================*//**
8398 @brief
8399 Set socket option to the specific `linger` structure.
8400 @copydetails setsockopt(const int, const int, const int)
8401 @see getsockopt_linger(const int, const int)
8402 @qualifier TLS
8403 *///=========================================================================
8404 rsocket* setsockopt(
8405 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8406 /// also be @c IPPROTO_TCP, etc.
8407 const int level,
8408 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8409 const int option,
8410 /// The structure that this socket option will be set to
8411 const linger& value) {
8412 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8413 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8414 return this;
8415 } // -x- rsocket* setsockopt -x-
8416
8417 /*======================================================================*//**
8418 @brief
8419 Set socket option to the specific `timeval` structure.
8420 @copydetails setsockopt(const int, const int, const linger&)
8421 @see getsockopt_timeval(const int, const int)
8422 @qualifier TLS
8423 *///=========================================================================
8424 rsocket* setsockopt(
8425 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8426 /// also be @c IPPROTO_TCP, etc.
8427 const int level,
8428 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8429 const int option,
8430 /// The structure that this socket option will be set to
8431 const timeval& value) {
8432 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8433 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8434 return this;
8435 } // -x- rsocket* setsockopt -x-
8436
8437 /*======================================================================*//**
8438 @brief
8439 Set socket option to the specific `in_addr` structure.
8440 @copydetails setsockopt(const int, const int, const linger&)
8441 @see getsockopt_in_addr(const int, const int)
8442 @qualifier TLS
8443 *///=========================================================================
8444 rsocket* setsockopt(
8445 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8446 /// also be @c IPPROTO_TCP, etc.
8447 const int level,
8448 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8449 const int option,
8450 /// The structure that this socket option will be set to
8451 const in_addr& value) {
8452 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8453 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8454 return this;
8455 } // -x- rsocket* setsockopt -x-
8456
8457 /*======================================================================*//**
8458 @brief
8459 Set socket option to the specific `ip_mreq` structure.
8460 @copydetails setsockopt(const int, const int, const linger&)
8461 @see getsockopt_ip_mreq(const int, const int)
8462 @qualifier TLS
8463 *///=========================================================================
8464 rsocket* setsockopt(
8465 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8466 /// also be @c IPPROTO_TCP, etc.
8467 const int level,
8468 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8469 const int option,
8470 /// The structure that this socket option will be set to
8471 const ip_mreq& value) {
8472 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8473 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8474 return this;
8475 } // -x- rsocket* setsockopt -x-
8476
8477 /*======================================================================*//**
8478 @brief
8479 Set socket option to the specific `ip_mreq_source` structure.
8480 @copydetails setsockopt(const int, const int, const linger&)
8481 @see getsockopt_ip_mreq_source(const int, const int)
8482 @qualifier TLS
8483 *///=========================================================================
8484 rsocket* setsockopt(
8485 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8486 /// also be @c IPPROTO_TCP, etc.
8487 const int level,
8488 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8489 const int option,
8490 /// The structure that this socket option will be set to
8491 const ip_mreq_source& value) {
8492 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8493 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8494 return this;
8495 } // -x- rsocket* setsockopt -x-
8496
8497 /*======================================================================*//**
8498 @brief
8499 Set socket option to the specific `icmp6_filter` structure.
8500 @copydetails setsockopt(const int, const int, const linger&)
8501 @see getsockopt_icmp6_filter(const int, const int)
8502 @qualifier TLS
8503 *///=========================================================================
8504 rsocket* setsockopt(
8505 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8506 /// also be @c IPPROTO_TCP, etc.
8507 const int level,
8508 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8509 const int option,
8510 /// The structure that this socket option will be set to
8511 const icmp6_filter& value) {
8512 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8513 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8514 return this;
8515 } // -x- rsocket* setsockopt -x-
8516
8517 /*======================================================================*//**
8518 @brief
8519 Set socket option to the specific `sockaddr_in6` structure.
8520 @copydetails setsockopt(const int, const int, const linger&)
8521 @see getsockopt_sockaddr_in6(const int, const int)
8522 @qualifier TLS
8523 *///=========================================================================
8524 rsocket* setsockopt(
8525 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8526 /// also be @c IPPROTO_TCP, etc.
8527 const int level,
8528 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8529 const int option,
8530 /// The structure that this socket option will be set to
8531 const sockaddr_in6& value) {
8532 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8533 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8534 return this;
8535 } // -x- rsocket* setsockopt -x-
8536
8537 /*======================================================================*//**
8538 @brief
8539 Set socket option to the specific `ip6_mtuinfo` structure.
8540 @copydetails setsockopt(const int, const int, const linger&)
8541 @see getsockopt_ip6_mtuinfo(const int, const int)
8542 @qualifier TLS
8543 *///=========================================================================
8544 rsocket* setsockopt(
8545 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8546 /// also be @c IPPROTO_TCP, etc.
8547 const int level,
8548 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8549 const int option,
8550 /// The structure that this socket option will be set to
8551 const ip6_mtuinfo& value) {
8552 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8553 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8554 return this;
8555 } // -x- rsocket* setsockopt -x-
8556
8557 /*======================================================================*//**
8558 @brief
8559 Set socket option to the specific `ipv6_mreq` structure.
8560 @copydetails setsockopt(const int, const int, const linger&)
8561 @see getsockopt_ipv6_mreq(const int, const int)
8562 @qualifier TLS
8563 *///=========================================================================
8564 rsocket* setsockopt(
8565 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8566 /// also be @c IPPROTO_TCP, etc.
8567 const int level,
8568 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8569 const int option,
8570 /// The structure that this socket option will be set to
8571 const ipv6_mreq& value) {
8572 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8573 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8574 return this;
8575 } // -x- rsocket* setsockopt -x-
8576
8577 /*======================================================================*//**
8578 @brief
8579 Set socket option to the specific `group_req` structure.
8580 @copydetails setsockopt(const int, const int, const linger&)
8581 @see getsockopt_group_req(const int, const int)
8582 @qualifier TLS
8583 *///=========================================================================
8584 rsocket* setsockopt(
8585 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8586 /// also be @c IPPROTO_TCP, etc.
8587 const int level,
8588 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8589 const int option,
8590 /// The structure that this socket option will be set to
8591 const group_req& value) {
8592 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8593 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8594 return this;
8595 } // -x- rsocket* setsockopt -x-
8596
8597 /*======================================================================*//**
8598 @brief
8599 Set socket option to the specific `group_source_req` structure.
8600 @copydetails setsockopt(const int, const int, const linger&)
8601 @see getsockopt_group_source_req(const int, const int)
8602 @qualifier TLS
8603 *///=========================================================================
8604 rsocket* setsockopt(
8605 /// The level at which the option resides; typically @c SOL_SOCKET, or could
8606 /// also be @c IPPROTO_TCP, etc.
8607 const int level,
8608 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
8609 const int option,
8610 /// The structure that this socket option will be set to
8611 const group_source_req& value) {
8612 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
8613 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
8614 return this;
8615 } // -x- rsocket* setsockopt -x-
8616
8617 /*======================================================================*//**
8618 @brief
8619 Shut down the underlying socket, partially or fully.
8620
8621 <div style=padding-left:32px;>
8622 <table>
8623 <tr>
8624 <td valign=top>SHUT_RD:</td>
8625 <td>Further receives will be disallowed.</td>
8626 </tr>
8627 <tr>
8628 <td valign=top>SHUT_WR:</td>
8629 <td>Further sends will be disallowed (this may cause actions specific
8630 to the protocol family of the socket to occur).</td>
8631 </tr>
8632 <tr>
8633 <td valign=top>SHUT_RDWR:</td>
8634 <td>Further sends and receives will be disallowed (default).</td>
8635 </tr>
8636 </table>
8637 </div>
8638
8639 @throws randolf::rex::xEBADF The underlying socket is not open
8640 @throws randolf::rex::xEINVAL Invalid argument passed
8641 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8642 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8643 doesn't refer to a socket
8644
8645 @returns The same rsocket object so as to facilitate stacking
8646 @see close
8647 @see close_passive
8648 @see connect
8649 @qualifier POSIX
8650 @qualifier TLS
8651 *///=========================================================================
8652 rsocket* shutdown(
8653 /// SHUT_RD@n
8654 /// SHUT_RW@n
8655 /// SHUT_RDWR (default)
8656 const int how = SHUT_RDWR,
8657 /// Shutdown the TLS connection too (but only if it's alrady enabled), which
8658 /// is the default (disabling this will usually result in errors for the
8659 /// endpoint upon raw socket shutdown)
8660 const bool tls_shutdown = true) {
8661 if (__debug) debug("shutdown(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8662 + ", " + std::to_string(how)
8663 + ");");
8664
8665 // --------------------------------------------------------------------------
8666 // Shut down TLS connections first, if TLS is enabled.
8667 //
8668 // If SSL_shutdown returns a 0, then it means a "bidirectional shutdown" is
8669 // needed which entails calling SSL_shutdown a second time. The error codes
8670 // returned by SSL_shutdown are as follows:
8671 //
8672 // 1 = Shutdown completed normally
8673 //
8674 // 0 = Shutdown is not yet finished, and a second SSL_shutdown() is needed
8675 //
8676 // -1 = Critical error, probably due to a connection problem, or because
8677 // the endpoint already initiated a shutdown, etc.
8678 //
8679 // We don't really care what the second SSL_shutdown's return code is because
8680 // there's nothing we can do about it whatever it is. This situation is rare
8681 // though, and what follows next closes the raw socket, which is OpenSSL's
8682 // lifeline to communicating with the endpoint.
8683 // --------------------------------------------------------------------------
8684 if (__tls && tls_shutdown) {
8685 if (SSL_shutdown(__tls_fd) == 0) {
8686 SSL_shutdown(__tls_fd);
8687 } // -x- if SSL_shutdown -x-
8688 } // -x- if __tls -x-
8689
8690 // --------------------------------------------------------------------------
8691 // Shut down the raw socket.
8692 // --------------------------------------------------------------------------
8693 __rc_check(::shutdown(__socket_fd, how));
8694 //__socket_connected = false; // TODO: Figure out when to change this to false
8695 return this;
8696 } // -x- rsocket* shutdown -x-
8697
8698 /*======================================================================*//**
8699 @brief
8700 Complete the configuration of an rsocket that was previously initialized
8701 without any parameters (a.k.a., an "empty rsocket").
8702 @copydetails rsocket()
8703 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
8704 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
8705 @throws randolf::rex::xEALREADY If this socket() method was already used, or
8706 it was used after rsocket() initialized with at least one parameter
8707 @throws randolf::rex::xEINVAL Protocal family invalid or not available
8708 @throws randolf::rex::xEINVAL Invalid flags in type
8709 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
8710 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
8711 @throws randolf::rex::xENOBUFS Insufficient memory
8712 @throws randolf::rex::xENOMEM Insufficient memory
8713 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
8714 supported within the specified family (a.k.a., communication domain)
8715 @returns The same rsocket object so as to facilitate stacking
8716 @see port()
8717 @see rsocket()
8718 @see socket_family()
8719 @see socket_fd()
8720 @see socket_protocol()
8721 @see socket_type()
8722 @qualifier POSIX
8723 @qualifier TLS
8724 *///=========================================================================
8725 rsocket* socket(
8726 /// Communication domain; usually one of:@n
8727 /// AF_INET (IPv4)@n
8728 /// AF_INET6 (IPv6)@n
8729 /// AF_UNIX (UNIX domain sockets)
8730 const int family,
8731 /// Communication semantics; usually one of:@n
8732 /// SOCK_STREAM (common for TCP)@n
8733 /// SOCK_DGRAM (common for UDP)
8734 const int type = SOCK_STREAM,
8735 /// Network protocol; usually one of:@n
8736 /// IPPROTO_TCP@n
8737 /// IPPROTO_UDP@n
8738 /// IPPROTO_IP@n
8739 /// PF_UNSPEC (auto-detect)
8740 const int protocol = PF_UNSPEC) {
8741 __socket(family, type, protocol);
8742 return this;
8743 } // -x- rsocket* socket -x-
8744
8745 /*======================================================================*//**
8746 @brief
8747 Get underlying socket family/domain constant (SO_DOMAIN).
8748 @returns socket family/domain constant
8749 @see port()
8750 @see socket()
8751 @see socket_fd()
8752 @see socket_protocol()
8753 @see socket_type()
8754 @qualifier TLS
8755 *///=========================================================================
8756 const int socket_family() noexcept { return __socket_addr->ss_family; } // -x- int socket_family -x-
8757
8758 /*======================================================================*//**
8759 @brief
8760 Get underlying socket descriptor/handle.
8761 @returns socket descriptor/handle
8762 @returns 0 = socket not yet allocated
8763 @see port()
8764 @see socket()
8765 @see socket_family()
8766 @see socket_protocol()
8767 @see socket_type()
8768 @qualifier TLS
8769 *///=========================================================================
8770 const int socket_fd() noexcept { return __socket_fd; } // -x- int socket_fd -x-
8771
8772 /*======================================================================*//**
8773 @brief
8774 Set underlying socket descriptor/handle (to one that is presumed to be open).
8775 @note
8776 This method is only available while an underlying socket has not been created
8777 or previously assigned, such as after an empty @ref rsocket instantiation.
8778 @throws randolf::rex::xEALREADY If this socket_fd() method was already used,
8779 or it was used after socket() initialized it, or if rsocket() had
8780 initialized with at least one parameter that resulted in the creation
8781 of an underlying socket
8782 @returns The same rsocket object so as to facilitate stacking
8783 @see socket()
8784 @see socket_family()
8785 @see socket_protocol()
8786 @see socket_type()
8787 @qualifier TLS
8788 *///=========================================================================
8789 rsocket* socket_fd(
8790 /// New socket descriptor/handle
8791 const int new_socket_fd) {
8792 if (__debug) debug("socket_fd(socket{0x" + randolf::rtools::to_hex(new_socket_fd) + "}"
8793 + ");");
8794 if (__socket_fd != 0) throw randolf::rex::xEALREADY("EALREADY");
8795 __socket_fd = new_socket_fd;
8796 __socket_addr->ss_family = getsockopt_int(SOL_SOCKET, SO_DOMAIN);
8797 __socket_type = getsockopt_int(SOL_SOCKET, SO_TYPE);
8798 __socket_protocol = getsockopt_int(SOL_SOCKET, SO_PROTOCOL);
8799 __socket_open = true;
8800 return this;
8801 } // -x- rsocket* socket_fd -x-
8802
8803 /*======================================================================*//**
8804 @brief
8805 Get underlying socket protocol constant (SO_PROTOCOL).
8806 @returns socket protocol constant
8807 @see port()
8808 @see socket()
8809 @see socket_family()
8810 @see socket_fd()
8811 @see socket_type()
8812 @qualifier TLS
8813 *///=========================================================================
8814 const int socket_protocol() noexcept {
8815 return __socket_protocol;
8816 } // -x- int socket_protocol -x-
8817
8818 /*======================================================================*//**
8819 @brief
8820 Get underlying socket type constant (SO_TYPE).
8821 @returns socket type constant
8822 @see port()
8823 @see socket()
8824 @see socket_family()
8825 @see socket_fd()
8826 @see socket_protocol()
8827 @qualifier TLS
8828 *///=========================================================================
8829 const int socket_type() noexcept {
8830 return __socket_type;
8831 } // -x- int socket_type -x-
8832
8833 /*======================================================================*//**
8834 @brief
8835 Find out whether the underlying socket is at the out-of-band (OOB) mark.
8836
8837 @throws randolf::rex::xEBADF The underlying socket is not open
8838 @throws randolf::rex::xEINVAL The underlying socket file descriptor is not a
8839 type to which @ref sockatmark() can be applied
8840
8841 @returns TRUE = at OOB mark
8842 @returns FALSE = not at OOB mark
8843 @qualifier POSIX
8844 @qualifier TLS
8845 *///=========================================================================
8846 const bool sockatmark() {
8847 return __rc_check(::sockatmark(__socket_fd)) ? true : false;
8848 } // -x- bool sockatmark -x-
8849
8850 /*======================================================================*//**
8851 @brief
8852 Obtain the receive (read) or send (write/transmit) timeout is set to on the
8853 underlying socket.
8854
8855 Since getting the read timeout is such a common operation, this specialized
8856 method was created to ease software development efforts; internally we're
8857 just calling getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO).
8858
8859 @throws randolf::rex::xEBADF The underlying socket is not open
8860 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8861 part of the user address space
8862 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
8863 valid for this socket's family (a.k.a., communication domain)
8864 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
8865 is not supported
8866 @throws randolf::rex::xENOTDIR If the @c direction parameter is supplied with
8867 an incorrect value (not @c SO_RCVTIMEO or @c SO_SNDTIMEO); this
8868 exception is normally a file-system related error, so we're using it
8869 here instead of EINVAL to make detecting this problem simpler for
8870 software developers (plus, "DIR" relates well to "direction"); this
8871 exception shouldn't need to be caught in the vast majority of uses,
8872 although one use where it should be caught is if the end-user has
8873 free reign to set the @c direction parameter to any value (e.g., a
8874 customizable software debugging interface)
8875 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8876 doesn't refer to a socket
8877
8878 @returns @c timeval socket option structure wrapped in std::shared_ptr
8879 @qualifier TLS
8880 *///=========================================================================
8881 std::shared_ptr<timeval> timeout(
8882 /// Direction:@n
8883 /// @c SO_RCVTIMEO (default)@n
8884 /// @c SO_SNDTIMEO
8885 const int direction = SO_RCVTIMEO) {
8886
8887 // --------------------------------------------------------------------------
8888 // Get timeout for the specified direction.
8889 // --------------------------------------------------------------------------
8890 if (direction == SO_RCVTIMEO || direction == SO_SNDTIMEO) {
8891 return getsockopt_timeval(SOL_SOCKET, direction);
8892 } // -x- if direction -x-
8893
8894 // --------------------------------------------------------------------------
8895 // Throw exception because direction is invalid.
8896 // --------------------------------------------------------------------------
8897 throw randolf::rex::xENOTDIR("invalid timeout direction (must be SO_RCVTIMEO or SO_SNDTIMEO)");
8898
8899 } // -x- std::shared_ptr<timeval> timeout -x-
8900
8901 /*======================================================================*//**
8902 @brief
8903 Configure the receive (read) or send (write/transmit) timeout on the
8904 underlying socket.
8905
8906 Since setting the read timeout is such a common operation, this specialized
8907 method was created to ease software development efforts; internally we're
8908 just calling setsockopt(SOL_SOCKET, SO_RCVTIMEO, timeval).
8909 @attention
8910 Although a timeout of 100,000 microseconds (1/10 of one second) may suffice
8911 in healthy and efficient networks, a more conservative setting of 1 second
8912 tends to minimally yield more reliable results. Many end-user applications
8913 use defaults of 3 to 5 seconds to cover a wider variety of unpredictable
8914 network connections (such as over shared wireless connections that are slow),
8915 and this setting should ultimately be configurable by users/administrators.
8916
8917 @note
8918 The default timeout for new sockets is normally 0 (no timeout).
8919
8920 @throws randolf::rex::xEBADF The underlying socket is not open
8921 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8922 part of the user address space
8923 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
8924 valid for this socket's family (a.k.a., communication domain)
8925 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
8926 is not supported
8927 @throws randolf::rex::xENOTDIR If the @c direction parameter is supplied with
8928 an incorrect value (not @c SO_RCVTIMEO or @c SO_SNDTIMEO); this
8929 exception is normally a file-system related error, so we're using it
8930 here instead of EINVAL to make detecting this problem simpler for
8931 software developers (plus, "DIR" relates well to "direction"); this
8932 exception shouldn't need to be caught in the vast majority of uses,
8933 although one use where it should be caught is if the end-user has
8934 free reign to set the @c direction parameter to any value (e.g., a
8935 customizable software debugging interface)
8936 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8937 doesn't refer to a socket
8938
8939 @returns The same rsocket object so as to facilitate stacking
8940 @see timeout_recvline
8941 @qualifier TLS
8942 *///=========================================================================
8943 rsocket* timeout(
8944 /// timeval structure
8945 const struct timeval tv,
8946 /// Direction:@n
8947 /// @c SO_RCVTIMEO (default)@n
8948 /// @c SO_SNDTIMEO
8949 const int direction = SO_RCVTIMEO) {
8950
8951 // --------------------------------------------------------------------------
8952 // Set timeout for the specified direction.
8953 // --------------------------------------------------------------------------
8954 if (direction == SO_RCVTIMEO || direction == SO_SNDTIMEO) {
8955 return setsockopt(SOL_SOCKET, direction, tv);
8956 } // -x- if direction -x-
8957
8958 // --------------------------------------------------------------------------
8959 // Throw exception because direction is invalid.
8960 // --------------------------------------------------------------------------
8961 throw randolf::rex::xENOTDIR("invalid timeout direction (must be SO_RCVTIMEO or SO_SNDTIMEO)");
8962
8963 } // -x- rsocket* timeout -x-
8964
8965 /*======================================================================*//**
8966 @copydoc timeout(const struct timeval, const int)
8967 @see timeout_recvline
8968 @qualifier TLS
8969 *///=========================================================================
8970 rsocket* timeout(
8971 /// Timeout in seconds
8972 const long tv_sec,
8973 /// Timeout in microseconds
8974 const long tv_usec = 0,
8975 /// Direction:@n
8976 /// @c SO_RCVTIMEO (default)@n
8977 /// @c SO_SNDTIMEO
8978 const int direction = SO_RCVTIMEO) {
8979
8980 // --------------------------------------------------------------------------
8981 // Set timeout for the specified direction.
8982 // --------------------------------------------------------------------------
8983 if (direction == SO_RCVTIMEO || direction == SO_SNDTIMEO) {
8984 struct timeval tv{tv_sec, tv_usec};
8985 return setsockopt(SOL_SOCKET, direction, tv);
8986 } // -x- if direction -x-
8987
8988 // --------------------------------------------------------------------------
8989 // Throw exception because direction is invalid.
8990 // --------------------------------------------------------------------------
8991 throw randolf::rex::xENOTDIR("invalid timeout direction (must be SO_RCVTIMEO or SO_SNDTIMEO)");
8992
8993 } // -x- rsocket* timeout -x-
8994
8995 /*======================================================================*//**
8996 @brief
8997 Find out what the read timeout is set to when using the @ref recvline()
8998 method.
8999
9000 @returns @c long value (0 = no timeout)
9001 @see recvline
9002 @see timeout
9003 @see timeout_recvline(long)
9004 @qualifier TLS
9005 *///=========================================================================
9006 long timeout_recvline() {
9007 return __recvline_timeout;
9008 } // -x- long timeout_recvline -x-
9009
9010 /*======================================================================*//**
9011 @brief
9012 Set the read timeout for the @ref recvline() method (the @ref recvline()
9013 method's @c timeout parameter can override this setting).
9014
9015 @note
9016 The default timeout for this recvline_timeout setting is 0 (no timeout).
9017
9018 @throws randolf::rex::xERANGE if the timeout parameter is below 0
9019
9020 @returns The same rsocket object so as to facilitate stacking
9021 @see recvline
9022 @see timeout
9023 @see timeout_recvline
9024 @qualifier TLS
9025 *///=========================================================================
9026 rsocket* timeout_recvline(
9027 /// timeval structure
9028 const long timeout) {
9029 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
9030 __recvline_timeout = timeout;
9031 return this;
9032 } // -x- rsocket* timeout_recvline -x-
9033
9034 /*======================================================================*//**
9035 @brief
9036 Enable or disable encrypted communications (from the OpenSSL library).
9037
9038 @warning
9039 TLS cannot be enabled before a non-encrypted rsocket is open (an rsocket is
9040 typically opened with the @ref socket() method, the @ref connect() method, or
9041 one of the @ref accept() methods). If calling @c tls(true) on an rsocket
9042 that isn't open, an exception will be thrown.
9043
9044 If needed, a new TLS context will be instantiated and TLS will be initialized
9045 (if this hasn't already been done). TLS instantiation can be done first by
9046 calling the @ref tls_ctx() method (regardless of whether encryption is being
9047 enabled or disabled). If the default @ref TLS_FLAGS aren't sufficient for
9048 the needs of your application, then the @ref tls_ctx() method facilitates
9049 this regardless of wehther rsocket is open.
9050
9051 @note
9052 The reason a TLS context is instantiated and TLS is initialized even when the
9053 status is being set to FALSE is to facilitate TLS ingress from an unencrypted
9054 connection later in the session (see the @ref TLS_NO_INGRESS flag for more
9055 information).
9056
9057 @post
9058 If a client attempts to upgrade a TLS connection to TLS (e.g., by using a
9059 command such as @c STARTTLS, which is commonly transmitted in unencrypted
9060 form, like @c telnet-ssl does -- using `telnet-ssl -z ssl` prevents this
9061 condition), the following error that's difficult to track down may be
9062 triggered when calling any of the @c recv methods (I hope that including this
9063 information here in this documentation will be helpful):
9064 @verbatim
9065 SSL_ERROR_UNKNOWN: error:0A00010B:SSL routines::wrong version number
9066 @endverbatim
9067 This is most likely not a programming error, but rather a problem with how
9068 users may attempt to mis-use a connection based on a misunderstanding of the
9069 communications requirements (e.g., connecting unencrypted and attempting to
9070 upgrade to TLS over a connection that's expecting TLS encrypted data from the
9071 very beginning, without involving any ingress).
9072
9073 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9074 OpenSSL library doesn't document which errors may be returned)
9075
9076 @returns The same rsocket object so as to facilitate stacking
9077 @see is_tls
9078 @see tls_ctx
9079 @qualifier TLS
9080 *///=========================================================================
9081 rsocket* tls(
9082 /// TRUE = Enable encrypted communications@n FALSE = Disable encrypted communications
9083 const bool status = true,
9084 /// Configuration parameters
9085 const int flags = TLS_FLAGS::TLS_DEFAULT) { // | TLS_FLAGS::TLS_CLIENT
9086 if (__debug) debug("tls(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9087 + ", " + (status ? "true" : "false")
9088 + ");");
9089
9090 // --------------------------------------------------------------------------
9091 // Create default context (with "flags" passthrough), unless it was already
9092 // created (usually by one of the tls_ctx() methods).
9093 // --------------------------------------------------------------------------
9094 if (__tls_ctx == nullptr) tls_ctx((SSL_CTX*)nullptr, flags);
9095
9096 // --------------------------------------------------------------------------
9097 // Make sure tls_fd (ssl_fd) is allocated. If not, then it needs to be
9098 // allocated and configured.
9099 // --------------------------------------------------------------------------
9100 if (status == true && __tls_fd == nullptr) {
9101 __tls_fd = SSL_new(__tls_ctx);
9102 if (__debug) debug("SSL_set_fd(<tls_fd>, socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9103 + ");");
9104
9105 // --------------------------------------------------------------------------
9106 // Associate OpenSSL file descriptor with underlying socket file descriptor.
9107 // --------------------------------------------------------------------------
9108 __rc_check_tls(SSL_set_fd(__tls_fd, __socket_fd));
9109
9110 // --------------------------------------------------------------------------
9111 // Enable read-ahead so that SSL_peek will work more efficiently.
9112 // --------------------------------------------------------------------------
9113 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
9114 SSL_set_read_ahead(__tls_fd, 1); // 0=disable / 1=enable
9115
9116// SSL_CTX_set_max_pipelines(__tls_ctx, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
9117// SSL_set_max_pipelines(__tls_fd, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
9118
9119// SSL_CTX_set_mode(__tls_ctx, SSL_MODE_AUTO_RETRY);
9120// SSL_set_mode(__tls_fd, SSL_MODE_AUTO_RETRY);
9121
9122 // --------------------------------------------------------------------------
9123 // We're probably not going to use BIO because we don't need it, and it adds
9124 // unnecessary overhead for what we're doing. Also, BIO doesn't provide an
9125 // alternative to the SSL_peek function, and also doesn't resolve the need
9126 // for the MSG_WAITALL flag that the ::recv() function supports.
9127 //
9128 // TODO: Remove this completely, unless is can solve the problem of reading
9129 // all incoming data (needed for readline, primarily)
9130 // --------------------------------------------------------------------------
9131 //__tls_rbio = SSL_get_rbio(__tls_fd);
9132 //__tls_wbio = SSL_get_wbio(__tls_fd);
9133
9134 } // -x- if !__tls_fd -x-
9135 __tls = status;
9136 return this;
9137 } // -x- rsocket* tls -x-
9138
9139 /*======================================================================*//**
9140 @brief
9141 Return the current TLS context (multiple TLS contexts are supported, although
9142 typically needed to support SNI with inbound connections).
9143 @returns Pointer to OpenSSL's context (normally labelled @c SSL_CTX* in the
9144 documentation for OpenSSL), or nullptr if this context was never
9145 assigned to (or created by) this rsocket
9146 @see tls_ctx
9147 @qualifier TLS
9148 *///=========================================================================
9149 SSL_CTX* tls_ctx() noexcept {
9150 return __tls_ctx;
9151 } // -x- SSL_CTX* tls_ctx -x-
9152
9153 /*======================================================================*//**
9154 @brief
9155 Copy the source rsocket's TLS context map and add it to this rsocket's
9156 collection; or, if the source doesn't have any TLS contexts and this rsocket
9157 doesn't have any TLS contexts in its collection, then initialize TLS and
9158 instantiate a new TLS context. In either scenario, the source rsocket will
9159 be treated as a template as all TLS flags duplicated to enable encrypted
9160 socket I/O for use in this rsocket().
9161
9162 @note
9163 At least one TLS context is needed to enable encrypted socket I/O for use in
9164 this rsocket().
9165
9166 @post
9167 Encrypted socket I/O is only possible after a TLS context has been
9168 initialized (this is not a global setting as it has per-rsocket specificity).
9169
9170 @note
9171 The only @ref TLS_FLAGS flag that doesn't get transferred is @ref TLS_SERVER
9172 when no flags are specified. Specifying any flag(s) will cause this method
9173 to ignore the source rsocket's TLS flags so as to defer to this override.
9174
9175 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9176 OpenSSL library doesn't document which errors may be returned)
9177
9178 @returns The same rsocket object so as to facilitate stacking
9179 @see tls_sni
9180 @qualifier TLS
9181 *///=========================================================================
9182 rsocket* tls_ctx(
9183 /// OpenSSL's TLS context to use (if not provided, a new context will be
9184 /// created automatically using OpenSSL's defaults)
9185 rsocket* rtemplate,
9186 /// Configuration parameters
9187 const int flags = TLS_FLAGS::TLS_DEFAULT) {
9188 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9189 + ", <rsocket>"
9190 + ", " + std::to_string(flags)
9191 + ");");
9192
9193 // --------------------------------------------------------------------------
9194 // TLS-related variables (OpenSSL).
9195 //
9196 // Note: The __tls_fd variable cannot be allocated until after __socket_fd
9197 // has been, hence the "post" note in the documentation.
9198 // --------------------------------------------------------------------------
9199 __tls = (bool)rtemplate->__tls; // Preserve TLS mode setting
9200 if (rtemplate->__tls_ctx != nullptr) { // If TLS context is defined, then do these important things:
9201 __tls_ctx = rtemplate->__tls_ctx; // 1. copy the pointer to SSL_CTX
9202 SSL_CTX_up_ref(__tls_ctx); // 2. increment SSL_CTX's internal reference count
9203 } // -x- if __tlx_ctx -x-
9204
9205 // --------------------------------------------------------------------------
9206 // Copy or override TLS flags.
9207 // --------------------------------------------------------------------------
9208 if (flags == TLS_FLAGS::TLS_DEFAULT) {
9209 __tls_exclusive = rtemplate->__tls_exclusive; // TLS policy
9210 __tls_egress = rtemplate->__tls_egress; // TLS policy
9211 __tls_ingress = rtemplate->__tls_ingress; // TLS policy
9212 } else { // Save flags
9213 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
9214 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
9215 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
9216 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
9217 } // -x- if flags -x-
9218
9219 return this;
9220 } // -x- rsocket* tls_ctx -x-
9221
9222 /*======================================================================*//**
9223 @brief
9224 Initialize TLS and instantiate a TLS context, and add it to this rsocket's
9225 current collection of TLS contexts, and set it as the currently active TLS
9226 context (so that a certificate chain and private key may be added to it).
9227 @note
9228 At least one TLS context is needed to enable encrypted socket I/O for use in
9229 this rsocket().
9230 @post
9231 Encrypted socket I/O is only possible after a TLS context has been
9232 initialized (this is not a global setting as it has per-rsocket specificity).
9233 @note
9234 This is the default TLS context for this @c rsocket, which will also be used
9235 for non-SNI handshakes.
9236
9237 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9238 OpenSSL library doesn't document which errors may be returned)
9239
9240 @returns The same rsocket object so as to facilitate stacking
9241 @see tls_sni
9242 @qualifier TLS
9243 *///=========================================================================
9244 rsocket* tls_ctx(
9245 /// OpenSSL's TLS context to use (if not provided, a new context will be
9246 /// created using OpenSSL's defaults)
9247 SSL_CTX* ctx,
9248 /// Configuration parameters
9249 const int flags = TLS_FLAGS::TLS_DEFAULT) {
9250 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9251 + ", <ctx>"
9252 + ", " + std::to_string(flags)
9253 + ");");
9254
9255 // --------------------------------------------------------------------------
9256 // Ignore repeated calls to this method.
9257 // --------------------------------------------------------------------------
9258 if (__tls_ctx != nullptr) return this;
9259
9260 // --------------------------------------------------------------------------
9261 // Fire up OpenSSL's algorithms and pre-load its error strings.
9262 //
9263 // These two functions have been deprecated since OpenSSL v1.1.0, so we don't
9264 // need to call them. If someone needs them, then they can always call them
9265 // in their own code anyway.
9266 // --------------------------------------------------------------------------
9267 //OpenSSL_add_all_algorithms(); // Load and register cryptography algorithms
9268 //SSL_load_error_strings(); // Load all error messages into memory
9269
9270 // --------------------------------------------------------------------------
9271 // Save (ctx != nullptr) or create (ctx == nullptr) OpenSSL context.
9272 // --------------------------------------------------------------------------
9273 __tls_ctx = ctx == nullptr ? SSL_CTX_new(flags & TLS_FLAGS::TLS_SERVER ? TLS_server_method() : TLS_client_method()) // Create anew (default)
9274 : ctx; // Use OpenSSL context that was provided
9275 if (__tls_ctx == nullptr) randolf::rex::mk_exception("Cannot create TLS context", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
9276
9277 // --------------------------------------------------------------------------
9278 // Enable read-ahead so that SSL_peek will work properly.
9279 // --------------------------------------------------------------------------
9280 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
9281
9282 // --------------------------------------------------------------------------
9283 // Save flags.
9284 // --------------------------------------------------------------------------
9285 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
9286 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
9287 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
9288 __tls_server_mode = (flags & TLS_FLAGS::TLS_SERVER);
9289
9290 return this;
9291 } // -x- rsocket* tls_ctx -x-
9292
9293 /*======================================================================*//**
9294 @brief
9295 Check the private key it to ensure it's consistent with the corresponding TLS
9296 certificate chain.
9297
9298 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9299 OpenSSL library doesn't document which errors may be returned)
9300
9301 @returns The same rsocket object so as to facilitate stacking
9302 @see tls_ctx_use_privatekey_file
9303 @see tls_ctx_use_privatekey_pem
9304 @qualifier TLS
9305 *///=========================================================================
9306 rsocket* tls_ctx_check_privatekey() {
9307 if (__debug) debug("tls_ctx_check_privatekey();");
9308 if (!SSL_CTX_check_private_key(__tls_ctx)) randolf::rex::mk_exception("Cannot validate consistency between certificate chain and private key", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
9309 return this;
9310 } // -x- rsocket* tls_ctx_check_privatekey -x-
9311
9312 /*======================================================================*//**
9313 @brief
9314 Load a TLS certificate chain and private key in PEM format from text files
9315 and use them in the TLS context.
9316
9317 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9318 OpenSSL library doesn't document which errors may be returned)
9319
9320 @returns The same rsocket object so as to facilitate stacking
9321 @see tls_ctx_use_certificate_chain_and_privatekey_pems
9322 @see tls_ctx_use_certificate_chain_file
9323 @see tls_ctx_use_certificate_chain_pem
9324 @see tls_ctx_use_privatekey_file
9325 @see tls_ctx_use_privatekey_pem
9326 @see tls_ctx_check_privatekey
9327 @qualifier TLS
9328 *///=========================================================================
9329 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
9330 /// Pointer to ASCIIZ path and filename to certificate chain file (@c nullptr
9331 /// will simply be ignored)
9332 const char* chain_file,
9333 /// Pointer to ASCIIZ path and filename to private key file (@c nullptr will
9334 /// simply be ignored)
9335 const char* key_file) {
9336 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
9337 + std::string(chain_file)
9338 + ", " + std::string( key_file)
9339 + ");");
9340 if (chain_file != nullptr) tls_ctx_use_certificate_chain_file(chain_file);
9341 if ( key_file != nullptr) tls_ctx_use_privatekey_file( key_file);
9342 return this;
9343 } // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
9344
9345 /*======================================================================*//**
9346 @brief
9347 Load a TLS certificate chain and private key in PEM format from text files
9348 and use them in the TLS context.
9349
9350 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9351 OpenSSL library doesn't document which errors may be returned)
9352
9353 @returns The same rsocket object so as to facilitate stacking
9354 @see tls_ctx_use_certificate_chain_and_privatekey_pems
9355 @see tls_ctx_use_certificate_chain_file
9356 @see tls_ctx_use_certificate_chain_pem
9357 @see tls_ctx_use_privatekey_file
9358 @see tls_ctx_use_privatekey_pem
9359 @see tls_ctx_check_privatekey
9360 @qualifier TLS
9361 *///=========================================================================
9362 rsocket* tls_ctx_use_certificate_chain_and_privatekey_files(
9363 /// Pointer to ASCIIZ path and filename to certificate chain file (an empty
9364 /// string will simply be ignored)
9365 const std::string chain_file,
9366 /// Pointer to ASCIIZ path and filename to private key file (an empty string
9367 /// will simply be ignored)
9368 const std::string key_file) {
9369 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
9370 + chain_file
9371 + ", " + key_file
9372 + ");");
9373 if (!chain_file.empty()) tls_ctx_use_certificate_chain_file(chain_file);
9374 if ( !key_file.empty()) tls_ctx_use_privatekey_file( key_file);
9375 return this;
9376 } // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_files -x-
9377
9378 /*======================================================================*//**
9379 @brief
9380 Load a TLS certificate chain and a TLS private key in PEM format from memory
9381 and use them in the TLS context.
9382
9383 Although this functionality doesn't exist in OpenSSL (at the time of writing
9384 this method), it's provided here in a manner that has exactly the same effect
9385 as the @ref tls_ctx_use_certificate_chain_and_privatekey_files() methods, but
9386 without needing the PEM-formatted certificate chain stored in files
9387 beforehand.
9388
9389 @note
9390 The @c cert_pem_data and key_pem_data parameters are pointers to the memory
9391 locations that holds the PEM formatted certificate chain data and private key
9392 data, respectively. If the corresponding lengths of each of these data aren't
9393 specified or are set to zero (default), then they will be treated as multiline
9394 ASCIIZ strings.
9395
9396 Behind the scenes, we're just writing the cert_pem_data and key_pem_data
9397 memory to temporary files with severely-limited permissions (), then
9398 optionally overwriting those temporary files with random data prior to
9399 deleting them (this is the default, since better security practices should be
9400 the default, but on a secured system it may not be necessary and so this
9401 option can also be disabled to save CPU cycles and reduce overall disk-write
9402 I/O operations).
9403
9404 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9405 OpenSSL library doesn't document which errors may be returned)
9406
9407 @returns The same rsocket object so as to facilitate stacking
9408 @see tls_ctx_use_certificate_chain_and_privatekey_files
9409 @see tls_ctx_use_certificate_chain_file
9410 @see tls_ctx_use_certificate_chain_pem
9411 @see tls_ctx_use_privatekey_file
9412 @see tls_ctx_use_privatekey_pem
9413 @see tls_ctx_check_privatekey
9414 @qualifier TLS
9415 *///=========================================================================
9416 rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems(
9417 /// Pointer to certificate chain data in PEM format
9418 const char* cert_pem_data,
9419 /// Pointer to private key data in PEM format
9420 const char* key_pem_data,
9421 /// Length of cert_pem_data (in bytes), or 0 to auto-detect length if cert_pem_data is an ASCIIZ string
9422 size_t cert_len = 0,
9423 /// Length of key_pem_data (in bytes), or 0 to auto-detect length if key_pem_data is an ASCIIZ string
9424 size_t key_len = 0,
9425 /// Whether to overwrite the temporary files with random data before deleting them
9426 const bool random_fill = true) {
9427 tls_ctx_use_certificate_chain_pem(cert_pem_data, cert_len, random_fill);
9428 tls_ctx_use_privatekey_pem( key_pem_data, key_len, random_fill);
9429 return this;
9430 } // -x- rsocket* tls_ctx_use_certificate_chain_and_privatekey_pems -x-
9431
9432 /*======================================================================*//**
9433 @brief
9434 Load a TLS certificate chain in PEM format from a text file and use it in the
9435 TLS context.
9436
9437 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9438 OpenSSL library doesn't document which errors may be returned)
9439
9440 @returns The same rsocket object so as to facilitate stacking
9441 @see tls_ctx_use_certificate_chain_file
9442 @see tls_ctx_use_certificate_chain_pem
9443 @see tls_ctx_check_privatekey
9444 @qualifier TLS
9445 *///=========================================================================
9446 rsocket* tls_ctx_use_certificate_chain_file(
9447 /// Pointer to ASCIIZ path and filename to certificate chain file
9448 const char* file) {
9449 if (__debug) debug("tls_ctx_use_certificate_chain_file("
9450 + std::string(file)
9451 + ");");
9452 if (SSL_CTX_use_certificate_chain_file(__tls_ctx, file) != 1) randolf::rex::mk_exception(std::string("Cannot use certificate chain file ") + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
9453 return this;
9454 } // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
9455
9456 /*======================================================================*//**
9457 @brief
9458 Load a TLS certificate chain in PEM format from a text file and use it in the
9459 TLS context.
9460
9461 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9462 OpenSSL library doesn't document which errors may be returned)
9463
9464 @returns The same rsocket object so as to facilitate stacking
9465 @see tls_ctx_use_certificate_chain_file
9466 @see tls_ctx_use_certificate_chain_pem
9467 @see tls_ctx_check_privatekey
9468 @qualifier TLS
9469 *///=========================================================================
9470 rsocket* tls_ctx_use_certificate_chain_file(
9471 /// Path and filename to certificate chain file
9472 const std::string file) {
9473 if (__debug) debug("tls_ctx_use_certificate_chain_file("
9474 + file
9475 + ");");
9476 if (SSL_CTX_use_certificate_chain_file(__tls_ctx, file.data()) != 1) randolf::rex::mk_exception("Cannot use certificate chain file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
9477 return this;
9478 } // -x- rsocket* tls_ctx_use_certificate_chain_file -x-
9479
9480 /*======================================================================*//**
9481 @brief
9482 Load a TLS certificate chain in PEM format from memory and use it in the TLS
9483 context.
9484
9485 Although this functionality doesn't exist in OpenSSL (at the time of writing
9486 this method), it's provided here in a manner that has exactly the same effect
9487 as the @ref tls_ctx_use_certificate_chain_file() methods, but without needing
9488 the PEM-formatted certificate chain stored in a file beforehand.
9489
9490 @note
9491 The @c pem_data parameter is a pointer to the memory location that holds
9492 the PEM formatted certificate chain data. If the length of this data isn't
9493 specified or is set to zero (default), then it will be treated as a multiline
9494 ASCIIZ string.
9495
9496 Behind the scenes, we're just writing the pem_data memory to a temporary
9497 file with severely-limited permissions (), then optionally overwriting that
9498 temporary file with random data prior to deleting it (this is the default,
9499 since better security practices should be the default, but on a secured
9500 system it may not be necessary and so this option can also be disabled to
9501 save CPU cycles and reduce overall disk-write I/O operations).
9502
9503 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9504 OpenSSL library doesn't document which errors may be returned)
9505
9506 @returns The same rsocket object so as to facilitate stacking
9507 @see tls_ctx_use_certificate_chain_file
9508 @see tls_ctx_check_privatekey
9509 @qualifier TLS
9510 *///=========================================================================
9511 rsocket* tls_ctx_use_certificate_chain_pem(
9512 /// Pointer to certificate chain data in PEM format
9513 const char* pem_data,
9514 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
9515 size_t len = 0,
9516 /// Whether to overwrite the temporary file with random data before deleting it
9517 const bool random_fill = true) {
9518 if (__debug) debug("tls_ctx_use_certificate_chain_pem(<buf>, " + std::to_string(len)
9519 + ", " + (random_fill ? "true" : "false")
9520 + ");");
9521
9522 // --------------------------------------------------------------------------
9523 // Measure size of certificate chain if an ASCIIZ string was indicated.
9524 // --------------------------------------------------------------------------
9525 if (len == 0) len = std::strlen(pem_data);
9526
9527 // --------------------------------------------------------------------------
9528 // Generate filename for temporary use.
9529 // --------------------------------------------------------------------------
9530 std::string file = std::filesystem::temp_directory_path();
9531 file.append("/rsocket.")
9532 .append(std::to_string(::getpid()))
9533 .append(".")
9534 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
9535
9536 // --------------------------------------------------------------------------
9537 // Open temporary file.
9538 // --------------------------------------------------------------------------
9539 FILE* fp = fopen(file.data(), "w+");
9540 if (fp == nullptr) randolf::rex::mk_exception("Cannot open temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
9541 { int attr;
9542 ioctl(fileno(fp), FS_IOC_GETFLAGS, &attr);
9543 attr |= FS_NOATIME_FL // Don't update access time attribute
9544 | FS_NODUMP_FL // Don't include in filesystem backup dumps
9545 | FS_SECRM_FL; // Mark file for secure deletion (where supported)
9546 ioctl(fileno(fp), FS_IOC_SETFLAGS, &attr);
9547 }
9548 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
9549 if (fputs(pem_data, fp) == EOF) {
9550 fclose(fp);
9551 randolf::rex::mk_exception("Cannot write to temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
9552 } // -x- if !fputs -x-
9553 fflush(fp);
9554
9555 // --------------------------------------------------------------------------
9556 // Attempt to load certificate chain file, but save the error code for later
9557 // because we need to clean up the temporary file before possibly throwing an
9558 // exception.
9559 // --------------------------------------------------------------------------
9560 int rc = SSL_CTX_use_certificate_chain_file(__tls_ctx, file.data());
9561
9562 // --------------------------------------------------------------------------
9563 // Overwrite the contenst of the temporary file before deleting it so as to
9564 // sabotage a simple attempt to undelete the file and access the certificate.
9565 //
9566 // We're also re-using the "len" local variable because it's not needed once
9567 // we get the loop started, and it's more optimal to not allocate yet another
9568 // local variable while "len" goes to waste. :D
9569 // --------------------------------------------------------------------------
9570 if (random_fill) { // This option is configurable
9571 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
9572 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
9573 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
9574 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
9575 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
9576 } // -x- for i -x-
9577 } // -x- if randfill -x-
9578 fchmod(fileno(fp), 0); // Remove all permissions
9579 fclose(fp); // Close file handle
9580 unlink(file.data()); // Delete temporary file
9581
9582 // --------------------------------------------------------------------------
9583 // Error check ... was delayed here until after temporary file cleanup.
9584 // --------------------------------------------------------------------------
9585 if (rc != 1) randolf::rex::mk_exception("Cannot use certificate chain file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
9586
9587 return this;
9588 } // -x- rsocket* tls_ctx_use_certificate_chain_pem -x-
9589
9590 /*======================================================================*//**
9591 @brief
9592 Load a TLS private key in PEM format from a text file and use it in the TLS
9593 context.
9594
9595 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9596 OpenSSL library doesn't document which errors may be returned)
9597
9598 @returns The same rsocket object so as to facilitate stacking
9599 @see tls_ctx_use_privatekey_file
9600 @see tls_ctx_use_privatekey_pem
9601 @see tls_ctx_check_privatekey
9602 @qualifier TLS
9603 *///=========================================================================
9604 rsocket* tls_ctx_use_privatekey_file(
9605 /// Pointer to ASCIIZ path-and-filename of private key file
9606 const char* file) {
9607 if (__debug) debug("tls_ctx_use_privatekey_file("
9608 + std::string(file)
9609 + ");");
9610 if (SSL_CTX_use_PrivateKey_file(__tls_ctx, file, SSL_FILETYPE_PEM) != 1) randolf::rex::mk_exception(std::string("Cannot use private key file ") + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
9611
9612 return this;
9613 } // -x- rsocket* tls_ctx_use_privatekey_file -x-
9614
9615 /*======================================================================*//**
9616 @brief
9617 Load a TLS private key in PEM format from a text file and use it in the TLS
9618 context.
9619
9620 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9621 OpenSSL library doesn't document which errors may be returned)
9622
9623 @returns The same rsocket object so as to facilitate stacking
9624 @see tls_ctx_use_privatekey_file
9625 @see tls_ctx_use_privatekey_pem
9626 @see tls_ctx_check_privatekey
9627 @qualifier TLS
9628 *///=========================================================================
9629 rsocket* tls_ctx_use_privatekey_file(
9630 /// Path and filename to private key file
9631 const std::string file) {
9632 if (__debug) debug("tls_ctx_use_privatekey_file("
9633 + file
9634 + ");");
9635 if (SSL_CTX_use_PrivateKey_file(__tls_ctx, file.data(), SSL_FILETYPE_PEM) != 1) randolf::rex::mk_exception("Cannot use private key file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
9636
9637 return this;
9638 } // -x- rsocket* tls_ctx_use_privatekey_file -x-
9639
9640 /*======================================================================*//**
9641 @brief
9642 Load a TLS private key in PEM format from memory and use it in the TLS
9643 context.
9644
9645 Although this functionality doesn't exist in OpenSSL (at the time of writing
9646 this method), it's provided here in a manner that has exactly the same effect
9647 as the @ref tls_ctx_use_privatekey_file() methods, but without needing the
9648 PEM-formatted private key stored in a file beforehand.
9649
9650 @note
9651 The @c pem_data parameter is a pointer to the memory location that holds the
9652 PEM formatted private key data. If the length of this data isn't specified
9653 or is set to zero (default), then it will be treated as a multiline ASCIIZ
9654 string.
9655
9656 Behind the scenes, we're just writing the pem_data memory to a temporary
9657 file (with severely-limited permissions), then optionally overwriting that
9658 temporary file with random data prior to deleting it (this is the default,
9659 since better security practices should be the default, but on a secured
9660 system it may not be necessary and so this option can also be disabled to
9661 save CPU cycles and reduce overall disk-write I/O operations).
9662
9663 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9664 OpenSSL library doesn't document which errors may be returned)
9665
9666 @returns The same rsocket object so as to facilitate stacking
9667 @see tls_ctx_use_privatekey_file
9668 @see tls_ctx_check_privatekey
9669 @qualifier TLS
9670 *///=========================================================================
9671 rsocket* tls_ctx_use_privatekey_pem(
9672 /// Pointer to private key data in PEM format
9673 const char* pem_data,
9674 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
9675 size_t len = 0,
9676 /// Whether to overwrite the temporary file with random data before deleting it
9677 const bool random_fill = true) {
9678 if (__debug) debug("tls_ctx_use_privatekey_pem(<buf>, " + std::to_string(len)
9679 + ", " + (random_fill ? "true" : "false")
9680 + ");");
9681
9682 // --------------------------------------------------------------------------
9683 // Measure size of private key if an ASCIIZ string was indicated.
9684 // --------------------------------------------------------------------------
9685 if (len == 0) len = std::strlen(pem_data);
9686
9687 // --------------------------------------------------------------------------
9688 // Generate filename for temporary use.
9689 // --------------------------------------------------------------------------
9690 std::string file = std::filesystem::temp_directory_path();
9691 file.append("/rsocket.")
9692 .append(std::to_string(::getpid()))
9693 .append(".")
9694 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
9695
9696 // --------------------------------------------------------------------------
9697 // Open temporary file.
9698 // --------------------------------------------------------------------------
9699 FILE* fp = fopen(file.data(), "w+");
9700 if (fp == nullptr) randolf::rex::mk_exception("Cannot cannot open temporary file (to use private key) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
9701 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
9702 if (fputs(pem_data, fp) == EOF) {
9703 fclose(fp);
9704 randolf::rex::mk_exception("Cannot cannot write to temporary file (to use private key) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
9705 } // -x- if !fputs -x-
9706 fflush(fp);
9707
9708 // --------------------------------------------------------------------------
9709 // Attempt to load private key file, but save the error code for later
9710 // because we need to clean up the temporary file before possibly throwing an
9711 // exception.
9712 // --------------------------------------------------------------------------
9713 int rc = SSL_CTX_use_PrivateKey_file(__tls_ctx, file.data(), SSL_FILETYPE_PEM);
9714
9715 // --------------------------------------------------------------------------
9716 // Overwrite the contenst of the temporary file before deleting it so as to
9717 // sabotage a simple attempt to undelete the file and access the certificate.
9718 //
9719 // We're also re-using the "len" local variable because it's not needed once
9720 // we get the loop started, and it's more optimal to not allocate yet another
9721 // local variable while "len" goes to waste. :D
9722 // --------------------------------------------------------------------------
9723 if (random_fill) { // This option is configurable
9724 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
9725 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
9726 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
9727 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
9728 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
9729 } // -x- for i -x-
9730 } // -x- if randfill -x-
9731 fchmod(fileno(fp), 0); // Remove all permissions
9732 fclose(fp); // Close file handle
9733 unlink(file.data()); // Delete temporary file
9734
9735 // --------------------------------------------------------------------------
9736 // Error check ... was delayed here until after temporary file cleanup.
9737 // --------------------------------------------------------------------------
9738 if (rc != 1) randolf::rex::mk_exception("Cannot use private key file " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO | rex::rex::REX_FLAGS::REX_TLS);
9739
9740 return this;
9741 } // -x- rsocket* tls_ctx_use_privatekey_pem -x-
9742
9743 /*======================================================================*//**
9744 @brief
9745 Initiate the TLS handshake with the endpoint (which is presumed to be a
9746 server).
9747 This method makes it easier to support application-level commands such as @c
9748 STARTTLS (which are implemented in protocols like SMTP, POP3, IMAP4, MEOW,
9749 FTP, NNTP, LDAP, XMPP, etc.).
9750
9751 @throws randolf::rex::xALL Catch this exception for now (at this time, the
9752 OpenSSL library doesn't document which errors may be returned)
9753
9754 @returns The same rsocket object so as to facilitate stacking
9755 @see connect()
9756 @see connect(std::string, int)
9757 @see TLS_NO_INGRESS
9758 @qualifier TLS
9759 *///=========================================================================
9760 rsocket* tls_do_handshake() {
9761 if (__debug) debug("tls_handshake(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9762 + ");");
9763 __rc_check_tls(SSL_do_handshake(__tls_fd));
9764 return this;
9765 } // -x- rsocket* tls_do_handshake -x-
9766
9767 /*======================================================================*//**
9768 @brief
9769 Get OpenSSL's TLS structure.
9770 @returns TLS structure
9771 @returns nullptr = TLS structure not yet allocated
9772 @qualifier TLS
9773 *///=========================================================================
9774 const SSL* tls_fd() noexcept {
9775 return __tls_fd;
9776 } // -x- int tls_fd -x-
9777
9778 /*======================================================================*//**
9779 @brief
9780 Return the current @ref rsocket_sni object that this @c rsocket will use when
9781 accepting incoming encrypted connections.
9782 @returns Pointer to @c rsocket_sni object
9783 @returns nullptr = SNI is not assigned
9784 @see name_sni
9785 @see tls_sni(rsocket_sni*)
9786 @qualifier TLS
9787 *///=========================================================================
9788 rsocket_sni* tls_sni() noexcept {
9789 return __tls_sni;
9790 } // -x- rsocket_sni* tls_sni -x-
9791
9792 /*======================================================================*//**
9793 @brief
9794 Set the current @ref rsocket_sni object that this @c rsocket will use when
9795 accepting incoming encrypted connections.
9796
9797 Use the @ref name() method to find out which server name was supplied by the
9798 endpoint that triggered the SNI callback, regardless of whether it matches
9799 any of the TLS certificates used with this rsocket object or the rsocket_sni
9800 object that's associated with this rsocket object. If an SNI callback wasn't
9801 triggered, or if the endpoint didn't provide a server name, then it will
9802 remain unaffected (and the default {empty string} will remain unchanged).
9803 @returns The same rsocket object so as to facilitate stacking
9804 @see name_sni
9805 @see tls_sni
9806 @see is_tls_sni_match
9807 @qualifier TLS
9808 *///=========================================================================
9809 rsocket* tls_sni(
9810 /// Pointer to the @ref rsocket_sni object to use, or specify @c nullptr to
9811 /// remove SNI support from this rsocket object
9812 rsocket_sni* sni) noexcept {
9813
9814 // --------------------------------------------------------------------------
9815 // Remove SNI support.
9816 // --------------------------------------------------------------------------
9817 if (sni == nullptr) {
9818
9819 // --------------------------------------------------------------------------
9820 // Only remove SNI support if it's configured.
9821 // --------------------------------------------------------------------------
9822 if (__tls_sni != nullptr) SSL_CTX_set_client_hello_cb(__tls_ctx, nullptr, this);
9823
9824 // --------------------------------------------------------------------------
9825 // Add or set SNI support.
9826 // --------------------------------------------------------------------------
9827 } else {
9828
9829 // --------------------------------------------------------------------------
9830 // Configure SNI callbacks for TLS. This callback will occur in the early
9831 // stages, which is triggered by the SSL_accept() method.
9832 // --------------------------------------------------------------------------
9833 SSL_CTX_set_client_hello_cb(__tls_ctx, tls_sni_callback, this);
9834
9835 } // -x- if !sni -x-
9836
9837 // --------------------------------------------------------------------------
9838 // Update internal pointer to the SNI map, or nullptr if it's being removed.
9839 // --------------------------------------------------------------------------
9840 __tls_sni = sni;
9841
9842 return this;
9843 } // -x- rsocket_sni* tls_sni -x-
9844
9845private:
9846 /*======================================================================*//**
9847 @brief
9848 Get OpenSSL's TLS structure.
9849 @returns TLS structure
9850 @returns nullptr = TLS structure not yet allocated
9851 @qualifier TLS
9852 *///=========================================================================
9853 int static tls_sni_callback(
9854 /// OpenSSL's socket descriptor/handle
9855 SSL* tls_fd,
9856 /// Where to store the @c alert value
9857 int* al,
9858 /// Context-specific argument
9859 void* arg) noexcept {
9860
9861 // --------------------------------------------------------------------------
9862 // Internal variables.
9863 // --------------------------------------------------------------------------
9864 rsocket* r = (rsocket*)arg; // We may be updating the TLS context (this is normally the new client's rsocket object)
9865 const unsigned char* out = nullptr;
9866 size_t out_size = 0;
9867
9868 // --------------------------------------------------------------------------
9869 // Obtain a pointer to newly-allocated ClientHello fields data. If *out is
9870 // nullptr, it means that no TLSEXT_TYPE_server_name was received from the
9871 // client, and so the default TLS context will suffice. If out_size is less
9872 // than or equal to 2, then it also won't have what we need.
9873 // --------------------------------------------------------------------------
9874 if (!SSL_client_hello_get0_ext(tls_fd, TLSEXT_TYPE_server_name, &out, &out_size)
9875 || out == nullptr
9876 || out_size <= 2) {
9877 //std::cout << "no server_name / out_size=" << out_size << std::endl;
9878 r->__tls_sni_has_name = false; // No SNI name
9879 r->__tls_sni_match = false; // No SNI match
9880 return SSL_CLIENT_HELLO_SUCCESS; // 1
9881 }
9882// if (!SSL_client_hello_get0_ext(tls_fd, TLSEXT_TYPE_server_name, &out, &out_size)
9883// || out == nullptr || out_size <= 2) return SSL_CLIENT_HELLO_SUCCESS; // 1
9884
9885 // --------------------------------------------------------------------------
9886 // Scan for TLSEXT_NAMETYPE_host_name, but if we can't find it then we'll
9887 // just leave the default TLS context as is.
9888 // --------------------------------------------------------------------------
9889 unsigned char* pos = (unsigned char*)out;
9890 size_t len = (*(pos++) << 8); // Extract first MSB
9891 len += *(pos++); // Add first LSB
9892//std::cout << "scan for host_name / out_size=" << out_size << " len=" << len << std::endl;
9893 if (len + 2 != out_size) return SSL_CLIENT_HELLO_SUCCESS; // 1 // goto finish;
9894 out_size = len;
9895
9896 // --------------------------------------------------------------------------
9897 // We're taking a shortcut by examining only the first element in the list,
9898 // but in the future we need to make this more robust in case other types of
9899 // elements precede what we're looking for.
9900 //
9901 // Unfortunately, there's no documentation that properly-explains the format
9902 // of the list, so some deeper research into OpenSSL's source code will be
9903 // needed (a cursory look so far has not yielded the necessary insight).
9904 //
9905 // TODO: Turn this into a loop that supports future clients that provide
9906 // multiple SNI server names in their requests. (Although this isn't
9907 // occuring at present with common end-user tools such as web browsers
9908 // and eMail software, it may happen in the future as client/server
9909 // software becomes more sophisticated to support users, some of whom
9910 // are gradually becoming more savvy.)
9911 // --------------------------------------------------------------------------
9912 if (out_size <= 3 || *pos++ != TLSEXT_NAMETYPE_host_name) return SSL_CLIENT_HELLO_SUCCESS; // 1 // goto finish;
9913 out_size--; // Avoid a buffer overrun caused by corrupt or prematurely-truncated data
9914
9915//std::cout << "out_size=" << out_size << std::endl;
9916
9917 // --------------------------------------------------------------------------
9918 // Extract and use the hostname (SNI server name) that was supplied by the
9919 // endpoint so that the correct TLS certificate can be selected and assigned.
9920 // --------------------------------------------------------------------------
9921 len = (*(pos++) << 8); // Extract second MSB
9922 len += *(pos++); // Add second LSB
9923 if (!(len + 2 > out_size)) { // Only process SNI server name string if it's at least 2 bytes long
9924 // Debug: std::cout << "Server name: " << (const char*)p << std::endl; // Debug
9925
9926//std::cout << "out_size=" << out_size << " len=" << len << std::endl;
9927
9928 // --------------------------------------------------------------------------
9929 // Obtain the correct TLS context (wildcards supported) that is associated
9930 // with the hostname (SNI server name) that was supplied by the endpoint.
9931 // --------------------------------------------------------------------------
9932 const char* sni_name = (const char*)pos;
9933 r->__name_sni.assign(sni_name, len);
9934 SSL_CTX* new_ctx = r->__tls_sni->get_ctx(r->__name_sni.data(), true); // Third parameter (nullptr by default) is the default context (e.g., r->tls_ctx()) if SNI does not match
9935 r->__tls_sni_has_name = sni_name[0] != 0; // If the ASCIIZ string is empty, then SNI didn't receive a hostname or the hostname entry was empty
9936 r->__tls_sni_match = new_ctx != nullptr; // Track whether sni_name matches any of the certificates that are stored in rsocket_sni
9937
9938 // --------------------------------------------------------------------------
9939 // Change TLS context so that encryption with the endpoint uses the correct
9940 // TLS certificate (otherwise the endpoint will indicate security risk errors
9941 // to end users, log files, etc., and may {should} reject the connection).
9942 // --------------------------------------------------------------------------
9943 new_ctx = SSL_set_SSL_CTX(tls_fd, new_ctx); // Returns new_ctx or, if new_ctx is nullptr then it returns the original CTX
9944 r->tls_ctx(new_ctx); // If nullptr, then tls_ctx() will get OpenSSL to create a new SSL_CTX
9945
9946 } // -x- if len -x-
9947
9948 finish:
9949 // --------------------------------------------------------------------------
9950 // Free the ClientHello fields data, then return SSL_CLIENT_HELLO_SUCCESS.
9951 // Even if we encountered a problem with the ClientHello fields data, we
9952 // still return SSL_CLIENT_HELLO_SUCCESS so that the TLS context will be
9953 // accepted as valid.
9954 //
9955 // If we return SSL_CLIENT_HELLO_ERROR the connection will fail unnecessarily
9956 // for valid TLS certificates. If the ClientHello fields data is malformed
9957 // to a degree that doesn't satisfy OpenSSL, then OpenSSL will reject it, so
9958 // there's really no point in duplicating what OpenSSL already does properly,
9959 // which OpenSSL will pass through the standard error channels with normal
9960 // error details-and-diagnostics anyway.
9961 //
9962 // OpenSSL documentation says to do the following...
9963 // OPENSSL_free((char*)out);
9964 // ...but it's not necessary here because we'd be freeing a local variable,
9965 // which will fail and cause a crash.
9966 // --------------------------------------------------------------------------
9967 //std::cout << "out=" << randolf::rtools::to_hex(out[0], 8) << std::endl; // Debug
9968 //OPENSSL_free((unsigned char*)out);
9969
9970 return SSL_CLIENT_HELLO_SUCCESS; // 1
9971 } // -x- int tls_sni_callback -x-
9972
9973public:
9974 /*======================================================================*//**
9975 @brief
9976 Convert a 48-bit integer to a machine address in the form of @c
9977 xx:xx:xx:xx:xx:xx where every instance of @c xx is a hexadecimal
9978 representation of each respective 8-bit byte portion.
9979
9980 This method is needed because we don't want to bring in the heavy fmt::format
9981 class as a dependency.
9982 @returns Mac address as 17-character in the typical format expected by system
9983 administrators
9984 @see to_node
9985 @qualifier TLS
9986 *///=========================================================================
9987 static std::string to_mac(
9988 /// Pointer to 48-bit integer
9989 const void* addr) noexcept {
9990 std::string h;
9991 h.resize(18); // 48-bit mac address needs 6 hexadecimal pairs of nybbles delimited by colons plus a NULL terminator
9992 h.resize(snprintf(h.data(),
9993 h.size(),
9994 "%02x:%02x:%02x:%02x:%02x:%02x",
9995 ((u_char*)addr)[0],
9996 ((u_char*)addr)[1],
9997 ((u_char*)addr)[2],
9998 ((u_char*)addr)[3],
9999 ((u_char*)addr)[4],
10000 ((u_char*)addr)[5])); // Convert, and truncate NULL terminator
10001 return h;
10002 } // -x- std::string to_mac -x-
10003
10004 /*======================================================================*//**
10005 @brief
10006 Convert a 48-bit integer to a node address in the form of @c xxxx:xxxx:xxxx
10007 where every instance of @c xxxx is a hexadecimal representation of each
10008 respective 16-bit word portion.
10009
10010 This method is needed because we don't want to bring in the heavy fmt::format
10011 class as a dependency.
10012 @returns Node address as 14-character in the typical format expected by
10013 network administrators
10014 @see to_mac
10015 @qualifier TLS
10016 *///=========================================================================
10017 static std::string to_node(
10018 /// Pointer to 48-bit integer
10019 const void* addr) noexcept {
10020 std::string h;
10021 h.resize(15); // 48-bit node address needs 3 hexadecimal sets of words delimited by colons plus a NULL terminator
10022 h.resize(snprintf(h.data(),
10023 h.size(),
10024 "%04x:%04x:%04x",
10025 ((u_int16_t*)addr)[0],
10026 ((u_int16_t*)addr)[1],
10027 ((u_int16_t*)addr)[2])); // Convert, and truncate NULL terminator
10028 return h;
10029 } // -x- std::string to_node -x-
10030
10031 /*======================================================================*//**
10032 @brief
10033 Send a formatted string to the @ref rsocket endpoint.
10034
10035 The @c format is described in the documentation for the POSIX or Standard C
10036 Library @c printf() function.
10037 @throws randolf::rex::xEBADF The underlying socket is not open
10038 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
10039 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
10040 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
10041 @throws randolf::rex::xENOMEM Insufficient memory
10042 @returns The same rsocket object so as to facilitate stacking
10043 @see eol_fix_printf
10044 @see is_eol_fix_printf
10045 @see net_io
10046 @see printf
10047 @see printfline
10048 @see vprintfline
10049 @qualifier POSIX
10050 @qualifier TLS
10051 *///=========================================================================
10052 rsocket* vprintf(
10053 /// Format string to use
10054 const char* format,
10055 /// Variadic arguments in @c va_list format
10056 va_list args) {
10057 char* buf;
10058 int rc = ::vasprintf(&buf, format, args);
10059 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
10060 if (__eol_fix_printf && !__eol.empty()) { // We need to edit the string and then send it
10061 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
10062 ::free(buf);
10063 __send(str.data(), str.length());
10064 } else { // No need to edit the string, so just send it as is
10065 try {
10066 __send(buf, rc);
10067 ::free(buf);
10068 } catch (std::exception& e) { // Free buf then re-throw the exception
10069 ::free(buf); // Prevent memory leak when an exception is thrown
10070 throw;
10071 }
10072 } // -x- if __eol_fix_printf -x-
10073 return this;
10074 } // -x- rsocket* vprintf -x-
10075
10076 /*======================================================================*//**
10077 @brief
10078 Send a formatted string to the @ref rsocket endpoint, and append an EoL
10079 sequence.
10080
10081 The @c format is described in the documentation for the POSIX or Standard C
10082 Library @c printf() function.
10083 @throws randolf::rex::xEBADF The underlying socket is not open
10084 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
10085 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
10086 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
10087 @throws randolf::rex::xENOMEM Insufficient memory
10088 @returns The same rsocket object so as to facilitate stacking
10089 @see eol
10090 @see eol_fix_printf
10091 @see is_eol_fix_printf
10092 @see net_io
10093 @see printf
10094 @see printfline
10095 @see sendline
10096 @see vprintf
10097 @qualifier TLS
10098 *///=========================================================================
10099 rsocket* vprintfline(
10100 /// Format string to use
10101 const char* format,
10102 /// Variadic arguments in @c va_list format
10103 va_list args) {
10104 char* buf;
10105 int rc = ::vasprintf(&buf, format, args);
10106 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
10107 if (__eol_fix_printf && !__eol.empty()) {
10108 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
10109 .append(__eol);
10110 ::free(buf);
10111 __send(str.data(), str.length());
10112 } else {
10113 try {
10114 __sendline(buf, rc);
10115 ::free(buf);
10116 } catch (std::exception& e) { // Free buf then re-throw the exception
10117 ::free(buf); // Prevent memory leak when an exception is thrown
10118 throw;
10119 }
10120 } // -x- if __eol_fix_printf -x-
10121 return this;
10122 } // -x- rsocket* vprintfline -x-
10123
10124 private:
10125 /*======================================================================*//**
10126 Track unencrypted bytes received. When the number of bytes is negative or
10127 zero, it isn't recorded.
10128 This is an internal function.
10129 @returns same value provided in @ref n
10130 @qualifier TLS
10131 *///=========================================================================
10132 int __track_bytes_rx(
10133 /// Number of bytes transferred
10134 const int n) {
10135 if (n > 0) __bytes_rx.fetch_add(n, std::memory_order_relaxed);
10136 return n;
10137 } // -x- __track_bytes_rx -x-
10138
10139 /*======================================================================*//**
10140 Track unencrypted bytes transmitted. When the number of bytes is negative or
10141 zero, it isn't recorded.
10142 This is an internal function.
10143 @returns same value provided in @ref n
10144 @qualifier TLS
10145 *///=========================================================================
10146 int __track_bytes_tx(
10147 /// Number of bytes transferred
10148 const int n) {
10149 if (n > 0) __bytes_tx.fetch_add(n, std::memory_order_relaxed);
10150 return n;
10151 } // -x- __track_bytes_tx -x-
10152
10153 /*======================================================================*//**
10154 Track encrypted bytes received. When the number of bytes is negative or
10155 zero, it isn't recorded.
10156 This is an internal function.
10157 @returns same value provided in @ref n
10158 @qualifier TLS
10159 *///=========================================================================
10160 int __track_crypt_rx(
10161 /// Number of bytes transferred
10162 const int n) {
10163 if (n > 0) __crypt_rx.fetch_add(n, std::memory_order_relaxed);
10164 return n;
10165 } // -x- __track_crypt_rx -x-
10166
10167 /*======================================================================*//**
10168 Track encrypted bytes transmitted. When the number of bytes is negative or
10169 zero, it isn't recorded.
10170 This is an internal function.
10171 @returns same value provided in @ref n
10172 @qualifier TLS
10173 *///=========================================================================
10174 int __track_crypt_tx(
10175 /// Number of bytes transferred
10176 const int n) {
10177 if (n > 0) __crypt_tx.fetch_add(n, std::memory_order_relaxed);
10178 return n;
10179 } // -x- __track_crypt_tx -x-
10180
10181 /*======================================================================*//**
10182 Specialized private timeout-handling class that sets a new timeout on the
10183 specified socket, then relies on RAII to activate the destructor to restore
10184 the original timeout (which was acquired in the constructor).
10185 *///=========================================================================
10186 class RAII_timeout {
10187
10188 private:
10189 // --------------------------------------------------------------------------
10190 // Prepare timeout structures so that we can override it (if timeout != 0)
10191 // and also restore it to what it used to be once we're finished. We need to
10192 // set up the "timeout_target" variable and few timeval structures, backup
10193 // the current non-recvline timeout settings, and set the recvline timeout.
10194 // --------------------------------------------------------------------------
10195 randolf::rsocket* r; // TODO: Add support for debug() method
10196 struct timeval old_tv{0, 0}; // {tv_sec, tv_usec};
10197 struct timeval new_tv{0, 0}; // {tv_sec, tv_usec};
10198
10199 public:
10200 /*======================================================================*//**
10201 @brief
10202 Change the specified rsocket's @c timeout, after first saving the current
10203 timeout settings for later restoration.
10204
10205 If both @c sec or @c usec are set to 0, then this class will not make any
10206 changes to the underlying socket's timeout.
10207 *///=========================================================================
10208 RAII_timeout(
10209 /// The rsocket class to which to apply the timeout
10210 randolf::rsocket* r,
10211 /// Timeout in seconds
10212 const int sec,
10213 /// Timeout in microseconds
10214 const int usec = 0) {
10215 if (sec == 0 && usec == 0) return; // Do nothing if timeout is 0
10216 this->r = r;
10217 if (r->__debug) r->debug("RAII_timeout(socket{0x" + randolf::rtools::to_hex(r->__socket_fd) + "}"
10218 + ", " + std::to_string(sec)
10219 + ", " + std::to_string(usec)
10220 + ");");
10221 socklen_t otv{sizeof(old_tv)};
10222 r->__rc_check(::getsockopt(r->__socket_fd, SOL_SOCKET, SO_RCVTIMEO, &old_tv, &otv)); // getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO);
10223 new_tv.tv_sec = sec;
10224 new_tv.tv_usec = usec;
10225 r->__rc_check(::setsockopt(r->__socket_fd, SOL_SOCKET, SO_RCVTIMEO, &new_tv, sizeof(new_tv))); // setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
10226//std::cout << "Set timeout to " << sec << std::endl; // Debug
10227 } // -x- consturctor __temporary_timeout -x-
10228
10229 /*======================================================================*//**
10230 @brief
10231 Restore the underlying socket's original @c timeout setting.
10232 *///=========================================================================
10233 ~RAII_timeout() noexcept {
10234 if (new_tv.tv_sec == 0 && new_tv.tv_usec == 0) return; // Do nothing if timeout is 0
10235 if (r->__debug) r->debug("~RAII_timeout(socket{0x" + randolf::rtools::to_hex(r->__socket_fd) + "}"
10236 + ", " + std::to_string(old_tv.tv_sec)
10237 + ", " + std::to_string(old_tv.tv_usec)
10238 + ");");
10239 // This shouldn't fail, but if it does it won't matter since it can only mean that the socket is screwed
10240 ::setsockopt(r->__socket_fd, SOL_SOCKET, SO_RCVTIMEO, &new_tv, sizeof(new_tv)); // setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
10241//std::cout << "Restore timeout" << std::endl; // Debug
10242 } // -x- destructor __temporary_timeout -x-
10243
10244 }; // -x- class __RAII_timeout -x-
10245
10246 }; // -x- class rsocket -x-
10247
10248}; // -x- namespace randolf -x-
10249
10250// Save this for a future sendlines() methods.
10251// const void* msg_ptr = msg.data(); // Prevent repeated calls to data() method
10252// const int len = msg.length(); // Prevent repeated calls to length() method