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, "64-bit __time_t 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 EoS (End of Stream) is used instead of EoF (End of File) because sockets
127 normally represent streams rather than files, which communicates more clearly
128 the intention that using the @c eos() method involves the semantics that come
129 with reading a stream rather than a file.
130
131 EoL (End of Line) is typically used with the line-receiving methods, which
132 are configured and utilized primarily (and almost exclusively) by the @c eol,
133 @c recvline, and @c recv_rline methods.
134
135 The following custom qualifiers are incorporated into headings by Doxygen
136 alongside method titles throughout the documentation:
137
138 - @c POSIX denotes a method that is based on POSIX functions by the same
139 name and don't deviate significantly from the POSIX function arguments
140 (intended to be helpful to developers transitioning to/from rsocket or
141 working on source code that utilizes @ref rsocket and POSIX functions)
142
143 - @c TLS denotes that a method works properly with TLS-encrypted sockets
144 (most of the POSIX functions have been made to work properly with TLS,
145 but for the few rare cases of functions that can't be made to work with
146 TLS an effort has also been made to mention this using Doxygen's
147 "warning" sections in addition to omitting the TLS qualifier)
148
149 @par Getting started with a few simple examples
150
151 This is an example of connecting to an HTTP server, using the "GET" command
152 to request the home page (using HTTP/1.0), then receiving-and-displaying the
153 resulting web page's contents via STDOUT (or sending an error message to
154 STDERR). Finally, we exit with an EXIT_SUCCESS (or EXIT_FAILURE) code.
155
156 @code{.cpp}
157 #include <iostream> // std::cout, std::cerr, std::endl, etc.
158 #include <randolf/rex>
159 #include <randolf/rsocket>
160
161 int main(int argc, char *argv[]) {
162 try {
163 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
164 r.connect("www.example.com", 80);
165 r.sendline("GET / HTTP/1.0");
166 r.sendline("Host: www.example.com");
167 r.sendline("Connection: close");
168 r.send_eol();
169 do {
170 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
171 } while (!r.eos()); // -x- while data -x-
172 r.close();
173 } catch (const randolf::rex::xALL& e) {
174 std::cerr << "Socket exception: " << e.what() << std::endl;
175 return EXIT_FAILURE;
176 } catch (const std::exception& e) {
177 std::cerr << "Other exception: " << e.what() << std::endl;
178 return EXIT_FAILURE;
179 }
180 return EXIT_SUCCESS;
181 } // -x- int main -x-
182 @endcode
183
184 Parameter stacking is supported (with methods that return @c rsocket*); in
185 this example, notice that semicolons (";") are omittted and "r." is replaced
186 with "." (when compared with the above):
187
188 @code{.cpp}
189 #include <iostream> // std::cout, std::cerr, std::endl, etc.
190 #include <randolf/rex>
191 #include <randolf/rsocket>
192
193 int main(int argc, char *argv[]) {
194 try {
195 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
196 r.connect("www.example.com", 80)
197 .sendline("GET / HTTP/1.0")
198 .sendline("Host: www.example.com")
199 .sendline("Connection: close")
200 .send_eol();
201 do {
202 std::cout << r.recvline(0, MSG_WAITALL) << std::endl;
203 } while (!r.eos()); // -x- while data -x-
204 r.close();
205 } catch (const randolf::rex::xALL& e) {
206 std::cerr << "Socket exception: " << e.what() << std::endl;
207 return EXIT_FAILURE;
208 } catch (const std::exception& e) {
209 std::cerr << "Other exception: " << e.what() << std::endl;
210 return EXIT_FAILURE;
211 }
212 return EXIT_SUCCESS;
213 } // -x- int main -x-
214 @endcode
215
216 @par Features
217
218 This is meant to be a comprehensive socket class for C++, which is intended
219 to make socket I/O coding easier for developers with some key features:
220
221 - easy conversion from C-style POSIX sockets due to API consistency
222 - transparent TLS support (OpenSSL dependency)
223 - keys and certificate chains can also be loaded from memory
224 - SNI support (see the @ref rsocket_sni class for more information)
225 - underlying socket handle is accessible (via the @ref socket_fd() method)
226 - socket options are easier to get and to set
227 - sensible support for multipath protocol (see @ref mptcp)
228 - sensible support for future or unknown socket options
229 - errors are presented as ~100 separate exception classes: @ref rex::rex
230 - one parent exception class makes it easier to catch all socket errors
231 - a few exceptions groups are also provided to catch groups of errors
232 - new buffers are returned as std::shared_ptr's to eliminate memory leaks
233 - constructors with sensible defaults help to simplify coding
234 - documentation includes code samples (with @c \#include lines as needed)
235 - debug output with helpful output (and option to set output file handle)
236 - low-overhead is considered (this is why there's a bit more overloading)
237 - thread-safety is noted where it is absolutely available (if any caution
238 is warranted, it will also be noted)
239 - can send ASCIIZ strings (a.k.a., C-strings) without needing to specify
240 string length
241 - can send @c std::string (which tracks its own string length)
242 - each socket can optionally have a @ref name arbitrarily assigned (e.g.,
243 by an algorithm, or even populated with the connecting user's name, or
244 whatever purpose the development goals find it useful for)
245
246 Additional features that are not part of the typical POSIX standard, but
247 deserve special mention because they are needed so often:
248
249 - easy access to internal I/O counters (see @ref rsocket_io for details)
250 - your rsocket_io structure can be updated automatically by rsocket's
251 destructor after underlying socket is closed (see @c net_io_final()
252 method for details)
253 - printf(), printfline(), vprintf(), vprintfline() // Formatted strings
254 - with automatic EoL sequence substitution (and/or the addition of)
255 - recv_asciiz(), send_asciiz() // ASCIIZ string I/O operations
256 - recv_byte() , send_byte() // 8-bit byte operations (LSB/MSB is N/A)
257 - recv_struct(), send_struct() // Multi-byte operations
258 - recv_uint16(), send_uint16() // 16-bit operations with LSB/MSB variants
259 - recv_uint32(), send_uint32() // 32-bit operations with LSB/MSB variants
260 - recv_uint64(), send_uint64() // 64-bit operations with LSB/MSB variants
261 - recvline(), recv_rline(), sendline() // ASCII text line I/O operations
262 - class-wide configurable newline sequence (defaults to @e autodetect)
263 - @ref rsocket_group class for grouping rsockets (and automatic inclusion
264 by accept() and accept4() methods; this may be a different group from
265 whichever group the parent rsocket is in, if it's even in one)
266 - option to send data to all rsocket objects in the rsocket_group
267 - with support from the rsocket_mux class (for multiplexing operations)
268 - automatic naming policies (possibly like net_io() formatting style)
269
270 Some advanced features are planned that exceed what the basic socket I/O
271 functions provide, but are also needed:
272
273 - recv_uint128()/send_uint128() // 128-bit operations w/ LSB/MSB variants
274 - recv_uint(n)/send_uint(n) where "n" specifies the number of bits (which
275 must be a multiple of 8), with LSB/MSB variants
276 - auto-detection of inbound TLS connection (this is turned off by default)
277 - This is not the same as STARTTLS (an application-level command, for
278 which the @ref tls_do_handshake() method will likely be used)
279 - simple timing tracking options using timing_start() and timing_stop()
280 methods, the results of which can be retrieved with timing_get() or a
281 similarly-named group of methods
282
283 Other features that are not a high priority:
284
285 - internal support for portability to Microsoft Windows, which is a major
286 undertaking that I know will be time-consuming since Windows Sockets
287 exhibit some nuanced behaviours and are not consistent with POSIX
288 sockets APIs that are used by Linux, UNIX, MacOS, and pretty much all
289 other Operating Systems. Because of this, MS-Windows portability just
290 isn't a high priority for me (without sufficient demand and sufficient
291 funding so I can commit my time without missing mortgage payments,
292 student loan payments {for my kids}, various living expenses, etc.).
293
294 @par Requirements
295 The @c __time_t structure must be 64-bit (or larger). This becomes espcially
296 important for the @ref recvline method, in addition to Year 2038 compliance.
297
298 @par Notes
299
300 I use the term "ASCIIZ string" to indicate an array of characters that's
301 terminated by a 0 (a.k.a., null). Although this is very much the same as a
302 C-string, the difference is that in many API functions a C-string must often
303 be accompanied by its length value. When referring to an ASCIIZ string, I'm
304 intentionally indicating that the length of the string is not needed because
305 the string is null-terminated. (This term was also commonly used in assembly
306 language programming in the 1970s, 1980s, and 1990s, and as far as I know is
307 still used by machine language programmers today.)
308
309 UTF-8 works without any problems as it is backward-compatible to 8-bit ASCII,
310 and because @c std::string uses 8-bit bytes to store strings internally. Do
311 keep in mind that the manipulation of UTF-8 substrings will require working
312 with UTF-8 codepoints that may consume one or more bytes, which is beyond the
313 scope of the impartial (to UTF-8 and ASCII) functionality that this rsocket
314 class provides.
315
316 UTF-8 newline codepoints NEL (c2 85), LS (e2 80 a8), and PS (e2 80 a9) garner
317 no special handling at this time - unlike CR/LF (0d/0a) - and are merely
318 treated as non-newline codepoints. There is a possibility of adding support
319 for this in the future, but additional research and planning is required to
320 make sure this works properly. What is most likely is that some UTF-8 flags
321 will be added to support each of these (which will probably be disabled by
322 default) that will be integrated into the readline() methods. This also
323 depends on how widely used these particular codepoints are, and pending
324 further research to determine whether these really are supposed to be used
325 functionally as newlines...
326
327 So far, there are two UTF-8 codepoints that absolutely are not functional,
328 yet which a small number of people have mistakenly assumed are:
329 - <sup>C</sup><sub>R</sub> = superscript "C" with subscript "R" (e2 90 9d)
330 - <sup>N</sup><sub>L</sub> = superscript "N" with subscript "L" (e2 90 a4)
331
332 The special characters above are intended to represent the Carriage-Return
333 and New-Line respectively in documentation such as ASCII character reference
334 charts, which were used mostly by IBM in the 1970s and 1980s for instruction
335 manuals (and in other documentation), and also on a few keyboard overlays.
336
337 @par Background
338
339 I created this class to make it easier to write internet server daemons. I
340 started out using C-style socket functions (because C++ doesn't come with a
341 socket class), but I found that I didn't enjoy mixing something as important
342 and detailed as socket I/O in a procedural way into the object-oriented
343 paradigm that C++ provides.
344
345 After looking for existing solutions (none of which served as comprehensive
346 replacements for socket I/O), I embarked on creating the rsocket class, and
347 then I began to understand why this probably hadn't been done -- it's a
348 massive undertaking, primarily because there are a lot of functions that are
349 needed to handle socket I/O. Further, [at the time of this writing] the @c
350 sockaddr_storage structure wasn't as widely used as it should be, and so
351 information about it tended to be scarce, incomplete, or incorrect (further
352 research, and diving down into some pretty deep "rabbit holes," was required
353 to understand this properly, which was worthwhile because it resulted in
354 having transparent support for IPv4 and IPv6 without breaking backward
355 compatibility for code expecting specific structures).
356
357 Moving error codes into exceptions is also a major effort because they are
358 diverse and plentiful, and there are so many errors that can occur at various
359 stages for many different reasons. There are also a few outlier functions
360 that require slightly different approaches to error handling due to subtly
361 different rules for handling their errors, and so the exception-generation
362 wasn't as straight-forward as one might optimistically expect, but this is
363 one of the many benefits of the object-oriented programming pardigm because
364 handling edge cases internally results in a consistent error-handling
365 interface using exceptions that also simplifies the source code. (Need to
366 handle a specific set of conditions? Catch the relevant exceptions for those
367 cases in an inner set of exceptions, and just catch all the others in a more
368 general way without the added complexity of repeatedly checking for errors
369 every step along the way.)
370
371 So, I dedicated time to make this work, and with the intention of making it
372 an open source project once I got it into a state that's ready for the
373 general public. This required putting my other C++ projects on hold, which
374 was fine because they didn't have strict deadlines and using this socket
375 class in them will speed up development in the long-term anyway, so it's
376 clearly worth the effort to me ... and I sincerely hope that my efforts will
377 be helpful to others too.
378
379 My background in programming began when I was a young child, teaching myself
380 BASIC and then machine language (when I found BASIC to be too limited) before
381 moving on to other languages like Perl and Java many years later. Eventually
382 I circled around to C (which I chose to learn the hard way by writing some
383 PostgreSQL extensions) and then C++ a few years after that. I have a lot of
384 experience with socket communications, including fully-featured DNS resolver
385 library code in machine language for Novell's NetWare that used C calling
386 conventions and supported varargs, which worked well for the few developers
387 who needed or wanted it.
388
389 @par History
390 - 2022-Nov-09 v1.00 Initial version
391 - 2022-Nov-26 v1.00 Moved exception handling to a separate class
392 - 2022-Nov-28 v1.00 Completed readline/send functionality
393 - 2022-Dec-03 v1.00 Added endianness transparency
394 - 2022-Dec-04 v1.00 Added printf() support
395 - 2022-Dec-24 v1.00 Added socket MUXing
396 - 2023-Feb-22 v1.00 Added TLS/SSL support
397 - 2023-Mar-10 v1.00 Added TLS-SNI support
398 - 2023-Apr-19 v1.00 Added TLS certificate loading from memory
399 - 2023-May-24 v1.00 Added support for clang++ compilation
400 - 2023-Jun-09 v1.00 Improvements to dynamic internal read buffering
401 - 2023-Oct-31 v1.00 Improvements to various classes
402 - 2024-Feb-21 v1.00 Added is_buffered() method
403 - 2024-Mar-31 v1.00 Completed @ref recvline (implemented a work-around for
404 a subtle SSL_peek failure to extract additional data
405 when a user at an end-point is communicating with
406 "icanon" mode enabled)
407 - 2024-May-24 v1.00 Changed source code to accomodate @c clang++ compiler
408 - 2024-Jun-04 v1.00 Added @c POSIX and @c TLS qualifiers to all applicable
409 methods (Doxygen incorporates into the documentation)
410 - 2024-Oct-23 v1.00 Various minor improvements to the documentation since
411 the previous update
412 - 2024-Nov-05 v1.00 Added eol_consumed_seq() method
413 - 2024-Nov-10 v1.00 Added discard() method
414 - 2024-Nov-17 v1.00 Added recv_rline() method
415 - 2024-Nov-19 v1.00 Added recv_as_string() method
416 - 2024-Nov-22 v1.00 Added discard_line() method
417 - 2024-Nov-28 v1.00 Switched internal ring buffer to a proper ring buffer
418 that's now available in the rring class (which was made
419 initially for this purpose)
420 - 2024-Dec-08 v1.00 Improved shutdown() method to handle SSL shutdown fully
421 and added a parameter to prevent calling SSL_shutdown()
422 - 2024-Dec-10 v1.00 Changed eos() method to use faster ioctl POSIX function
423 and OpenSSL's SSL_has_pending function (with TLS)
424 - 2024-Dec-10 v1.00 Added RECVLINE_FLAGS enum to expand the functionality
425 of how the recvline() and recv_rline methods deal with
426 data in specific scenarios, which helps to satisify the
427 needs of specific advanced data processing scenarios
428 - 2024-Dec-23 v1.00 Updated calls to methods that were renamed today in the
429 @ref rring class
430 - 2024-Dec-28 v1.00 Improvements to internal handling of SNI in all four
431 accept() methods along with the SNI callback function,
432 and added @ref is_tls_sni_has_name() method; <s>also
433 added the @c TLS_SNI_PROMISCUOUS flag</s>
434 - 2024-Dec-30 v1.00 Added the setsockopt() method without a value (and uses
435 a zero-length value internally)
436 - 2024-Dec-31 v1.00 Removed SSL_BIO handle generation calls along with all
437 references to it because we don't need it since we're
438 already handling our own buffering directly and working
439 with OpenSSL's raw API functions directly (the extra
440 layer of abstraction through BIO therefore doesn't
441 benefit what we're doing, and also adds slight overhead
442 that will become noticeable on extremely busy systems)
443 - 2025-Jan-05 v1.00 Added send_rline() method
444 - 2025-Jan-07 v1.00 Made major improvements to the @c recv(), @c recvline()
445 and @c recv_rilne methods, and added an internal class
446 that utilizes RAII to temporarily change the timeout
447 (for the @c recvline and @c recv_rline methods), then
448 finally restores the previous timeout, and added debug
449 output support accordingly; also made major
450 improvements to all recv() and related methods in how
451 they interact with the internal ring buffers (the code
452 is more efficient and a lot simpler)
453 - 2025-Jan-08 v1.00 Updated the @ref timeout methods by adding @c direction
454 (@c SO_RCVTIMEO {default} and @c SO_SNDTIMEO options),
455 and changed @c seconds from type @c int to type @c long
456 - 2025-Jan-18 v1.00 Improved internal memory handling, optimized discard()
457 and discard() line method loops, and simplified the
458 family() method, on top of various minor improvements
459 throughout this entire class during the past week
460 - 2025-Jan-23 v1.00 Performance optimizations
461 - 2025-Jan-30 v1.00 Added support for IPPROTO_MPTCP (whether to continue to
462 use IPPROTO_MPTCP instead of IPPROTO_TCP for the "ai"
463 family of POSIX functions that don't support MPTCP on
464 the majority of Operating Systems at this time; this
465 default may change in the future as MPTCP support from
466 this aspect is expected to improve over time)
467 - 2025-Feb-03 v1.00 Increased use of references and pointers; and renamed
468 two of the timeout() and timeout_recvline() methods to
469 get_timeout() and get_timeout_recvline(), respectively,
470 and a variety of other methods to @c get_ prefixed
471 methods to allow for greater flexibility in setting
472 default values in the API; also added @c shutdown()
473 call tracking so the desstructor can take care of this
474 step to prevent suble resource leaks if an open rsocket
475 object is deleted prematurely
476 - 2025-Feb-04 v1.00 Improvements to documentation (various)
477 - 2025-Feb-05 v1.00 Added @c accept_up() and @c accept4_up() methods that
478 return new rsockets in an @c std::unique_ptr object;
479 added direct support for @c ip_mreqn structure for
480 getsockopt_ip_mreqn and setsockopt methods (along with
481 the accompanying @c debug method to provide specialized
482 output for these two methods)
483 - 2025-Feb-09 v1.00 Added @ref recv_append_to() methods; and changed most
484 methods that return data wrapped in @c std::shared_ptr
485 to return data wrapped in @c std::unique_ptr instead
486 because this has a lot less overhead, and converting
487 from unique_ptr to shared_ptr is trivial, but the other
488 way around is not directly supported
489 - 2025-Feb-10 v1.00 Added the @ref recv_rline_append_to() method and the
490 @ref recvline_append_to() method
491 - 2025-Feb-11 v1.00 Improved both robustness and efficiency of the internal
492 __recvline method that all recvline() methods depend on
493 - 2025-Feb-12 v1.00 Further optimizing of the internal __recvline() method;
494 and added the @c RECVLINE_LIMIT_DISCARD flag option to
495 provide greater control over line-reading behaviour in
496 the event of a data overflow
497 - 2025-Feb-16 v1.00 Added and implemented the @ref TLS_NO_READ_AHEAD flag
498 - 2025-Feb-17 v1.00 Updated @ref net_io and related methods to include
499 support for the @c spare_rx and @c spare_tx statistics
500 @version 1.00
501 @author Randolf Richardson
502 *///=========================================================================
503 class rsocket {
504
505 // --------------------------------------------------------------------------
506 // The rsocket_group class needs access to some of our internal variables.
507 // --------------------------------------------------------------------------
508 friend class rsocket_group; // Grant the rsocket_group class access to our internals
509
510 // --------------------------------------------------------------------------
511 // Socket variables.
512 // --------------------------------------------------------------------------
513 int __socket_fd = 0; // Raw socket handle
514 int __socket_type = 0; // Socket type (e.g., SOCK_STREAM)
515 int __socket_protocol = 0; // Socket protocol (e.g., AF_INET)
516 struct sockaddr_storage* __socket_addr = (struct sockaddr_storage*)::malloc(sizeof(sockaddr_storage)); // Initialize to all elements to their default values
517 socklen_t __socket_addr_size = sizeof(sockaddr_storage); // We need to point to this (and it might be modified)
518 int __socket_backlog = SOMAXCONN; // Default backlog is 4096 on Linux, and 128 on older systems
519
520 // --------------------------------------------------------------------------
521 // Socket flags (internal, but with get-methods to provide read-only access).
522 // --------------------------------------------------------------------------
523 std::atomic_bool __socket_open = false; // Socket is open by way of socket() function
524 std::atomic_bool __socket_connected = false; // Socket is connected
525 bool __socket_mptcp_flag = true; // Whether IPPROTO_MPTCP will be substituted with IPPROTO_TCP with certain functions that don't support MPTCP
526 bool __socket_got_shutdown = false; // Whether shutdown() was called (also used by destructor to ensure proper shutdown on sockets that weren't closed)
527
528 // --------------------------------------------------------------------------
529 // TLS flags and variables.
530 // --------------------------------------------------------------------------
531 std::atomic_bool __tls = false; // Indicates whether socket I/O is encrypted with OpenSSL
532 SSL_CTX* __tls_ctx = nullptr; // TLS context (nullptr == no encryption initialized)
533 rsocket_sni* __tls_sni = nullptr; // SNI maps
534 bool __tls_sni_has_name = false; // Set to TRUE only if SNI received a hostname
535 bool __tls_sni_match = false; // Set to TRUE only if SNI matched in the SNI callback
536 SSL* __tls_fd = nullptr; // TLS connection structure (nullptr == no connection)
537 bool __tls_exclusive = false; // See enum TLS_FLAGS::TLS_EXCLUSIVE
538 bool __tls_egress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_EGRESS
539 bool __tls_ingress = true; // TRUE = permitted / see enum TLS_FLAGS::TLS_NO_INGRESS
540 bool __tls_server_mode = true; // See enum TLS_FLAGS::TLS_SERVER
541 bool __tls_no_read_ahead = false; // See enum TLS_FLAGS::TLS_NO_READ_AHEAD
542 inline static std::atomic_int __fn_counter = 0; // Used in generating unique-and-threadsafe temporary filenames
543 const rsocket* __rtemplate = nullptr; // Weak pointer to rsocket that was a template for this rsocket
544
545 // --------------------------------------------------------------------------
546 // Keep track of test for whether this host stores integers using big endian.
547 // --------------------------------------------------------------------------
548 inline static const bool __endian_is_msb = htons(42) == 42; // TRUE==MSB / FALSE==LSB
549
550 // --------------------------------------------------------------------------
551 // General variables. Atomic variables are used for variables as needed to
552 // support operations in a thread-safe manner.
553 // --------------------------------------------------------------------------
554 std::string __name; // Arbitrary name of this rsocket (used by some applications)
555 std::string __name_sni; // SNI hostname (only used when tls_sni is configured)
556 std::atomic_bool __debug = false;
557 std::atomic<std::FILE*> __debug_fd = stderr;
558 std::string __debug_prefix = "rsocket-debug";
559
560 // --------------------------------------------------------------------------
561 // Dynamic buffer expansion for extra-long lines resolves problems with the
562 // default of 4,096 bytes for the read-ahead policy in Linux sockets, so some
563 // automatic expansion is handled by the randolf::rring class (which was
564 // originally created to simplify most of coding for the ring buffer in this
565 // randolf::rsocket class).
566 // --------------------------------------------------------------------------
567 #define RSOCKET_BUFFER_SIZE 8192 // Hard-coded default buffer size
568 size_t __buffer_size = RSOCKET_BUFFER_SIZE; // Buffer size used by recv() when a buffer size isn't provided
569 rring* __buffer = nullptr; // Ring buffer; instantiated by calling recvline() or recv_rline()
570 rring_bam* __buffer_bam = nullptr; // Map of rring's memory blocks
571
572 // --------------------------------------------------------------------------
573 // EoL sequence variables.
574 // --------------------------------------------------------------------------
575 inline static const char __CR = (char)13; // CTRL-M / ASCII 13 (0Dh) / "\r" / Used by eol() methods
576 inline static const char __LF = (char)10; // CTRL-J / ASCII 10 (0Ah) / "\n" / Used by eol() methods
577 static constexpr const char* __CRLF = "\x0d\x0a"; // Used by eol() methods
578 std::string __eol; // Used by recvline() method; maintained by eol() methods
579 std::string __eol_consumed_seq; // The EoL sequence that was successfully received by the most recent use of the recvline() method
580 bool __eol_adoption = true; // Whether to adopt the dynamically detected EoL sequence when __eol is empty (the default)
581 std::string __eol_out = std::string(__CRLF); // Used by sendline() method; maintained by eol() methods; also used by sendline() method
582 bool __eol_fix_printf = true; // Whether to replace "\n" sequence in printf() methods to EoL sequence
583
584 // --------------------------------------------------------------------------
585 // Fine-tuning for __recvline(). This is used with nanosleep().
586 //
587 // To test, also use commands in canonical mode, like this:
588 // stty -icanon && openssl s_client host:port
589 // stty -icanon && netcat host port
590 // stty -icanon && telnet host port
591 // --------------------------------------------------------------------------
592 long __recvline_timeout = 0; // Number of seconds (0 = unlimited; maximum for "long" equates to approximately 292 billion years since the UNIX/Linux epoch)
593
594 // --------------------------------------------------------------------------
595 // Statistical variables.
596 // --------------------------------------------------------------------------
597 rsocket_io* __io_final_addr = nullptr; // Used by ~rsocket() destructor, net_io_final(), and net_io_update()
598 std::atomic_ulong __bytes_rx = 0; // Total number of bytes received
599 std::atomic_ulong __bytes_tx = 0; // Total number of bytes transmitted
600 std::atomic_ulong __crypt_rx = 0; // Total number of encrypted bytes recevied
601 std::atomic_ulong __crypt_tx = 0; // Total number of encrypted bytes transmitted
602
603 public:
604 /*======================================================================*//**
605 @brief
606 Optional flags used with rsocket's @ref recvline() and @ref recv_rline()
607 methods to specify relevant text-line reading policies/semantics, most of
608 which are primarily aimed at adding flexibility to ease the implementation of
609 text-based line-oriented protocols.
610
611 These flags provide access to advanced behaviours that most applications and
612 libraries probably won't need since most line-reading is generally pretty
613 straight-forward.
614
615 Both the @ref recvline and @ref recv_rline methods support all of these flags
616 so as to yield the same expected results, which also makes it easier for
617 developers to switch between these two ways of receiving lines of text from
618 endpoints that are automated in a script-like fashion or individuals using a
619 keyboard to send lines and/or keystrokes with more natural timing.
620 @see recvline
621 @see recv_rline
622 *///=========================================================================
623 enum RECVLINE_FLAGS: int {
624
625 /*----------------------------------------------------------------------*//**
626 The RECVLINE_DEFAULT flag isn't necessary, but it's included here for
627 completeness as it accomodates programming styles that prefer to emphasize
628 when defaults are being relied upon.
629 *///-------------------------------------------------------------------------
630 RECVLINE_DEFAULT = 0,
631
632 /*----------------------------------------------------------------------*//**
633 The RECVLINE_NO_DISCARD_ON_OVERFLOW flag prevents data from being discarded
634 when an @ref randolf::rex::xEOVERFLOW exception is thrown (which is caused
635 when a line of text exceeds the specified maximum line length).
636
637 @note
638 The @c MSG_PEEK flag also prevents the automatic discarding of data,
639 regardless of whether the @ref randolf::rex::xEOVERFLOW is thrown, thus it
640 differs from this flag in multiple ways that vary with how and why it's used.
641
642 @warning
643 Data will need to be consumed in a different way, such as by attempting to
644 read into a larger buffer than what had been allocated, or by receiving
645 portions or discarding some or all that remains.
646
647 By default, when the @ref randolf::rex::xEOVERFLOW exception is thrown, all
648 pending data (up to and including the upcoming EoL sequence, or the EoS,
649 whichever comes first) is discarded.
650
651 @note
652 Data is never discarded in the event of a timeout.
653 @see discard_line()
654 @see RECVLINE_LIMIT_DISCARD
655 *///-------------------------------------------------------------------------
656 RECVLINE_NO_DISCARD_ON_OVERFLOW = 1,
657
658 /*----------------------------------------------------------------------*//**
659 The RECVLINE_PARTIAL flag receives an incomplete line of text and returns it
660 instead of throwing the @ref randolf::rex::xEOVERFLOW exception, and causes
661 the @ref eol_consumed_seq() method to return an empty string (which indicates
662 that no EoL sequence was consumed).
663 @see eol_consumed_seq()
664 *///-------------------------------------------------------------------------
665 RECVLINE_PARTIAL = 2,
666
667 /*----------------------------------------------------------------------*//**
668 The RECVLINE_LIMIT_DISCARD flag instructs @ref recvline(), @ref recv_rline(),
669 and other line-receiving methods to not exceed the maximum length specified
670 if an EoL sequence was not detected when discarding data.
671
672 By default, when an EoL sequence is not detected the final discard will
673 consume all the data beyond the maximum length until either EoS (End of
674 Stream) is encountered, or when an EoL sequence is detected (before EoS).
675
676 @note
677 Data is never discarded in the event of a timeout.
678
679 This flag is particularly useful for implementing mechanisms like the BDAT
680 command in the SMTP protocol, which specifies the maximum number of bytes (or
681 characters) that can be received in via each BDAT CHUNK -- discarding data
682 beyond a BDAT CHUNK would consume the next SMTP command (which is most likely
683 to be the next BDAT command).
684 @see discard_line()
685 @see RECVLINE_NO_DISCARD_ON_OVERFLOW
686 *///-------------------------------------------------------------------------
687 RECVLINE_LIMIT_DISCARD = 4,
688
689 }; // -x- enum RECVLINE_FLAGS -x-
690
691 /*======================================================================*//**
692 @brief
693 Optional flags used with various methods to determine whether they will throw
694 an @ref randolf::rex::xETIMEDOUT exception or merely return a @c nullptr, an
695 empty set, or a 0 (zero) when a timeout duration elapses.
696
697 If you're not sure of whether to use @ref TIMEOUT_EMPTY, @ref TIMEOUT_NULL,
698 or @ref TIMEOUT_ZERO in your code, then I suggest using @ref TIMEOUT_NULL
699 since @c NULL will likely be the most recognizable in most code reviews.
700
701 @note
702 You'll know when this is an option because the method will support this.
703 *///=========================================================================
704 enum TIMEOUT_BEHAVIOUR: bool {
705
706 /*----------------------------------------------------------------------*//**
707 Indicate that an @ref randolf::rex::xETIMEDOUT exception should be thrown
708 when the timeout duration elapses.
709 *///-------------------------------------------------------------------------
710 TIMEOUT_EXCEPTION = true,
711
712 /*----------------------------------------------------------------------*//**
713 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
714 when the timeout duration elapses.
715 *///-------------------------------------------------------------------------
716 TIMEOUT_EMPTY = false,
717
718 /*----------------------------------------------------------------------*//**
719 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
720 when the timeout duration elapses.
721 *///-------------------------------------------------------------------------
722 TIMEOUT_NULL = false,
723
724 /*----------------------------------------------------------------------*//**
725 Indicate that a @c nullptr, an empty set, or a 0 (zero) should be returned
726 when the timeout duration elapses.
727 *///-------------------------------------------------------------------------
728 TIMEOUT_ZERO = false,
729
730 }; // -x- enum TIMEOUT_BEHAVIOUR -x-
731
732 /*======================================================================*//**
733 @brief
734 Optional flags used with rsocket's @ref tls() and @ref tls_ctx() methods to
735 specify relevant policies/semantics.
736 *///=========================================================================
737 enum TLS_FLAGS: int {
738
739 /*----------------------------------------------------------------------*//**
740 The TLS_DEFAULT flag isn't necessary, but it's included here for completeness
741 as it accomodates programming styles that prefer to emphasize when defaults
742 are being relied upon.
743 *///-------------------------------------------------------------------------
744 TLS_DEFAULT = 0,
745
746 /*----------------------------------------------------------------------*//**
747 Only encrypted connections are permitted, initially, and all attempts to
748 begin with unencrypted connections will consistently fail.
749
750 Encrypted connections must begin with a cryptographic handshake packet, or
751 else the connection will be rejected as due to being reasonably assumed to be
752 "not encrypted."
753 @post
754 If this flag isn't set, an application-level mechanism can be used to upgrade
755 to TLS encryption (this is common with connections that begin unencrypted,
756 and issue a proprietary command {such as the @c STARTTLS command in SMTP} to
757 upgrade to TLS later on {of which the @ref tls() method is used to affect an
758 ingress; see the @ref TLS_NO_INGRESS flag for additional information}).
759 @note
760 Creating an exclusively @c unencrypted connection is accomplished by not
761 initializing TLS.
762 @see tls()
763 @see TLS_NO_EGRESS to prevent support for application-level downgrades to
764 unencrypted connections
765 *///-------------------------------------------------------------------------
766 TLS_EXCLUSIVE = 1,
767
768 /*----------------------------------------------------------------------*//**
769 Mid-stream upgrades to encrypted connections are not permitted (e.g., via
770 application-level initiations like the @c STARTTLS command as seen in SMTP),
771 which will also cause calls to the @ref tls() method to fail fast after plain
772 non-encrypted data has already been sent or received.
773 @pre
774 This flag is not necessary when the @ref TLS_EXCLUSIVE flag is set.
775 @see is_tls_ingress_okay()
776 @see tls_do_handshake()
777 @see TLS_NO_EGRESS
778 *///-------------------------------------------------------------------------
779 TLS_NO_INGRESS = 2,
780
781 /*----------------------------------------------------------------------*//**
782 Mid-stream downgrades to unencrypted connections are not permitted (e.g., via
783 application-level initiations like a hypothetical @c STOPTLS command as seen
784 in FTPS), which will also cause calls to the @ref tls() method to fail fast
785 after encrypted data has already been sent or received.
786 @pre
787 This flag may be combined with the @ref TLS_EXCLUSIVE flag to prevent a
788 connection from being downgraded programatically.
789 @note
790 Although egress to an unencrypted connection doesn't occur automatically
791 (since egress can only be affected programatically to support commands at the
792 application level), this flag is useful to prevent third-party code from
793 downgrading an encrypted @ref rsocket to unencrypted.
794 @warning
795 Supporting unencrypted communications is strongly discouraged over public
796 networks (e.g., the internet) because unencrypted streams are trivially
797 susceptible to man-in-the-middle attacks that can alter the contents of the
798 data in both directions (which is a particularly dangerous prospect for
799 sending/receiving sensitive information).
800 @see is_tls_egress_okay()
801 @see TLS_NO_INGRESS
802 *///-------------------------------------------------------------------------
803 TLS_NO_EGRESS = 4,
804
805 /*----------------------------------------------------------------------*//**
806 This is a convenience flag that provides an option for developers to be more
807 clear in their use of the @ref tls() and @ref tls_ctx() methods to indicate
808 intent to rely on what is already the default.
809 @see tls_do_handshake()
810 @see TLS_SERVER
811 *///-------------------------------------------------------------------------
812 TLS_CLIENT = 0,
813
814 /*----------------------------------------------------------------------*//**
815 Indicates that this rsocket will be for a server daemon, and to initialize a
816 new TLS context (when one isn't being provided) using OpenSSL's
817 @c TLS_server_method() function instead of OpenSSL's @c TLS_client_method()
818 function (the latter is the default because most code is anticipated to be
819 client-oriented).
820
821 @attention
822 Setting the CLIENT/SERVER flag incorrectly will typically cause the following
823 error that's difficult to track down, which is usually triggered by calling
824 @ref accept, @ref accept4(), or @ref connect(), and so I hope that including
825 this error message here in this documentation will be helpful:
826 @verbatim
827 error:140C5042:SSL routines:ssl_undefined_function:called a function you should not call
828 @endverbatim
829
830 The absence of this flag has the same effect as specifying the @ref
831 TLS_CLIENT flag.
832 @see TLS_CLIENT
833 *///-------------------------------------------------------------------------
834 TLS_SERVER = 8,
835
836 /*----------------------------------------------------------------------*//**
837 Skip enabling read-ahead mode when setting up the context in @ref tls_ctx()
838 and related methods. Methods that don't configure read-ahead mode using the
839 @c SSL_CTX_set_read_ahead() or @c SSL_set_read_ahead() methods will ignore
840 this flag if it's present. (The default is to enable read-ahead mode, which
841 is particularly helpful with the @ref recvline and @ref recv_rline methods.)
842 @pre
843 This flag is provided for scenarios where read-head mode should not be
844 enabled.
845 @see tls_ctx()
846 *///-------------------------------------------------------------------------
847 TLS_NO_READ_AHEAD = 16,
848
849 }; // -x- enum TLS_FLAGS -x-
850
851 private:
852 /*======================================================================*//**
853 Return Code check, and throws an rsocket-specific exception if an error
854 occurred, which is indicated by @c -1 (a lot of example code shows @c < @c 1
855 comparisons, but we specifically test for @c -1 because the documentation
856 clearly states @c -1. This is important because a system that can support an
857 extremely high number of socket handles might, in theory, assign handles with
858 values that get interpreted as negative integers, and so less-than-zero tests
859 would result in dropped packets or dropped sockets (any such socket code that
860 allocates such handles obviously must not ever allocate @c -1 since this
861 would definitely be misinterpreted as an error).
862
863 If rc is not @c -1, then it is simply returned as is.
864
865 If n is nonzero and rc is 0, then n will override errno. (This option is
866 provided to accomodate the few socket library functions that return values
867 that are never errors, and expect the developer to rely on other means of
868 detecting whether an error occurred. This is an example of where Object
869 Oriented Programming is helpful in making things better.)
870 @returns Original value of @c rc (if no exceptions were thrown)
871 *///=========================================================================
872 const int __rc_check(
873 /// Return code
874 const int rc,
875 /// Override @c errno (if not 0)
876 const int n = 0,
877 /// Exception handling flags (see @ref rex::REX_FLAGS for details)
878 const int flags = rex::rex::REX_FLAGS::REX_DEFAULT) {
879 if (rc == -1 || (rc == 0 && n != 0)) {
880 const int socket_errno = n == 0 ? errno : n; // If "n" is not zero, use it instead
881 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
882 randolf::rex::mk_exception("", socket_errno, flags); // This function doesn't return (this code ends here)
883 } // -x- if rc -x-
884 return rc;
885 } // -x- int __rc_check -x-
886
887 /*======================================================================*//**
888 Similar to @ref __rc_check(), but specialized for handling OpenSSL return
889 codes.
890 *///=========================================================================
891 const int __rc_check_tls(
892 /// Return code (from OpenSSL's API functions)
893 const int rc) {
894 if (rc <= 0) {
895 if (__debug) debug("__rc_check_tls(" + std::to_string(rc) + ") throwing exception"); // TODO: Remove this
896 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)
897 } // -x- if rc -x-
898 return rc;
899 } // -x- int __rc_check -x-
900
901 /*======================================================================*//**
902 Internal function that opens the socket. This is used by the constructors
903 and their accompanying socket() methods.
904
905 Throws randolf::rex::xEALREADY exception if the socket is already open to
906 prevent multiple calls to @c open, which can be particularly problematic if
907 any of the additional function calls specify different parameters -- this
908 helps developer(s) to avoid unexpected results if they're inadvertently using
909 the same rsocket object when they intend to use different rsocket objects.
910 *///=========================================================================
911 void __socket(
912 /// Communication domain; usually one of:@n
913 /// AF_INET (IPv4)@n
914 /// AF_INET6 (IPv6)@n
915 /// AF_UNIX (UNIX domain sockets)
916 const int family,
917 /// Communication semantics; usually one of:@n
918 /// SOCK_STREAM (common for TCP)@n
919 /// SOCK_DGRAM (common for UDP)
920 const int type,
921 /// Network protocol; usually one of:@n
922 /// IPPROTO_TCP / IPPROTO_MPTCP@n
923 /// IPPROTO_UDP@n
924 /// IPPROTO_IP
925 const int protocol) {
926 if (__debug) debug("socket(" + std::to_string(family)
927 + ", " + std::to_string(type)
928 + ", " + std::to_string(protocol)
929 + ");");
930
931 // --------------------------------------------------------------------------
932 // Syntax checks.
933 // --------------------------------------------------------------------------
934 if (__socket_open) throw randolf::rex::xEALREADY("EALREADY: Socket is already open"); // Socket has already been opened
935 if (family == AF_UNSPEC) throw randolf::rex::xEAFNOSUPPORT("EAFNOSUPPORT: AF_UNSPEC family (SO_DOMAIN) is not supported by socket()");
936
937 // --------------------------------------------------------------------------
938 // Build minimum parts of __socket_addr structure.
939 // --------------------------------------------------------------------------
940 __socket_addr->ss_family = family;
941 __socket_type = type;
942 __socket_protocol = protocol;
943
944 // --------------------------------------------------------------------------
945 // Create new socket handle, and save it, then set internal variable to
946 // indicate that the socket is open.
947 // --------------------------------------------------------------------------
948 __socket_fd = __rc_check(::socket(__socket_addr->ss_family, __socket_type, __socket_protocol));
949 __socket_open = true;
950
951 } // -x- void __socket -x-
952
953 public:
954 /*======================================================================*//**
955 @brief
956 Instantiate an empty rsocket without actually opening a socket, and therefore
957 also without throwing any exceptions (useful in header-file definitions).
958
959 @details
960 Instantiating an empty rsocket is particularly useful for header-file
961 definitions since exceptions can't be handled outside of subroutines, and
962 it's also useful for enabling debug() mode @em before setting the socket's
963 configuration with one of the socket() methods; for example:
964 @code{.cpp}
965 randolf::rsocket r; // Empty rsocket (empty / incomplete initialization)
966 r.debug(true); // Enable debug mode
967 r.socket(...); // Required to complete rsocket initialization
968 @endcode
969
970 @par Notes
971
972 The built-in defaults, when not provided, are as follows ("family" is also
973 known as the "communication domain"):
974 - @c family = AF_INET
975 - @c type = SOCK_STREAM
976 - @c protocol = PF_UNSPEC
977
978 You will need to use one of the socket(...) methods to specify socket details
979 after defining rsocket objects with empty constructors so that you can catch
980 runtime exceptions. (This also provides you with an option to enable debug
981 mode during runtime prior to attempting to open an rsocket.)
982
983 @par Examples
984
985 @code{.cpp}
986 #include <iostream> // std::cout, std::cerr, std::endl, etc.
987 #include <randolf/rex>
988 #include <randolf/rsocket>
989
990 randolf::rsocket r; // <-- Empty rsocket initialization (no exceptions)
991
992 int main(int argc, char *argv[]) {
993 try {
994 r.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Required to complete rsocket initialization
995 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
996 // ... other socket I/O operations
997 r.close();
998 } catch (const randolf::rex::xALL& e) {
999 std::cerr << "Socket exception: " << e.what() << std::endl;
1000 return EXIT_FAILURE;
1001 } catch (const std::exception& e) {
1002 std::cerr << "Other exception: " << e.what() << std::endl;
1003 return EXIT_FAILURE;
1004 }
1005 return EXIT_SUCCESS;
1006 } // -x- int main -x-
1007 @endcode
1008 @see mptcp()
1009 @see rsocket()
1010 @see socket()
1011 @see socket_family()
1012 @see socket_fd()
1013 @see socket_protocol()
1014 @see socket_type()
1015 @qualifier TLS
1016 *///=========================================================================
1017 rsocket() noexcept {} // -x- constructor rsocket -x-
1018
1019 /*======================================================================*//**
1020 @brief
1021 Instantiate an rsocket based on a minimal subset of the settings in the
1022 specified rsocket (using it as a template), without actually opening a
1023 socket, and therefore also without throwing any exceptions.
1024
1025 @note
1026 This constructor does not suffice as a full clone()-like operation, and is
1027 minimal because it's used internally by the @ref accept() and @ref accept4()
1028 methods.
1029
1030 Details that are absorbed from the template/source rsocket (which eliminates
1031 the need to assign, set, and configure various parameters (TLS and TLS SNI
1032 parameters will be copied in a passive way by default):
1033 - Socket family (SO_DOMAIN)
1034 - Socket type (SO_TYPE)
1035 - Socket protocol (SO_PROTOCOL)
1036 - Socket protocol special internal handling provision for @c IPPROTO_MPTCP
1037 - TLS details (status, context {which includes loaded certificates},
1038 policies specified with @ref TLS_FLAGS, etc.)
1039 - EoL details
1040 - TLS context (you'll still need to call `tls_ctx(r->tls_ctx())`)
1041 - TLS SNI map (you'll still need to call `tls_sni(r->tls_sni())`)
1042
1043 @post
1044 The TLS Context will not be initialized because it needs a real socket to
1045 draw from. If using TLS, you'll need to use the @ref tls() method after
1046 the underlying socket has been initiated.
1047
1048 When @c flag_create_socket is set to FALSE, no exceptions will be thrown.
1049
1050 @see mptcp()
1051 @see rsocket()
1052 @see socket()
1053 @see socket_family()
1054 @see socket_fd()
1055 @see socket_protocol()
1056 @see socket_type()
1057 @qualifier TLS
1058 *///=========================================================================
1059 rsocket(
1060 /// Source rsocket object to use as a template to absorb settings from
1061 const rsocket* rtemplate,
1062 /// TRUE = create a new socket handle (default)@n
1063 /// FALSE = don't create a new socket because a new one will be assigned or
1064 /// created later (all variants of the @ref accept() methods do this)
1065 const bool flag_create_socket = true) {
1066
1067 // --------------------------------------------------------------------------
1068 // General socket variables.
1069 // --------------------------------------------------------------------------
1070 __rtemplate = rtemplate; // Used by SNI callback (TODO)
1071 if (flag_create_socket) {
1072 __socket(rtemplate->__socket_addr->ss_family,
1073 rtemplate->__socket_type,
1074 rtemplate->__socket_protocol);
1075 } else { // !flag_create_socket
1076 __socket_addr->ss_family = rtemplate->__socket_addr->ss_family;
1077 __socket_type = rtemplate->__socket_type;
1078 __socket_protocol = rtemplate->__socket_protocol;
1079 } // -x- if flag_create_socket -x-
1080 __socket_mptcp_flag = rtemplate->__socket_mptcp_flag;
1081 __name = rtemplate->__name;
1082
1083 // --------------------------------------------------------------------------
1084 // TLS and SNI settings, but not whether TLS is enabled or other settings
1085 // since this is at a pre-handshake stage since this constructor is mostly
1086 // used by the accept() and accept4() methods before accepting a connection.
1087 // --------------------------------------------------------------------------
1088 __tls_ctx = rtemplate->__tls_ctx;
1089 __tls_sni = rtemplate->__tls_sni;
1090
1091 // --------------------------------------------------------------------------
1092 // EoL variables.
1093 // --------------------------------------------------------------------------
1094 __eol.assign( rtemplate->__eol); // Copy source std::string's contents (not a reference)
1095 __eol_fix_printf = rtemplate->__eol_fix_printf; // Dynamic printf modification policy
1096 __eol_adoption = rtemplate->__eol_adoption; // EoL adoption policy
1097
1098 } // -x- constructor rsocket -x-
1099
1100 /*======================================================================*//**
1101 @brief
1102 Instantiate an rsocket with IP/host address and [optional] port number.
1103
1104 This is either the endpoint that our underlying socket will be connecting to,
1105 or it's the local address of the server daemon that our socket will listen()
1106 to and accept() inbound connections from.
1107
1108 @par Notes
1109 The built-in defaults, when not provided, are as follows ("family" is also
1110 known as the "communication domain"):
1111 - @c family = AF_INET
1112 - @c type = SOCK_STREAM
1113 - @c protocol = PF_UNSPEC
1114
1115 The socket() methods do the same work as the constructors with matching
1116 arguments, and are provided as convenience methods intended to augment
1117 empty rsocket constructors used in header files, but do require an address to
1118 be specified (for protocols that need port numbers, such as TCP or UDP, a
1119 "port" number also needs to be specified since the default port 0 will result
1120 in the dynamic allocation of a port number by the system).
1121
1122 For UNIX domain sockets use family AF_UNIX, type SOCK_STREAM, and protocol
1123 IPPROTO_IP when instantiating or opening an rsocket.
1124
1125 @par Examples
1126
1127 @code{.cpp}
1128 #include <iostream> // std::cout, std::cerr, std::endl, etc.
1129 #include <randolf/rex>
1130 #include <randolf/rsocket>
1131
1132 int main(int argc, char *argv[]) {
1133 try {
1134 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP); // <-- Same as socket() method
1135 r.setsockopt(SOL_SOCKET, SO_REUSEADDR, true);
1136 r.bind("127.0.0.1", 32768);
1137 // ... other socket I/O operations
1138 r.close();
1139 } catch (const randolf::rex::xALL& e) {
1140 std::cerr << "Socket exception: " << e.what() << std::endl;
1141 return EXIT_FAILURE;
1142 } catch (const std::exception& e) {
1143 std::cerr << "Other exception: " << e.what() << std::endl;
1144 return EXIT_FAILURE;
1145 }
1146 return EXIT_SUCCESS;
1147 } // -x- int main -x-
1148 @endcode
1149
1150 When using @c IPPROTO_MPTCP a special internal handling provision is used to
1151 automatically prevent errors resulting from the use of POSIX functions that
1152 don't support MPTCP by substituting only the @c IPPROTO_MPTCP parameter with
1153 the @c IPPROTO_TCP parameter. (This substitution does not occur with any
1154 other protocols.)
1155
1156 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
1157 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
1158 @throws randolf::rex::xEINVAL Protocal family invalid or not available
1159 @throws randolf::rex::xEINVAL Invalid flags in type
1160 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1161 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1162 @throws randolf::rex::xENOBUFS Insufficient memory
1163 @throws randolf::rex::xENOMEM Insufficient memory
1164 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1165 supported within the specified family (a.k.a., communication domain)
1166 @see mptcp()
1167 @see rsocket()
1168 @see socket()
1169 @qualifier TLS
1170 *///=========================================================================
1171 rsocket(
1172 /// Communication domain; usually one of:@n
1173 /// AF_INET (IPv4)@n
1174 /// AF_INET6 (IPv6)@n
1175 /// AF_UNIX (UNIX domain sockets)
1176 const int family,
1177 /// Communication semantics; usually one of:@n
1178 /// SOCK_STREAM (common for TCP)@n
1179 /// SOCK_DGRAM (common for UDP)
1180 const int type = SOCK_STREAM,
1181 /// Network protocol; usually one of:@n
1182 /// IPPROTO_TCP / IPPROTO_MPTCP@n
1183 /// IPPROTO_UDP@n
1184 /// IPPROTO_IP@n
1185 /// PF_UNSPEC (auto-detect)
1186 const int protocol = PF_UNSPEC) {
1187 __socket(family, type, protocol);
1188 } // -x- constructor rsocket -x-
1189
1190 /*======================================================================*//**
1191 @brief
1192 Destructor, which closes any underlying sockets, frees any TLS structures
1193 that were allocated by OpenSSL, and performs any other necessary clean-up
1194 before finally copying the I/O statistics to a designated structure (if one
1195 was specified with the @ref net_io_final() method).
1196 @attention
1197 Developers should take care to check that the @ref rsocket_io::is_final flag
1198 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
1199 since there's no guarantee that the destructor will necessarily be executed
1200 in a timely manner (this flag is set last, after all other statistics are
1201 copied into the structure while in a mutex-locked state).
1202
1203 The order in which various resources are deleted, freed, shutdown, and closed
1204 is important, particularly when TLS is enabled and SNI is used, otherwise the
1205 result can be segmentation faults (which might not show up until later, such
1206 as after returning from @c main depending on how multi-threaded applications
1207 interact with the Operating System; while @c rsocket is designed with threads
1208 in mind, it's also designed to be neutral in this respect so as to provide
1209 the same consistency to both thread-based and non-thread-based applications).
1210 @see net_io_final()
1211 @qualifier TLS
1212 *///=========================================================================
1213 ~rsocket() noexcept {
1214
1215 // --------------------------------------------------------------------------
1216 // Debug.
1217 // --------------------------------------------------------------------------
1218 if (__debug) {
1219 debug("destructor(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1220 + ");");
1221 debug(false); // Disable debug mode (might trigger extra clean-up in future)
1222 } // -x- if _debug -x-
1223
1224 // --------------------------------------------------------------------------
1225 // Perform shutdown() before close() if shutdown didn't occur. If we don't
1226 // do this, closing an open socket that wasn't shutdown can leak resources
1227 // on some Operating Systems, and will leak OpenSSL resources.
1228 // --------------------------------------------------------------------------
1229 try {
1230 if (!__socket_got_shutdown && __socket_open) shutdown(SHUT_RDWR);
1231 } catch (const randolf::rex::xENOTCONN& e) {
1232 // Do nothing, although it would be nice to prevent this (delete rsocket after a bind() failiure to trigger this exception)
1233 }
1234
1235 // --------------------------------------------------------------------------
1236 // Free memory and resources and close handles that need to be freed/closed.
1237 // --------------------------------------------------------------------------
1238 if (__tls_sni != nullptr) tls_sni(nullptr); // Remove any SNI callbacks // WE SHOULDN'T NEED THIS, BUT OpenSSL LEAKS ONCE IN A WHILE IF WE DON'T DO IT
1239 if (__tls_fd != nullptr) SSL_free(__tls_fd); // OpenSSL resource clean-up
1240 //if (__tls_ctx != nullptr) SSL_CTX_free(__tls_ctx); // This causes high CPU on non-encrypted connections
1241 if (__socket_fd != 0) ::close(__socket_fd); // POSIX close() is ensured with ::close()
1242 if (__socket_addr != nullptr) ::free(__socket_addr); // Socket address structure
1243 if (__rtemplate != nullptr) __rtemplate = nullptr; // Clear reference to rsocket template (this is for security; it doesn't actually free or delete anything)
1244
1245 // --------------------------------------------------------------------------
1246 // Copy statistics to final location. (We do this last in case there's an
1247 // issue with locking; there shouldn't be, but if there is then at least
1248 // we're not inadvertently hanging on to any other resources at this point
1249 // that need to be closed and freed.)
1250 // --------------------------------------------------------------------------
1251 if (__io_final_addr != nullptr) {
1252 __io_final_addr->lock();
1253 __io_final_addr->bytes_rx = __bytes_rx;
1254 __io_final_addr->bytes_tx = __bytes_tx;
1255 __io_final_addr->bytes_sx = __tls ? 0 : (__buffer != nullptr ? __buffer->get_utilized() : 0); // <bytes in buffer; are these lost bytes that should be subtracted from __bytes_rx?>
1256 __io_final_addr->crypt_rx = __crypt_rx;
1257 __io_final_addr->crypt_tx = __crypt_tx;
1258 __io_final_addr->crypt_sx = __tls ? (__buffer != nullptr ? __buffer->get_utilized() : 0) : 0; // <bytes in buffer; are these lost bytes that should be subtracted from __crypt_rx?>
1259 __io_final_addr->is_final = true;
1260 __io_final_addr->unlock();
1261 } // -x- if __io_final_addr -x-
1262
1263 // --------------------------------------------------------------------------
1264 // Free memory and resources and close handles that need to be freed/closed.
1265 // --------------------------------------------------------------------------
1266 if (__buffer_bam != nullptr) ::free(__buffer_bam); // Release the rring_bam structure (C)
1267 if (__buffer != nullptr) delete __buffer; // Release the rring buffer (C++)
1268
1269 } // -x- destructor ~rsocket -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 randolf::rsocket* c = r->accept();
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 option 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 To prevent resource leaks, the resulting rsocket needs to be deleted after
1325 it's no longer needed.
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 Reference to new rsocket object representing the connection received
1365 @see accept_sp()
1366 @see accept_up()
1367 @see accept4()
1368 @see accept4_sp()
1369 @see accept4_up()
1370 @see listen
1371 @see setsockopt(int, int, int)
1372 @qualifier POSIX
1373 @qualifier TLS
1374 *///=========================================================================
1375 rsocket* accept() {
1376 if (__debug) debug("accept(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1377 + ");");
1378
1379 // --------------------------------------------------------------------------
1380 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1381 // new incoming connection.
1382 // --------------------------------------------------------------------------
1383 rsocket* r = new rsocket(this, false);
1384
1385 // --------------------------------------------------------------------------
1386 // Wait for incoming connection, and update internal flags in client rsocket.
1387 // --------------------------------------------------------------------------
1388 r->__socket_fd = ::accept(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size);
1389 if (r->__socket_fd == -1) {
1390 delete r; // Memory management
1391 __rc_check(-1); // This part throws the exception
1392 } // -x- if rc -x-
1393 r->__socket_open = true;
1394 r->__socket_connected = true;
1395
1396 // --------------------------------------------------------------------------
1397 // Perform TLS accept() as well, but only if TLS is enabled.
1398 //
1399 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1400 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1401 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1402 // --------------------------------------------------------------------------
1403 if (__tls) {
1404 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress | __tls_no_read_ahead); // | __tls_server_mode);
1405 r->tls(true); // Make sure __tls_fd is configured correctly
1406 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1407 __rc_check_tls(SSL_accept(r->__tls_fd));
1408 } // -x- if __tls -x-
1409
1410 return r;
1411 } // -x- rsocket* accept -x-
1412
1413 /*======================================================================*//**
1414 @brief
1415 Accept new [inbound] socket connections. (This is typically used in a loop.)
1416
1417 @pre
1418 The resulting rsocket object is created before the actual call to the @c
1419 accept() function.
1420
1421 For faster initial connections, disabling the Nagle Algorithm (which was
1422 originally introduced to the Standards Track in 1984 by John Nagle as a
1423 solution that actually solved a lot of network congestion problems by adding
1424 a 200ms internal timeout in TCP packet assembly that made it possible to
1425 reduce the total quantity of packets being transmitted by combining multiple
1426 small portions of data, within this 200ms timeframe, into a single packet
1427 instead of sending multiple packets in rapid succession) will likely improve
1428 responsiveness from a direct end-user experience perspective. To disable the
1429 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1430 connections:
1431 @code{.cpp}
1432 #include <netinet/tcp.h> // TCP_NODELAY
1433 // ...
1434 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1435 std::shared_ptr<randolf::rsocket> c = r->accept_sp();
1436 @endcode
1437 This particular socket option will also be carried through automatically by
1438 the network stack and therefore also be set on the resulting client socket,
1439 so it's more efficient to set it this way.
1440
1441 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1442 socket option on the resulting client socket, although this takes more work
1443 despite being used only where it's needed:
1444 @code{.cpp}
1445 #include <netinet/tcp.h> // TCP_CORK
1446 // ...
1447 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1448 c->sendline("Welcome");
1449 c->sendline("=======");
1450 c->send("Login name: ");
1451 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1452 @endcode
1453 Upon the end of the packet-assembly phase, which is communicated to the
1454 network stack by setting the @c TCP_CORK socket option to @c false, the
1455 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1456 which effectively serves as a temporary bypass of the Nagle Algorithm
1457 (this is probably better overall for optimizing internet traffic for the
1458 internet as a whole if your application doesn't need the Nagle Algorithm
1459 disabled for everything).
1460
1461 @note
1462 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1463 select(), and accept()/accept4() when using multiple sockets.
1464
1465 @post
1466 The resulting rsocket is wrapped in a @c std::shared_ptr (a C++ smart
1467 pointer that aids in the prevention of resource leaks). Internally, the
1468 `shared pointer` is created using @c std::make_shared because it's more
1469 efficient since it performs only one heap-allocation instead of two (that
1470 accounts for both the control block and the instantiated object's data),
1471 and because it's more robust due to slightly better exception safety.
1472
1473 @throws randolf::rex::xEBADF The underlying socket is not open
1474 @throws randolf::rex::xECONNABORTED The connection was aborted
1475 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1476 part of the user address space
1477 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1478 @throws randolf::rex::xEHOSTUNREACH No route to host
1479 @throws randolf::rex::xEINTR Interrupted by a signal
1480 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1481 length of the address is invalid
1482 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1483 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1484 @throws randolf::rex::xENETUNREACH No route to network
1485 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1486 @throws randolf::rex::xENOBUFS Insufficient memory
1487 @throws randolf::rex::xENOMEM Insufficient memory
1488 @throws randolf::rex::xENONET Requested host is not reachable on the network
1489 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1490 is not supported
1491 @throws randolf::rex::xENOSR System ran out of stream resources
1492 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1493 doesn't refer to a socket
1494 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1495 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1496 @throws randolf::rex::xEPROTO Protocol error
1497 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1498 supported within the specified family (a.k.a., communication domain)
1499 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1500 a system call is restartable and can be intercepted-and-redirected
1501 (there is no need to catch this exception unless you are an advanced
1502 developer, in which case you likely still won't need to catch it in
1503 code at this level)
1504 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1505 supported within the specified family (a.k.a., communication domain)
1506 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1507 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1508 no inbound connections are waiting
1509
1510 @returns Newly-created rsocket object representing the connection received,
1511 wrapped in std::shared_ptr (a C++ smart pointer)
1512 @see accept()
1513 @see accept_up()
1514 @see accept4()
1515 @see accept4_sp()
1516 @see accept4_up()
1517 @see listen
1518 @see setsockopt(int, int, int)
1519 @qualifier TLS
1520 *///=========================================================================
1521 std::shared_ptr<rsocket> accept_sp() {
1522 if (__debug) debug("accept_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1523 + ");");
1524
1525 // --------------------------------------------------------------------------
1526 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1527 // new incoming connection.
1528 // --------------------------------------------------------------------------
1529 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1530
1531 // --------------------------------------------------------------------------
1532 // Wait for incoming connection, and update internal flags in client rsocket.
1533 // --------------------------------------------------------------------------
1534 r->__socket_fd = __rc_check(::accept(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size));
1535 r->__socket_open = true;
1536 r->__socket_connected = true;
1537
1538 // --------------------------------------------------------------------------
1539 // Perform TLS accept() as well, but only if TLS is enabled.
1540 //
1541 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1542 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1543 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1544 // --------------------------------------------------------------------------
1545 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1546 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress | __tls_no_read_ahead); // | __tls_server_mode);
1547 r->tls(true); // Make sure __tls_fd is configured correctly
1548 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1549 __rc_check_tls(SSL_accept(r->__tls_fd));
1550 } // -x- if __tls -x-
1551
1552 return r;
1553 } // -x- std::shared_ptr<rsocket> accept_sp -x-
1554
1555 /*======================================================================*//**
1556 @brief
1557 Accept new [inbound] socket connections. (This is typically used in a loop.)
1558
1559 @pre
1560 The resulting rsocket object is created before the actual call to the @c
1561 accept() function.
1562
1563 For faster initial connections, disabling the Nagle Algorithm (which was
1564 originally introduced to the Standards Track in 1984 by John Nagle as a
1565 solution that actually solved a lot of network congestion problems by adding
1566 a 200ms internal timeout in TCP packet assembly that made it possible to
1567 reduce the total quantity of packets being transmitted by combining multiple
1568 small portions of data, within this 200ms timeframe, into a single packet
1569 instead of sending multiple packets in rapid succession) will likely improve
1570 responsiveness from a direct end-user experience perspective. To disable the
1571 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1572 connections:
1573 @code{.cpp}
1574 #include <netinet/tcp.h> // TCP_NODELAY
1575 // ...
1576 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1577 std::unique_ptr<randolf::rsocket> c = r->accept_sp();
1578 @endcode
1579 This particular socket option will also be carried through automatically by
1580 the network stack and therefore also be set on the resulting client socket,
1581 so it's more efficient to set it this way.
1582
1583 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1584 socket option on the resulting client socket, although this takes more work
1585 despite being used only where it's needed:
1586 @code{.cpp}
1587 #include <netinet/tcp.h> // TCP_CORK
1588 // ...
1589 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1590 c->sendline("Welcome");
1591 c->sendline("=======");
1592 c->send("Login name: ");
1593 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1594 @endcode
1595 Upon the end of the packet-assembly phase, which is communicated to the
1596 network stack by setting the @c TCP_CORK socket option to @c false, the
1597 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1598 which effectively serves as a temporary bypass of the Nagle Algorithm
1599 (this is probably better overall for optimizing internet traffic for the
1600 internet as a whole if your application doesn't need the Nagle Algorithm
1601 disabled for everything).
1602
1603 @note
1604 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1605 select(), and accept()/accept4() when using multiple sockets.
1606
1607 @post
1608 The resulting rsocket is wrapped in a @c std::unique_ptr (a C++ smart
1609 pointer that aids in the prevention of resource leaks). Internally, the
1610 `unique pointer` is created using @c std::make_unique because it's more
1611 efficient since it performs only one heap-allocation instead of two (that
1612 accounts for both the control block and the instantiated object's data), and
1613 because it's more robust due to slightly better exception safety.
1614
1615 @throws randolf::rex::xEBADF The underlying socket is not open
1616 @throws randolf::rex::xECONNABORTED The connection was aborted
1617 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1618 part of the user address space
1619 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1620 @throws randolf::rex::xEHOSTUNREACH No route to host
1621 @throws randolf::rex::xEINTR Interrupted by a signal
1622 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1623 length of the address is invalid
1624 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1625 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1626 @throws randolf::rex::xENETUNREACH No route to network
1627 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1628 @throws randolf::rex::xENOBUFS Insufficient memory
1629 @throws randolf::rex::xENOMEM Insufficient memory
1630 @throws randolf::rex::xENONET Requested host is not reachable on the network
1631 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1632 is not supported
1633 @throws randolf::rex::xENOSR System ran out of stream resources
1634 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1635 doesn't refer to a socket
1636 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1637 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1638 @throws randolf::rex::xEPROTO Protocol error
1639 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1640 supported within the specified family (a.k.a., communication domain)
1641 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1642 a system call is restartable and can be intercepted-and-redirected
1643 (there is no need to catch this exception unless you are an advanced
1644 developer, in which case you likely still won't need to catch it in
1645 code at this level)
1646 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1647 supported within the specified family (a.k.a., communication domain)
1648 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1649 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1650 no inbound connections are waiting
1651
1652 @returns Newly-created rsocket object representing the connection received,
1653 wrapped in std::unique_ptr (a C++ smart pointer)
1654 @see accept()
1655 @see accept_sp()
1656 @see accept4()
1657 @see accept4_sp()
1658 @see accept4_up()
1659 @see listen
1660 @see setsockopt(int, int, int)
1661 @qualifier TLS
1662 *///=========================================================================
1663 std::unique_ptr<rsocket> accept_up() {
1664 if (__debug) debug("accept_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1665 + ");");
1666
1667 // --------------------------------------------------------------------------
1668 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1669 // new incoming connection.
1670 // --------------------------------------------------------------------------
1671 std::unique_ptr<rsocket> r = std::make_unique<rsocket>(this, false);
1672
1673 // --------------------------------------------------------------------------
1674 // Wait for incoming connection, and update internal flags in client rsocket.
1675 // --------------------------------------------------------------------------
1676 r->__socket_fd = __rc_check(::accept(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size));
1677 r->__socket_open = true;
1678 r->__socket_connected = true;
1679
1680 // --------------------------------------------------------------------------
1681 // Perform TLS accept() as well, but only if TLS is enabled.
1682 //
1683 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1684 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1685 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1686 // --------------------------------------------------------------------------
1687 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1688 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress | __tls_no_read_ahead); // | __tls_server_mode);
1689 r->tls(true); // Make sure __tls_fd is configured correctly
1690 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1691 __rc_check_tls(SSL_accept(r->__tls_fd));
1692 } // -x- if __tls -x-
1693
1694 return r;
1695 } // -x- std::unique_ptr<rsocket> accept_up -x-
1696
1697 /*======================================================================*//**
1698 @brief
1699 Accept new [inbound] socket connections, with socket flags specified. (This
1700 is typically used in a loop.)
1701
1702 @pre
1703 The resulting rsocket object is created before the actual call to the @c
1704 accept4() function.
1705
1706 For faster initial connections, disabling the Nagle Algorithm (which was
1707 originally introduced to the Standards Track in 1984 by John Nagle as a
1708 solution that actually solved a lot of network congestion problems by adding
1709 a 200ms internal timeout in TCP packet assembly that made it possible to
1710 reduce the total quantity of packets being transmitted by combining multiple
1711 small portions of data, within this 200ms timeframe, into a single packet
1712 instead of sending multiple packets in rapid succession) will likely improve
1713 responsiveness from a direct end-user experience perspective. To disable the
1714 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1715 connections:
1716 @code{.cpp}
1717 #include <netinet/tcp.h> // TCP_NODELAY
1718 // ...
1719 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1720 randolf::rsocket* c = r->accept4();
1721 @endcode
1722 This particular socket option will also be carried through automatically by
1723 the network stack and therefore also be set on the resulting client socket,
1724 so it's more efficient to set it this way.
1725
1726 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1727 socket option on the resulting client socket, although this takes more work
1728 despite being used only where it's needed:
1729 @code{.cpp}
1730 #include <netinet/tcp.h> // TCP_CORK
1731 // ...
1732 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1733 c->sendline("Welcome");
1734 c->sendline("=======");
1735 c->send("Login name: ");
1736 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1737 @endcode
1738 Upon the end of the packet-assembly phase, which is communicated to the
1739 network stack by setting the @c TCP_CORK socket option to @c false, the
1740 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1741 which effectively serves as a temporary bypass of the Nagle Algorithm
1742 (this is probably better overall for optimizing internet traffic for the
1743 internet as a whole if your application doesn't need the Nagle Algorithm
1744 disabled for everything).
1745
1746 @note
1747 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1748 select(), and accept()/accept4() when using multiple sockets.
1749
1750 @post
1751 To prevent resource leaks, the resulting rsocket needs to be deleted after
1752 it's no longer needed.
1753
1754 @throws randolf::rex::xEBADF The underlying socket is not open
1755 @throws randolf::rex::xECONNABORTED The connection was aborted
1756 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1757 part of the user address space
1758 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1759 @throws randolf::rex::xEHOSTUNREACH No route to host
1760 @throws randolf::rex::xEINTR Interrupted by a signal
1761 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1762 length of the address is invalid
1763 @throws randolf::rex::xEINVAL Invalid value in flags
1764 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1765 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1766 @throws randolf::rex::xENETUNREACH No route to network
1767 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1768 @throws randolf::rex::xENOBUFS Insufficient memory
1769 @throws randolf::rex::xENOMEM Insufficient memory
1770 @throws randolf::rex::xENONET Requested host is not reachable on the network
1771 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1772 is not supported
1773 @throws randolf::rex::xENOSR System ran out of stream resources
1774 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1775 doesn't refer to a socket
1776 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1777 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1778 @throws randolf::rex::xEPROTO Protocol error
1779 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1780 supported within the specified family (a.k.a., communication domain)
1781 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1782 a system call is restartable and can be intercepted-and-redirected
1783 (there is no need to catch this exception unless you are an advanced
1784 developer, in which case you likely still won't need to catch it in
1785 code at this level)
1786 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1787 supported within the specified family (a.k.a., communication domain)
1788 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1789 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1790 no inbound connections are waiting
1791
1792 @returns Newly-created rsocket object representing the connection received
1793 @see accept()
1794 @see accept_sp()
1795 @see accept_up()
1796 @see accept4_sp()
1797 @see accept4_up()
1798 @see listen
1799 @see setsockopt(int, int, int)
1800 @qualifier POSIX
1801 @qualifier TLS
1802 *///=========================================================================
1803 rsocket* accept4(
1804 /// SOCK_NONBLOCK@n
1805 /// SOCK_CLOEXEC
1806 const int posix_flags = 0) {
1807 if (__debug) debug("accept4(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1808 + ", " + std::to_string(posix_flags)
1809 + ");");
1810
1811 // --------------------------------------------------------------------------
1812 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1813 // new incoming connection.
1814 // --------------------------------------------------------------------------
1815 rsocket* r = new rsocket(this, false);
1816
1817 // --------------------------------------------------------------------------
1818 // Wait for incoming connection, and update internal flags in client rsocket.
1819 // --------------------------------------------------------------------------
1820 r->__socket_fd = ::accept4(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size, posix_flags);
1821 if (r->__socket_fd == -1) {
1822 delete r; // Memory management
1823 __rc_check(-1); // This part throws the exception
1824 } // -x- if rc -x-
1825 r->__socket_open = true;
1826 r->__socket_connected = true;
1827
1828 // --------------------------------------------------------------------------
1829 // Perform TLS accept() as well, but only if TLS is enabled.
1830 //
1831 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1832 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1833 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1834 // --------------------------------------------------------------------------
1835 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1836 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress | __tls_no_read_ahead); // | __tls_server_mode);
1837 r->tls(true); // Make sure __tls_fd is configured correctly
1838 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1839 __rc_check_tls(SSL_accept(r->__tls_fd));
1840 } // -x- if __tls -x-
1841
1842 return r;
1843 } // -x- rsocket* accept4 -x-
1844
1845 /*======================================================================*//**
1846 @brief
1847 Accept new [inbound] socket connections, with socket flags specified. (This
1848 is typically used in a loop.)
1849
1850 @pre
1851 The resulting rsocket object is created before the actual call to the @c
1852 accept4() function.
1853
1854 For faster initial connections, disabling the Nagle Algorithm (which was
1855 originally introduced to the Standards Track in 1984 by John Nagle as a
1856 solution that actually solved a lot of network congestion problems by adding
1857 a 200ms internal timeout in TCP packet assembly that made it possible to
1858 reduce the total quantity of packets being transmitted by combining multiple
1859 small portions of data, within this 200ms timeframe, into a single packet
1860 instead of sending multiple packets in rapid succession) will likely improve
1861 responsiveness from a direct end-user experience perspective. To disable the
1862 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
1863 connections:
1864 @code{.cpp}
1865 #include <netinet/tcp.h> // TCP_NODELAY
1866 // ...
1867 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
1868 std::shared_ptr<randolf::rsocket> c = r->accept4_sp();
1869 @endcode
1870 This particular socket option will also be carried through automatically by
1871 the network stack and therefore also be set on the resulting client socket,
1872 so it's more efficient to set it this way.
1873
1874 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
1875 socket option on the resulting client socket, although this takes more work
1876 despite being used only where it's needed:
1877 @code{.cpp}
1878 #include <netinet/tcp.h> // TCP_CORK
1879 // ...
1880 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
1881 c->sendline("Welcome");
1882 c->sendline("=======");
1883 c->send("Login name: ");
1884 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
1885 @endcode
1886 Upon the end of the packet-assembly phase, which is communicated to the
1887 network stack by setting the @c TCP_CORK socket option to @c false, the
1888 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
1889 which effectively serves as a temporary bypass of the Nagle Algorithm
1890 (this is probably better overall for optimizing internet traffic for the
1891 internet as a whole if your application doesn't need the Nagle Algorithm
1892 disabled for everything).
1893
1894 @note
1895 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
1896 select(), and accept()/accept4() when using multiple sockets.
1897
1898 @post
1899 The resulting rsocket is wrapped in a @c std::shared_ptr (a C++ smart
1900 pointer that aids in the prevention of resource leaks). Internally, the
1901 `shared pointer` is created using @c std::make_shared because it's more
1902 efficient since it performs only one heap-allocation instead of two (that
1903 accounts for both the control block and the instantiated object's data),
1904 and because it's more robust due to slightly better exception safety.
1905
1906 @throws randolf::rex::xEBADF The underlying socket is not open
1907 @throws randolf::rex::xECONNABORTED The connection was aborted
1908 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
1909 part of the user address space
1910 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
1911 @throws randolf::rex::xEHOSTUNREACH No route to host
1912 @throws randolf::rex::xEINTR Interrupted by a signal
1913 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
1914 length of the address is invalid
1915 @throws randolf::rex::xEINVAL Invalid value in flags
1916 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
1917 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
1918 @throws randolf::rex::xENETUNREACH No route to network
1919 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
1920 @throws randolf::rex::xENOBUFS Insufficient memory
1921 @throws randolf::rex::xENOMEM Insufficient memory
1922 @throws randolf::rex::xENONET Requested host is not reachable on the network
1923 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
1924 is not supported
1925 @throws randolf::rex::xENOSR System ran out of stream resources
1926 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
1927 doesn't refer to a socket
1928 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
1929 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
1930 @throws randolf::rex::xEPROTO Protocol error
1931 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
1932 supported within the specified family (a.k.a., communication domain)
1933 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
1934 a system call is restartable and can be intercepted-and-redirected
1935 (there is no need to catch this exception unless you are an advanced
1936 developer, in which case you likely still won't need to catch it in
1937 code at this level)
1938 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
1939 supported within the specified family (a.k.a., communication domain)
1940 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
1941 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
1942 no inbound connections are waiting
1943
1944 @returns Newly-created rsocket object representing the connection received,
1945 wrapped in std::shared_ptr (a C++ smart pointer)
1946 @see accept()
1947 @see accept_sp()
1948 @see accept_up()
1949 @see accept4()
1950 @see accept4_up()
1951 @see listen
1952 @see setsockopt(int, int, int)
1953 @qualifier TLS
1954 *///=========================================================================
1955 std::shared_ptr<rsocket> accept4_sp(
1956 /// SOCK_NONBLOCK@n
1957 /// SOCK_CLOEXEC
1958 const int posix_flags = 0) {
1959 if (__debug) debug("accept4_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
1960 + ", " + std::to_string(posix_flags)
1961 + ");");
1962
1963 // --------------------------------------------------------------------------
1964 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
1965 // new incoming connection.
1966 // --------------------------------------------------------------------------
1967 std::shared_ptr<rsocket> r = std::make_shared<rsocket>(this, false);
1968
1969 // --------------------------------------------------------------------------
1970 // Wait for incoming connection, and update internal flags in client rsocket.
1971 // --------------------------------------------------------------------------
1972 r->__socket_fd = __rc_check(::accept4(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size, posix_flags));
1973 r->__socket_open = true;
1974 r->__socket_connected = true;
1975
1976 // --------------------------------------------------------------------------
1977 // Perform TLS accept() as well, but only if TLS is enabled.
1978 //
1979 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
1980 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
1981 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
1982 // --------------------------------------------------------------------------
1983 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
1984 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress | __tls_no_read_ahead); // | __tls_server_mode);
1985 r->tls(true); // Make sure __tls_fd is configured correctly
1986 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
1987 __rc_check_tls(SSL_accept(r->__tls_fd));
1988 } // -x- if __tls -x-
1989// if (__tls_ctx != nullptr) { r->tls(true); r->__tls = (bool)__tls; } // Make sure __tls_fd is allocated appropriately
1990
1991 return r;
1992 } // -x- std::shared_ptr<rsocket> accept4_sp -x-
1993
1994 /*======================================================================*//**
1995 @brief
1996 Accept new [inbound] socket connections, with socket flags specified. (This
1997 is typically used in a loop.)
1998
1999 @pre
2000 The resulting rsocket object is created before the actual call to the @c
2001 accept4() function.
2002
2003 For faster initial connections, disabling the Nagle Algorithm (which was
2004 originally introduced to the Standards Track in 1984 by John Nagle as a
2005 solution that actually solved a lot of network congestion problems by adding
2006 a 200ms internal timeout in TCP packet assembly that made it possible to
2007 reduce the total quantity of packets being transmitted by combining multiple
2008 small portions of data, within this 200ms timeframe, into a single packet
2009 instead of sending multiple packets in rapid succession) will likely improve
2010 responsiveness from a direct end-user experience perspective. To disable the
2011 Nagle Algorithm, set the @c TCP_NODELAY socket option before accepting new
2012 connections:
2013 @code{.cpp}
2014 #include <netinet/tcp.h> // TCP_NODELAY
2015 // ...
2016 r->setsockopt(IPPROTO_TCP, TCP_NODELAY, true);
2017 std::unique_ptr<randolf::rsocket> c = r->accept4_sp();
2018 @endcode
2019 This particular socket option will also be carried through automatically by
2020 the network stack and therefore also be set on the resulting client socket,
2021 so it's more efficient to set it this way.
2022
2023 An alternative to disabling the Nagle Algorithm is to set the @c TCP_CORK
2024 socket option on the resulting client socket, although this takes more work
2025 despite being used only where it's needed:
2026 @code{.cpp}
2027 #include <netinet/tcp.h> // TCP_CORK
2028 // ...
2029 c->setsockopt(SOL_TCP, TCP_CORK, true); // Commence packet-assembly phase
2030 c->sendline("Welcome");
2031 c->sendline("=======");
2032 c->send("Login name: ");
2033 c->setsockopt(SOL_TCP, TCP_CORK, false); // End of packet-assembly phase
2034 @endcode
2035 Upon the end of the packet-assembly phase, which is communicated to the
2036 network stack by setting the @c TCP_CORK socket option to @c false, the
2037 packet is immediately sent, and without the 200ms Nagle Algorithm delay,
2038 which effectively serves as a temporary bypass of the Nagle Algorithm
2039 (this is probably better overall for optimizing internet traffic for the
2040 internet as a whole if your application doesn't need the Nagle Algorithm
2041 disabled for everything).
2042
2043 @note
2044 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
2045 select(), and accept()/accept4() when using multiple sockets.
2046
2047 @post
2048 The resulting rsocket is wrapped in a @c std::unique_ptr (a C++ smart
2049 pointer that aids in the prevention of resource leaks). Internally, the
2050 `unique pointer` is created using @c std::make_unique because it's more
2051 efficient since it performs only one heap-allocation instead of two (that
2052 accounts for both the control block and the instantiated object's data), and
2053 because it's more robust due to slightly better exception safety.
2054
2055 @throws randolf::rex::xEBADF The underlying socket is not open
2056 @throws randolf::rex::xECONNABORTED The connection was aborted
2057 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2058 part of the user address space
2059 @throws randolf::rex::xEHOSTDOWN Host is down or its network is disconnected
2060 @throws randolf::rex::xEHOSTUNREACH No route to host
2061 @throws randolf::rex::xEINTR Interrupted by a signal
2062 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
2063 length of the address is invalid
2064 @throws randolf::rex::xEINVAL Invalid value in flags
2065 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
2066 @throws randolf::rex::xENETDOWN Network is disconnected or router is down
2067 @throws randolf::rex::xENETUNREACH No route to network
2068 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
2069 @throws randolf::rex::xENOBUFS Insufficient memory
2070 @throws randolf::rex::xENOMEM Insufficient memory
2071 @throws randolf::rex::xENONET Requested host is not reachable on the network
2072 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
2073 is not supported
2074 @throws randolf::rex::xENOSR System ran out of stream resources
2075 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2076 doesn't refer to a socket
2077 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
2078 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
2079 @throws randolf::rex::xEPROTO Protocol error
2080 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
2081 supported within the specified family (a.k.a., communication domain)
2082 @throws randolf::rex::xERESTARTSYS Used in back-end tracing to indicate that
2083 a system call is restartable and can be intercepted-and-redirected
2084 (there is no need to catch this exception unless you are an advanced
2085 developer, in which case you likely still won't need to catch it in
2086 code at this level)
2087 @throws randolf::rex::xESOCKTNOSUPPORT Specified socket `type` is not
2088 supported within the specified family (a.k.a., communication domain)
2089 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
2090 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
2091 no inbound connections are waiting
2092
2093 @returns Newly-created rsocket object representing the connection received,
2094 wrapped in std::unique_ptr (a C++ smart pointer)
2095 @see accept()
2096 @see accept_sp()
2097 @see accept_up()
2098 @see accept4()
2099 @see accept4_sp()
2100 @see listen
2101 @see setsockopt(int, int, int)
2102 @qualifier TLS
2103 *///=========================================================================
2104 std::unique_ptr<rsocket> accept4_up(
2105 /// SOCK_NONBLOCK@n
2106 /// SOCK_CLOEXEC
2107 const int posix_flags = 0) {
2108 if (__debug) debug("accept4_sp(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2109 + ", " + std::to_string(posix_flags)
2110 + ");");
2111
2112 // --------------------------------------------------------------------------
2113 // Pre-allocate new rsocket to reduce CPU-cycle consumption after accepting a
2114 // new incoming connection.
2115 // --------------------------------------------------------------------------
2116 std::unique_ptr<rsocket> r = std::make_unique<rsocket>(this, false);
2117
2118 // --------------------------------------------------------------------------
2119 // Wait for incoming connection, and update internal flags in client rsocket.
2120 // --------------------------------------------------------------------------
2121 r->__socket_fd = __rc_check(::accept4(__socket_fd, (sockaddr*)r->__socket_addr, &r->__socket_addr_size, posix_flags));
2122 r->__socket_open = true;
2123 r->__socket_connected = true;
2124
2125 // --------------------------------------------------------------------------
2126 // Perform TLS accept() as well, but only if TLS is enabled.
2127 //
2128 // TODO: Add TLS_AUTO_DETECT flag and implement it with this conditional:
2129 // if (r->recv_char(MSG_PEEK) == (char)22) { ... enable TLS ... }
2130 // std::cout << "First byte: " << (int)(r->recv_char(MSG_PEEK)) << std::endl;
2131 // --------------------------------------------------------------------------
2132 if (__tls) { // Perform TLS accept() as well, but only if TLS is enabled
2133 r->tls_ctx(__tls_ctx, __tls_exclusive | __tls_ingress | __tls_egress | __tls_no_read_ahead); // | __tls_server_mode);
2134 r->tls(true); // Make sure __tls_fd is configured correctly
2135 if (__tls_sni != nullptr) r->tls_sni(__tls_sni); // Initialize SNI
2136 __rc_check_tls(SSL_accept(r->__tls_fd));
2137 } // -x- if __tls -x-
2138// if (__tls_ctx != nullptr) { r->tls(true); r->__tls = (bool)__tls; } // Make sure __tls_fd is allocated appropriately
2139
2140 return r;
2141 } // -x- std::unique_ptr<rsocket> accept4_up -x-
2142
2143 /*======================================================================*//**
2144 @brief
2145 Override the default @ref listen backlog for this rsocket.
2146
2147 @note
2148 The default backlog is `SOMAXCONN` (4096 on Linux, and 128 on older systems).
2149
2150 @returns The same rsocket object so as to facilitate stacking
2151 @see get_backlog
2152 @see listen
2153 @qualifier TLS
2154 *///=========================================================================
2155 rsocket& backlog(
2156 /// Backlog queue size (0 = use SOMAXCONN, which is the system default of
2157 /// 4096 on Linux, and 128 on older systems)
2158 const int backlog) noexcept {
2159 __socket_backlog = backlog == 0 ? SOMAXCONN : backlog;
2160 return *this;
2161 } // -x- rsocket& backlog -x-
2162
2163 /*======================================================================*//**
2164 @brief
2165 Bind this socket to the specified network address (and port number if the
2166 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
2167 used for server deamons, but can also be used to control the address from
2168 which the local host will make an outbound connection via the @ref connect()
2169 method (this bound address is the address from which the endpoint will
2170 recieve your connection).
2171
2172 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
2173 (or 1023 on some Operating Systems that test only for below 1024) in
2174 the absence of elevated access or the absence of a capability flag
2175 having been set
2176 @throws randolf::rex::xEACCES If binding to an interface on systems that
2177 require elevated access for direct interface binding in absence of
2178 said elevated access
2179 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
2180 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
2181 address is not available on any local interface
2182 @throws randolf::rex::xEBADF The underlying socket is not open
2183 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2184 part of the user address space
2185 @throws randolf::rex::xEINVAL Socket is already bound
2186 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
2187 valid for this socket's family (a.k.a., communication domain)
2188 @throws randolf::rex::xENOMEM Insufficient memory
2189 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2190 doesn't refer to a socket
2191 @n@n
2192 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
2193 @throws randolf::rex::xEACCES Write permission or search permission denied
2194 on a component of the path prefix (@c AF_UNIX family)
2195 @throws randolf::rex::xELOOP Too many symbolic links encountered while
2196 resolving address (@c AF_UNIX family)
2197 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
2198 @throws randolf::rex::xENOENT One of the path's directory components doesn't
2199 exist (@c AF_UNIX family)
2200 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
2201 directory (@c AF_UNIX family)
2202 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
2203
2204 @returns The same rsocket object so as to facilitate stacking
2205 @see accept()
2206 @see accept_sp()
2207 @see accept4()
2208 @see accept4_sp()
2209 @see bind(std::string, int)
2210 @see connect
2211 @see listen
2212 @qualifier POSIX
2213 @qualifier TLS
2214 *///=========================================================================
2215 rsocket& bind(
2216 /// Socket address structure
2217 const struct sockaddr* addr,
2218 /// Length of socket structure
2219 const socklen_t addrlen = sizeof(sockaddr)) {
2220 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2221 + ", <sockaddr*>"
2222 + ", size=" + std::to_string(addrlen)
2223 + ");");
2224 if (addrlen > sizeof(sockaddr_storage)) randolf::rex::mk_exception("addlen is too large for bind()", EINVAL);
2225 __rc_check(::bind(__socket_fd, addr, addrlen));
2226 std::memcpy(__socket_addr, addr, addrlen); // No errors were returned by bind(), so now we'll copy addr to __socket_addr
2227 return *this;
2228 } // -x- rsocket& bind -x-
2229
2230 //===========================================================================
2231 //
2232 // TODO: Document usage of IP_BIND_ADDRESS_NO_PORT to support connecting
2233 // from a specific IP address without losing ephemeral port selection.
2234 // Notes: https://idea.popcount.org/2014-04-03-bind-before-connect/
2235 //
2236 // TODO: Support AF_BLUETOOTH addresses
2237 // Reference:
2238 // https://man7.org/linux/man-pages/man7/address_families.7.html
2239 //
2240 // TODO: Support AF_IPX addresses
2241 // Reference:
2242 // https://man7.org/linux/man-pages/man7/address_families.7.html
2243 //
2244 // TODO: Support AF_APPLETALK addresses
2245 // Reference: https://man7.org/linux/man-pages/man7/ddp.7.html
2246 //
2247 // TODO: Support AF_PACKET addresses
2248 // Reference: https://man7.org/linux/man-pages/man7/packet.7.html
2249 //
2250 // TODO: Support AF_X25 addresses
2251 // Reference: https://man7.org/linux/man-pages/man7/x25.7.html
2252 //
2253 // TODO: Support AF_NETLINK addresses
2254 // Reference: https://man7.org/linux/man-pages/man7/netlink.7.html
2255 //
2256 /*======================================================================*//**
2257 @brief
2258 Bind this socket to the specified network address (and port number if the
2259 address family utilizes port numbers {e.g., TCP or UDP}). This is mostly
2260 used for server deamons, but can also be used to control the address from
2261 which the local host will make an outbound connection via the @ref connect()
2262 method (this bound address is address from which the endpoint will recieve
2263 your connection).
2264
2265 In addition to the standard IPv4 (AF_INET family) and IPv6 (AF_INET6 family)
2266 support, rsocket also supports a few other binding options in a manner that
2267 makes it easy to utilize, without having to handle special implementation
2268 details (because they're handled behind-the-scenese by this rsocket class).
2269
2270 The address string supports a number of different notations and formats,
2271 which are documented, hereunder, with examples:
2272 - IPv4 addresses
2273 - IPv6 addresses
2274 - Network interfaces
2275 - UNIX domain sockets
2276
2277 <hr>
2278 @par IPv4 address notations (address family @c AF_INET)
2279 Takes the form of @c x.x.x.x where each "x" represents an octet in the range
2280 of @c 0 through @c 255 (e.g., @c 127.0.0.1).
2281
2282 Under a proper implenetation of TCP/IP, an IPv4 address can be abbreviated
2283 to fewer octets. The following examples demonstrate this (an unabbreviated
2284 IPv4 address is included for completeness):
2285 - @c 0.0.0.1 may be abbreviated to @c 1
2286 - @c 4.0.0.1 may be abbrevaited to @c 4.1
2287 - @c 4.3.0.1 may be abbreviated to @c 4.3.1
2288 - @c 4.3.2.1 is not abbreviated (this is the most common usage)
2289
2290 @par Example of binding to IPv4 localhost
2291
2292 @code{.cpp}
2293 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2294 #include <randolf/rex>
2295 #include <randolf/rsocket>
2296
2297 int main(int argc, char *argv[]) {
2298 try {
2299 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
2300 r.bind("127.0.0.1", 32768); // <-- You are here
2301 // ... other socket I/O operations
2302 r.close();
2303 } catch (const randolf::rex::xEACCES& e) {
2304 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
2305 return EXIT_FAILURE;
2306 } catch (const randolf::rex::xALL& e) {
2307 std::cerr << "Socket exception: " << e.what() << std::endl;
2308 return EXIT_FAILURE;
2309 } catch (const std::exception& e) {
2310 std::cerr << "Other exception: " << e.what() << std::endl;
2311 return EXIT_FAILURE;
2312 }
2313 return EXIT_SUCCESS;
2314 } // -x- int main -x-
2315 @endcode
2316
2317 @note
2318 Specifying the IP address of @c 0.0.0.0 will bind to all IPv4 addresses that
2319 are assigned to all of the host machine's network interfaces with IPv4
2320 bindings.
2321 @n@n
2322 Specifying the IP address of @c 127.0.0.1 (localhost) is useful for serving
2323 only those applications that are running on the local host and use an IPv4
2324 socket to communicate.
2325
2326 <hr>
2327 @par IPv6 address notations (address family @c AF_INET6)
2328 Takes the form of @c x:x:x:x:x:x:x:x where each "x" represents a segment in
2329 the range of @c 0 through @c ffff in hexadecimal (e.g., `0:0:0:0:0:0:0:1`),
2330 or `x:x:x:x:x:x:y.y.y.y` where `y.y.y.y` represents an [unabbreviated] IPv4
2331 address (which merely replaces the last two IPv6 segments).
2332
2333 Under a proper implenetation of TCP/IP, an IPv6 address can be abbreviated
2334 to fewer segments by using a sequence of two colons (`::`) to indicate that
2335 the undefined segments are @c 0 (this abbreviation can only be used once,
2336 and may represent segments at the beginning or end, or anywhere in between).
2337 The following examples demonstrate this (an unabbreviated IPv6 address is
2338 included for completeness):
2339 - `0:0:0:0:0:0:0:1` may be abbreviated to `::1`
2340 - `0:0:0:0:0:0:2:1` may be abbreviated to `::2:1`
2341 - `8:0:0:0:0:0:2:1` may be abbreviated to `8::2:1`
2342 - `8:7:0:0:0:0:2:1` may be abbreviated to `8:7::2:1`
2343 - `8:7:0:0:0:0:0:0` may be abbreviated to `8:7::`
2344 - `8:0:0:0:0:0:0:0` may be abbreviated to `8::`
2345 - `8:7:6:5:4:3:2:1` is not abbreviated
2346
2347 @par Example of binding to IPv6 localhost
2348
2349 @code{.cpp}
2350 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2351 #include <randolf/rex>
2352 #include <randolf/rsocket>
2353
2354 int main(int argc, char *argv[]) {
2355 try {
2356 randolf::rsocket r(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
2357 r.bind("::1", 32768); // <-- You are here
2358 // ... other socket I/O operations
2359 r.close();
2360 } catch (const randolf::rex::xEACCES& e) {
2361 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
2362 return EXIT_FAILURE;
2363 } catch (const randolf::rex::xALL& e) {
2364 std::cerr << "Socket exception: " << e.what() << std::endl;
2365 return EXIT_FAILURE;
2366 } catch (const std::exception& e) {
2367 std::cerr << "Other exception: " << e.what() << std::endl;
2368 return EXIT_FAILURE;
2369 }
2370 return EXIT_SUCCESS;
2371 } // -x- int main -x-
2372 @endcode
2373
2374 @note
2375 Specifying the IP address of @c :: will bind to all IPv6 addresses that are
2376 assigned to all of the host machine's network interfaces with IPv6 bindings.
2377 @n@n
2378 Specifying the IP address of @c ::1 (localhost) is useful for serving only
2379 those applications that are running on the local host and use an IPv6 socket
2380 to communicate.
2381
2382 <hr>
2383 @par Interface address notation (address families @c AF_INET and @c AF_INET6)
2384 Takes the form of @c if=name where "name" represents the name of a local
2385 network interface.
2386
2387 Interface binding is useful when binding to interfaces that aren't configured
2388 with a static IP address because they were dymamically configured via the
2389 Dynamic Host Configuration Protocol (DHCP) or Stateless Address Configuration
2390 (SLAAC), or the configuration was changed manually by an administrator and
2391 you don't want your software to handle such changes gracefully, even if the
2392 new IP address is within the scope of an entirely different CIDR. (Changing
2393 between the IPv4 and IPv6 addresses is not supported, however, due to the
2394 fundamental differences between these two address families that includes
2395 differences beyond that of IP address format, although under a proper
2396 implementation of TCP/IP the binding should survive such changes when the IP
2397 address is reverted to the initial IP address family of the bound interface.)
2398
2399 Examples of interfaces include:
2400 - `if=lo` typical for localhost virtual network adapter
2401 - `if=bond0` typical for the first bonded virtual network adapter (used in
2402 failover and load-balancing network configurations)
2403 - `if=br0` typical for the first bridge virtual network adapter
2404 - `if=eth0` typical for the first ethernet network adapter
2405 - `if=tap0` typical for the first virtual layer 2 ethernet switch (used by
2406 VPNs to extend a remote network into the local network stack)
2407 - `if=tun0` typical for the first virtual layer 3 ethernet tunnel (used by
2408 VPNs to provide access to a remote network)
2409 - `if=wlo1` typical for the first wireless network adapter
2410
2411 @par Example of binding to interface localhost
2412
2413 @code{.cpp}
2414 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2415 #include <randolf/rex>
2416 #include <randolf/rsocket>
2417
2418 int main(int argc, char *argv[]) {
2419 try {
2420 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
2421 r.bind("if=lo", 32768); // <-- You are here
2422 // ... other socket I/O operations
2423 r.close();
2424 } catch (const randolf::rex::xEACCES& e) {
2425 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
2426 return EXIT_FAILURE;
2427 } catch (const randolf::rex::xALL& e) {
2428 std::cerr << "Socket exception: " << e.what() << std::endl;
2429 return EXIT_FAILURE;
2430 } catch (const std::exception& e) {
2431 std::cerr << "Other exception: " << e.what() << std::endl;
2432 return EXIT_FAILURE;
2433 }
2434 return EXIT_SUCCESS;
2435 } // -x- int main -x-
2436 @endcode
2437
2438 @note
2439 This is not a standard feature of the POSIX bind() function. This rsocket
2440 class uses the setsockopt() function behind-the-scenes to configure (enable)
2441 the @c SO_BINDTODEVICE option, and then the bind() function is called with
2442 the IPv4 address @c 0.0.0.0 if the underlying socket was initialized to the
2443 @c AF_INET family, or the IPv6 address @c :: if the underlying socket was
2444 initialized to the @c AF_INET6 family.
2445 @n@n
2446 Specifying the interface address of `if=lo` (localhost) is useful for serving
2447 only those applications that are running on the local host and use an IPv4 or
2448 IPv6 socket to communicate.
2449
2450 <hr>
2451 @par UNIX Domain Sockets address-notation (address family @c AF_UNIX)
2452 Takes the form of @c /path where "/path" represents an absolute path on the
2453 local file system (the path can also be relative, in which case it doesn't
2454 begin with a slash (`/`) character, but extra care is needed to ensure that
2455 the path will actually be at its expected location).
2456
2457 @par Example of binding to UNIX domain sockets
2458
2459 @code{.cpp}
2460 #include <iostream> // std::cout, std::cerr, std::endl, etc.
2461 #include <randolf/rex>
2462 #include <randolf/rsocket>
2463
2464 int main(int argc, char *argv[]) {
2465 try {
2466 randolf::rsocket r(AF_UNIX, SOCK_STREAM);
2467 r.bind("/var/run/rsocket-test-socket.tmp"); // <-- You are here
2468 // ... other socket I/O operations
2469 r.close();
2470 } catch (const randolf::rex::xEACCES& e) {
2471 std::cerr << "Socket bind-access exception: " << e.what() << std::endl;
2472 return EXIT_FAILURE;
2473 } catch (const randolf::rex::xALL& e) {
2474 std::cerr << "Socket exception: " << e.what() << std::endl;
2475 return EXIT_FAILURE;
2476 } catch (const std::exception& e) {
2477 std::cerr << "Other exception: " << e.what() << std::endl;
2478 return EXIT_FAILURE;
2479 }
2480 return EXIT_SUCCESS;
2481 } // -x- int main -x-
2482 @endcode
2483
2484 @note
2485 Normal socket I/O functionality is used to exchange data with another process
2486 that can open the same UNIX domain socket (normally on the same host,
2487 although it may not be outside the realm of possibility for future Linux
2488 kernels to support UNIX domain sockets on remote hosts).
2489
2490 <hr>
2491 @throws randolf::rex::xEACCES If the port number is below or equal to 1024
2492 (or 1023 on some Operating Systems that test only for below 1024) in
2493 the absence of elevated access or the absence of a capability flag
2494 having been set
2495 @throws randolf::rex::xEACCES If binding to an interface on systems that
2496 require elevated access for direct interface binding in absence of
2497 said elevated access
2498 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
2499 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
2500 address is not available on any local interface
2501 @throws randolf::rex::xEBADF The underlying socket is not open
2502 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2503 part of the user address space
2504 @throws randolf::rex::xEINVAL Socket is already bound
2505 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
2506 valid for this socket's family (a.k.a., communication domain)
2507 @throws randolf::rex::xENOMEM Insufficient memory
2508 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2509 doesn't refer to a socket
2510 @n@n
2511 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
2512 @throws randolf::rex::xEACCES Write permission or search permission denied
2513 on a component of the path prefix (@c AF_UNIX family)
2514 @throws randolf::rex::xELOOP Too many symbolic links encountered while
2515 resolving address (@c AF_UNIX family)
2516 @throws randolf::rex::xENAMETOOLONG Address is too long (@c AF_UNIX family)
2517 @throws randolf::rex::xENOENT One of the path's directory components doesn't
2518 exist (@c AF_UNIX family)
2519 @throws randolf::rex::xENOTDIR One of the path's diretory components is not a
2520 directory (@c AF_UNIX family)
2521 @throws randolf::rex::xEROFS Read-only file system (@c AF_UNIX family)
2522
2523 @returns The same rsocket object so as to facilitate stacking
2524 @see accept()
2525 @see accept_sp()
2526 @see accept4()
2527 @see accept4_sp()
2528 @see bind(const struct sockaddr*, const socklen_t)
2529 @see connect
2530 @see listen
2531 @qualifier TLS
2532 *///=========================================================================
2533 rsocket& bind(
2534 /// IPv4 address, IPv6 address, hostname, UNIX domain sockets path, or specialized variant (see notes, above)
2535 const std::string& address,
2536 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
2537 const int port = 0) {
2538 if (__debug) debug("bind(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2539 + ", " + address
2540 + " port=" + std::to_string(port) + (port == 0 ? " (emphemeral or not-applicable)" : "")
2541 + ", size=" + std::to_string(__socket_addr_size)
2542 + ");");
2543
2544//std::cout << " address: " << address << std::endl;
2545 if (address.starts_with("if=")) { // Bind to interface
2546// TODO: Use family() to simplify this section of code
2547 struct ifreq ifr{};
2548 if (address.size() - 3 >= sizeof(ifr.ifr_name)) {} // TODO: Throw exception because ASCIIZ name will be too long
2549 address.copy(ifr.ifr_name, address.size() - 3, 3); // Copy address, excluding the "if=" portion
2550 __rc_check(::setsockopt(__socket_fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)));
2551// TODO: Possibly also add support for SO_BINDTOIFINDEX (perhaps only if "if=" begins with a number and only after SO_BINDTODEVICE fails?)
2552 // TODO: Figure out how to use INADDR_ANY instead of 0 or :: (assuming this is even possible)
2553 // TODO: Test IPv6 more (telnet doesn't seem to be able to connect, and continues to work on IPv4 address)
2554// __socket_addr = *mk_sockaddr_storage(__socket_addr.ss_family == AF_INET6 ? "::" : "0", port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_mptcp_flag && __socket_protocol == IPPROTO_MPTCP ? IPPROTO_TCP : __socket_protocol});
2555 addrinfo ai{0, __socket_addr->ss_family, __socket_type, __socket_mptcp_flag && __socket_protocol == IPPROTO_MPTCP ? IPPROTO_TCP : __socket_protocol};
2556 struct sockaddr_storage* sa = mk_sockaddr_storage(__socket_addr->ss_family == AF_INET6 ? "::" : "0", port, &ai);
2557 ::free((void*)__socket_addr);
2558 __socket_addr = sa;
2559 __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
2560// TODO: Figure out whether to use (sockaddr*)&__socket_addr or (sockaddr*)__socket_addr
2561// } else if (address.starts_with("if_cidr=")) { // Bind to interface based on matching CIDR
2562// // REMINDER: Update static family() method as well
2563// getifaddrs: https://man7.org/linux/man-pages/man3/getifaddrs.3.html
2564// } else if (address.starts_with("if_mac=")) { // Bind to interface based on its physical/hardware machine address
2565// // REMINDER: Update static family() method as well
2566// // Resolve interface name, and then just do what if= does (above)
2567
2568 } else {
2569// __socket_addr = *mk_sockaddr_storage(address, port, new addrinfo{0, __socket_addr.ss_family, __socket_type, __socket_mptcp_flag && __socket_protocol == IPPROTO_MPTCP ? IPPROTO_TCP : __socket_protocol});
2570 addrinfo ai{0, __socket_addr->ss_family, __socket_type, __socket_mptcp_flag && __socket_protocol == IPPROTO_MPTCP ? IPPROTO_TCP : __socket_protocol};
2571 struct sockaddr_storage* sa = mk_sockaddr_storage(address, port, &ai);
2572 ::free((void*)__socket_addr);
2573 __socket_addr = sa;
2574// TODO: Figure out whether to use (sockaddr*)&__socket_addr or (sockaddr*)__socket_addr
2575// __rc_check(::bind(__socket_fd, (sockaddr*)&__socket_addr, __socket_addr_size));
2576 __rc_check(::bind(__socket_fd, (sockaddr*)__socket_addr, __socket_addr_size));
2577// __rc_check(::bind(__socket_fd, (sockaddr*)&(__socket_addr = *mk_sockaddr_storage(address, port)), __socket_addr_size));
2578 } // -x- if address.starts_with -x-
2579
2580 return *this;
2581 } // -x- rsocket& bind -x-
2582
2583 /*======================================================================*//**
2584 @brief
2585 Override the default buffer size (typically 8,192 bytes) used by the various
2586 @ref recv() methods.
2587
2588 If resetting to the compiled-in default, use the buffer_size_reset() method
2589 instead of setting the value directly. This ensures that future versions,
2590 with a different compiled-in default, will be reset to the compiled-in value.
2591 @exception std::overflow_error If the new size exceeds the amount of data
2592 that's already present in the ring buffer
2593 @returns The same rsocket object so as to facilitate stacking
2594 @see buffer_size_reset()
2595 @see get_buffer_size()
2596 @qualifier TLS
2597 *///=========================================================================
2598 rsocket& buffer_size(
2599 /// Size of the new buffer (in bytes)
2600 const size_t nbytes) {
2601 __buffer_size = nbytes;
2602 if (__debug) debug("buffer_size(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2603 + " nbytes=" + std::to_string(nbytes)
2604 + ");");
2605 if (__buffer != nullptr) __buffer->set_size(nbytes);
2606 return *this;
2607 } // -x- rsocket& buffer_size -x-
2608
2609 /*======================================================================*//**
2610 @brief
2611 Reset the default buffer size (typically 8,192 bytes) used by the various
2612 @ref recv() methods.
2613
2614 This method is preferred for resetting to the compiled-in default instead of
2615 setting the value directly. This ensures that future versions, with a
2616 different compiled-in default, will be reset to the compiled-in value.
2617 @exception std::overflow_error If the new size exceeds the amount of data
2618 that's already present in the ring buffer
2619 @returns The same rsocket object so as to facilitate stacking
2620 @see buffer_size(const size_t nbytes)
2621 @see get_buffer_size()
2622 @qualifier TLS
2623 *///=========================================================================
2624 rsocket& buffer_size_reset() {
2625 __buffer_size = RSOCKET_BUFFER_SIZE;
2626 if (__debug) debug("buffer_size_reset(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2627 + " nbytes=" + std::to_string(RSOCKET_BUFFER_SIZE)
2628 + ");");
2629 if (__buffer != nullptr) __buffer->set_size(__buffer_size);
2630 return *this;
2631 } // -x- rsocket& buffer_size_reset -x-
2632
2633 //===========================================================================
2634 //
2635 // TODO: If family is AF_UNIX then also unlink the domain socket file.
2636 //
2637 /*======================================================================*//**
2638 @brief
2639 Close this rsocket. (If this rsocket was already closed, then calling this
2640 method additional times will have no effect, and will not cause exceptions to
2641 be thrown.)
2642 @warning
2643 This method may throw exceptions in some circumstances, which is extremely
2644 rare. If you prefer to close without having to contend with any exceptions
2645 (e.g., while calling close() from within a destructor), the close_passive()
2646 method will return an integer indicating success/failure instead of throwing
2647 an exception, and then you'll have to handle errno manually (which is useful
2648 in destructors because any error can merely be handled procedurally).
2649
2650 @throws randolf::rex::xEBADF The underlying socket is not open
2651 @throws randolf::rex::xEINTR Interrupted by a signal
2652 @throws randolf::rex::xEIO An I/O error occurred
2653
2654 @returns The same rsocket object so as to facilitate stacking
2655 @see is_closed()
2656 @qualifier POSIX
2657 @qualifier TLS
2658 *///=========================================================================
2659 rsocket& close() {
2660 if (__socket_open) {
2661 __rc_check(::close(__socket_fd));
2662 __socket_open = false;
2663 } // -x- if __socket_open -x-
2664 return *this;
2665 } // -x- rsocket& close -x-
2666
2667 /*======================================================================*//**
2668 @brief
2669 Close this rsocket without throwing any exceptions (an error code is returned
2670 instead, which is useful while calling close_passive() from within a
2671 destructor).
2672 @returns 0 = success
2673 @returns -1 = error (errno will be set accordingly)
2674 @see is_closed()
2675 @qualifier TLS
2676 *///=========================================================================
2677 int close_passive() noexcept {
2678 if (__socket_open) {
2679 int rc = ::close(__socket_fd);
2680 if (rc == 0) __socket_open = false;
2681 return rc;
2682 } // -x- if __socket_open -x-
2683 return 0; // Indicate success (because the socket was previously closed successfully)
2684 } // -x- int close_passive -x-
2685
2686 /*======================================================================*//**
2687 @brief
2688 Connect this socket to a specific endpoint (which may differ from this
2689 rsocket's address that was previously configured by the @ref bind() method).
2690
2691 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
2692 @throws randolf::rex::xEADDRNOTAVAIL No ephemeral ports are available for
2693 assignment to unbound socket
2694 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
2695 @throws randolf::rex::xEAGAIN Insufficient entries in the routing cache
2696 @throws randolf::rex::xEALREADY Previous connection attempt not completed on
2697 nonblocking socket
2698 @throws randolf::rex::xEBADF The underlying socket is not open
2699 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
2700 connections
2701 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
2702 part of the user address space
2703 @throws randolf::rex::xEINPROGRESS Connection cannot be completed immediately
2704 on nonblocking socket (@c AF_UNIX family fails with xEAGAIN instead)
2705 @throws randolf::rex::xEINTR Interrupted by a signal
2706 @throws randolf::rex::xEISCONN Socket is already connected
2707 @throws randolf::rex::xENETUNREACH No route to network
2708 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
2709 doesn't refer to a socket
2710 @throws randolf::rex::xEPERM Connection forbidden/denied by firewall rules
2711 @throws randolf::rex::xEPERM Can't connect to a broadcast address when the
2712 socket's broadcast flag isn't enabled
2713 @throws randolf::rex::xEPROTOTYPE Socket type doesn't support the requested
2714 communications protocol (e.g., connecting a UNIX domain socket of
2715 type @c SOCK_STREAM to an endpoint expecting type @c SOCK_DGRAM)
2716 @throws randolf::rex::xETIMEDOUT Timeout period elapsed
2717 @n@n
2718 <b>The following exceptions are specific to AF_UNIX family sockets...</b>
2719 @throws randolf::rex::xEACCES Write permission or search permission denied
2720 on a component of the path prefix (@c AF_UNIX family)
2721 @throws randolf::rex::xEAGAIN Connection cannot be completed immediately on
2722 nonblocking socket (@c AF_UNIX family)
2723
2724 @returns The same rsocket object so as to facilitate stacking
2725 @see tls_do_handshake()
2726 @qualifier POSIX
2727 @qualifier TLS
2728 *///=========================================================================
2729 rsocket& connect(
2730 /// IPv4 address, IPv6 address, hostname, or UNIX domain sockets path
2731 const std::string& address,
2732 /// TCP or UDP port number ranging 1-65535 (0 = ephemeral port; a.k.a., automatic assignment)
2733 const int port = 0) {
2734 if (__debug) debug("connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2735 + ", " + address
2736 + " (port=" + std::to_string(port) + ")"
2737 + ", " + std::to_string(__socket_addr_size)
2738 + ");");
2739
2740 addrinfo ai{0, __socket_addr->ss_family, __socket_type, __socket_mptcp_flag && __socket_protocol == IPPROTO_MPTCP ? IPPROTO_TCP : __socket_protocol};
2741 struct sockaddr_storage* sa = mk_sockaddr_storage(address, port, &ai);
2742 ::free((void*)__socket_addr); // Free before re-assigning new pointer
2743 __socket_addr = sa;
2744
2745 __rc_check(::connect(__socket_fd, (sockaddr*)__socket_addr, __socket_addr_size), 0, rex::rex::REX_FLAGS::REX_ALT_EAGAIN);
2746 __socket_connected = true;
2747
2748 // --------------------------------------------------------------------------
2749 // TLS.
2750 // --------------------------------------------------------------------------
2751 if (__tls) {
2752 if (__debug) debug("tls_connect(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
2753 + ");");
2754 __rc_check_tls(SSL_connect(__tls_fd));
2755 } // -x- if __tls -x-
2756
2757 return *this;
2758 } // -x- rsocket& connect -x-
2759
2760 /*======================================================================*//**
2761 @brief
2762 Find out whether debug mode is enabled.
2763
2764 @par Threads
2765 This method is threadsafe.
2766 @returns TRUE = enabled
2767 @returns FALSE = not enabled
2768 @see net_io
2769 @qualifier TLS
2770 *///=========================================================================
2771 bool debug() noexcept {
2772 return __debug;
2773 } // -x- bool debug -x-
2774
2775 /*======================================================================*//**
2776 @brief
2777 Debug mode. When debug mode is enabled, output is sent to stderr by default,
2778 unless a second parameter specifies a different file handle (e.g., stdout, or
2779 even a socket).
2780
2781 debug(...) returns -1 if fd can't be written to (errno will be set in
2782 accordance with std::fprintf's behaviour since std::fprintf is used for
2783 writing debug output). Normally we'd throw an exception for such errors, but
2784 with debug is a special case because debugging needs to be as quick and
2785 convenient as possible for developers.
2786
2787 debug(...) returns -2 if writing to stderr failed when attempting to announce
2788 that fd can't be written to (as described above).
2789
2790 debug(...) returns -3 if some other error occurs (e.g., internal formatting
2791 error {which should not happen} or memory allocation failed, in which case
2792 there's a more serious problem that will be causing other problems elsewhere
2793 anyway).
2794
2795 Developers may add their own debug messages by using debug(std::string),
2796 which will only be written out if debug mode is enabled. This same method is
2797 used internally, so messages will be indistinguishable from developer's
2798 messages.
2799
2800 @par Threads
2801 This method is thread-safe.
2802 @returns 0 = success
2803 @returns -1 = error writing to stream (errno will be set accordingly)
2804 @returns -2 = error writing to stderr (errno will be set accordingly)
2805 @returns -3 = error (errno will be set accordingly)
2806 @see net_io
2807 @qualifier TLS
2808 *///=========================================================================
2809 int debug(
2810 /// TRUE = enable debug mode@n
2811 /// FALSE = disable debug mode (does not close any file handles)
2812 const bool debug_flag,
2813 /// File descriptor/handle to use for debug output
2814 std::FILE* fd = stderr) noexcept {
2815
2816 int rc = debug_fd(fd); // Attempt to change debug handle
2817 if (rc != 0) return rc; // Return error id new debug handle failed
2818 time_t tm = std::time(nullptr);
2819 std::string dt(27, 0);
2820 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2821 try {
2822 if (debug_flag || __debug) // Show debug message if debug is already or if we're turning it on
2823 std::fprintf(__debug_fd,
2824 "[%s] %s: Debug mode is %s\n",
2825 dt.data(),
2826 __debug_prefix.data(),
2827 debug_flag ? "ON" : "OFF");
2828 } catch (const std::system_error& e) { // Error writing to fd
2829 try {
2830 std::fprintf(stderr,
2831 "[%s] %s: error writing to stream\n",
2832 dt.data(),
2833 __debug_prefix.data());
2834 } catch (const std::exception& e) {
2835 return -2;
2836 }
2837 return -1;
2838 } catch (const std::exception& e) {
2839 return -3;
2840 }
2841 __debug = debug_flag; // Save debug flag
2842
2843 return 0; // Indicate success (no errors)
2844 } // -x- int debug -x-
2845
2846 /*======================================================================*//**
2847 @brief
2848 Send the specified message as debug output (as long as debug mode is enabled;
2849 if disabled, no debug output will be sent).
2850 @returns 0 = success
2851 @returns -1 = error writing to stream (errno will be set accordingly)
2852 @returns -2 = error writing to stderr (errno will be set accordingly)
2853 @returns -3 = error (errno will be set accordingly)
2854 @see debug_prefix
2855 @see net_io
2856 @qualifier TLS
2857 *///=========================================================================
2858 int debug(
2859 /// Debug message as an ASCIIZ string
2860 const char* msg) noexcept {
2861 return __debug ? __debug_out(std::string(msg)) : 0;
2862 } // -x- int debug -x-
2863
2864 /*======================================================================*//**
2865 @copydoc debug(const char*)
2866 @qualifier TLS
2867 *///=========================================================================
2868 int debug(
2869 /// Debug message as an std::string object
2870 const std::string& msg) noexcept {
2871 return __debug ? __debug_out(msg) : 0;
2872 } // -x- int debug -x-
2873
2874 /*======================================================================*//**
2875 @brief
2876 Find out which file descriptor/handle is used for debug output.
2877 @returns file handle/descriptor currently used for debug output
2878 *///=========================================================================
2879 std::FILE* debug_fd() noexcept {
2880 return __debug_fd;
2881 } // Get debug-output file handle
2882
2883 /*======================================================================*//**
2884 @brief
2885 Specify a different file descriptor/handle to use for debug output
2886 @returns 0 = success
2887 @returns -1 = error writing to stream (errno will be set accordingly)
2888 @returns -2 = error writing to stderr (errno will be set accordingly)
2889 @returns -3 = error (errno will be set accordingly)
2890 *///=========================================================================
2891 int debug_fd(
2892 /// File descriptor/handle to use for debug output
2893 std::FILE* fd) noexcept { // Set debug-output file handle
2894 if (__debug_fd != fd) {
2895 debug("Current debug-output file handle unset"); // For this message, we only want to send this out if debug mode is enabled
2896 __debug_fd = fd; // Save new debug-output file handle
2897 return debug("New debug-output file handle set"); // For this message, we only want to send this out if debug mode is enabled
2898 } // -x- if ~fd -x-
2899 return 0; // Indicate success (no errors)
2900 } // -x- int debug_fd -x-
2901
2902 /*======================================================================*//**
2903 @brief
2904 Find out what the current prefix is set to that's used in debug output.
2905
2906 This may be set at any time, including before or after enabling or disabling
2907 debug mode.
2908 @returns debug prefix string
2909 @see debug
2910 @qualifier TLS
2911 *///=========================================================================
2912 std::string debug_prefix() noexcept {
2913 return __debug_prefix;
2914 } // -x- std::string debug_prefix -x-
2915
2916 /*======================================================================*//**
2917 @brief
2918 Change the prefix used in debug output.
2919 @returns The same rsocket object so as to facilitate stacking
2920 @see debug
2921 @qualifier TLS
2922 *///=========================================================================
2923 rsocket& debug_prefix(
2924 /// New debug prefix string
2925 const std::string& prefix) noexcept {
2926 __debug_prefix = prefix;
2927 return *this;
2928 } // -x- rsocket& debug_prefix -x-
2929
2930 private:
2931 /*----------------------------------------------------------------------*//**
2932 @brief
2933 Internal debug-output function. This doesn't check __debug flag because it's
2934 expected that the normal debug() methods are taking care of this.
2935 @returns 0 = success
2936 @returns -1 = error writing to stream (errno will be set accordingly)
2937 @returns -2 = error writing to stderr (errno will be set accordingly)
2938 @returns -3 = error (errno will be set accordingly)
2939 @see debug
2940 @see debug_prefix
2941 *///-------------------------------------------------------------------------
2942 int __debug_out(
2943 /// Debugging message
2944 const std::string& msg) noexcept {
2945
2946 time_t tm = std::time(nullptr);
2947 std::string dt(27, 0);
2948 dt.resize(std::strftime(dt.data(), dt.size(), "%Y-%b-%d %T %z", std::localtime(&tm)));
2949 try {
2950 std::fprintf(__debug_fd,
2951 "[%s] %s: %s\n",
2952 dt.data(),
2953 __debug_prefix.data(),
2954 msg.data());
2955 } catch (const std::system_error& e) { // Error writing to fd
2956 try {
2957 std::fprintf(stderr,
2958 "[%s] %s: error writing to stream\n",
2959 dt.data(),
2960 __debug_prefix.data());
2961 } catch (const std::exception& e) {
2962 return -2;
2963 }
2964 return -1;
2965 } catch (const std::exception& e) {
2966 return -3;
2967 }
2968
2969 return 0; // Indicate success (no errors)
2970 } // -x- int __debug_out -x-
2971
2972 // --------------------------------------------------------------------------
2973 // These are specialized internal debug output methods for presenting socket
2974 // options when they are get or set. These methods are marked as "private"
2975 // because they are really aren't useful outside of internal-use contexts.
2976 //
2977 // @par Threads
2978 // Some of these methods are thread-safe.
2979 //
2980 // These methods are thread-safe when they don't reference structures (e.g.,
2981 // integer {int}, unsigned integer {u_int}, and unsigned character {u_char}).
2982 //
2983 // These methods are not necessarily thread-safe if they reference structures
2984 // unless those structures are defined with std::atomic, or they were
2985 // constructed on-the-fly as anonymous parameters.
2986 // --------------------------------------------------------------------------
2987 int __debug_sockopt(
2988 /// Name of function or method
2989 const std::string& func,
2990 /// The level at which the option resides; typically @c SOL_SOCKET
2991 const int level,
2992 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
2993 const int option) {
2994 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
2995 + ", " + std::to_string(level)
2996 + ", " + std::to_string(option)
2997 + ", <zero>"
2998 + ", size=0"
2999 + ");");
3000 } // -x- int __debug_sockopt -x-
3001 int __debug_sockopt(
3002 /// Name of function or method
3003 const std::string& func,
3004 /// The level at which the option resides; typically @c SOL_SOCKET
3005 const int level,
3006 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3007 const int option,
3008 /// The structure that this socket option will be set to
3009 const int value) {
3010 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3011 + ", " + std::to_string(level)
3012 + ", " + std::to_string(option)
3013 + ", " + std::to_string(value)
3014 + ", size=" + std::to_string(sizeof(value))
3015 + ");");
3016 } // -x- int __debug_sockopt -x-
3017 int __debug_sockopt(
3018 /// Name of function or method
3019 const std::string& func,
3020 /// The level at which the option resides; typically @c SOL_SOCKET
3021 const int level,
3022 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3023 const int option,
3024 /// The structure that this socket option will be set to
3025 const u_int value) {
3026 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3027 + ", " + std::to_string(level)
3028 + ", " + std::to_string(option)
3029 + ", u_int{" + std::to_string(value) + "}"
3030 + ", size=" + std::to_string(sizeof(value))
3031 + ");");
3032 } // -x- int __debug_sockopt -x-
3033 int __debug_sockopt(
3034 /// Name of function or method
3035 const std::string& func,
3036 /// The level at which the option resides; typically @c SOL_SOCKET
3037 const int level,
3038 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3039 const int option,
3040 /// The structure that this socket option will be set to
3041 const u_char value) {
3042 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3043 + ", " + std::to_string(level)
3044 + ", " + std::to_string(option)
3045 + ", u_char{" + std::to_string(value) + "}"
3046 + ", size=" + std::to_string(sizeof(value))
3047 + ");");
3048 } // -x- int __debug_sockopt -x-
3049 int __debug_sockopt(
3050 /// Name of function or method
3051 const std::string& func,
3052 /// The level at which the option resides; typically @c SOL_SOCKET
3053 const int level,
3054 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3055 const int option,
3056 /// The structure that this socket option will be set to
3057 const linger* value) {
3058 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3059 + ", " + std::to_string(level)
3060 + ", " + std::to_string(option)
3061 + ", linger{l_onoff=" + std::to_string(value->l_onoff)
3062 + ", l_linger=" + std::to_string(value->l_linger) + "}"
3063 + ", " + std::to_string(sizeof(value))
3064 + ");");
3065 } // -x- int __debug_sockopt -x-
3066 int __debug_sockopt(
3067 /// Name of function or method
3068 const std::string& func,
3069 /// The level at which the option resides; typically @c SOL_SOCKET
3070 const int level,
3071 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3072 const int option,
3073 /// The structure that this socket option will be set to
3074 const timeval* value) {
3075 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3076 + ", " + std::to_string(level)
3077 + ", " + std::to_string(option)
3078 + ", timeval{tv_sec=" + std::to_string(value->tv_sec)
3079 + ", tv_usec=" + std::to_string(value->tv_usec) + "}"
3080 + ", " + std::to_string(sizeof(value))
3081 + ");");
3082 } // -x- int __debug_sockopt -x-
3083 int __debug_sockopt(
3084 /// Name of function or method
3085 const std::string& func,
3086 /// The level at which the option resides; typically @c SOL_SOCKET
3087 const int level,
3088 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3089 const int option,
3090 /// The structure that this socket option will be set to
3091 const in_addr* value) {
3092 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3093 + ", " + std::to_string(level)
3094 + ", " + std::to_string(option)
3095 + ", in_addr{" + std::to_string(value->s_addr) + "}"
3096 + ", " + std::to_string(sizeof(value))
3097 + ");");
3098 } // -x- int __debug_sockopt -x-
3099 int __debug_sockopt(
3100 /// Name of function or method
3101 const std::string& func,
3102 /// The level at which the option resides; typically @c SOL_SOCKET
3103 const int level,
3104 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3105 const int option,
3106 /// The structure that this socket option will be set to
3107 const ip_mreq* value) {
3108 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3109 + ", " + std::to_string(level)
3110 + ", " + std::to_string(option)
3111 + ", ip_mreq{addr=" + std::to_string(value->imr_multiaddr.s_addr)
3112 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
3113 + ", " + std::to_string(sizeof(value))
3114 + ");");
3115 } // -x- int __debug_sockopt -x-
3116 int __debug_sockopt(
3117 /// Name of function or method
3118 const std::string& func,
3119 /// The level at which the option resides; typically @c SOL_SOCKET
3120 const int level,
3121 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3122 const int option,
3123 /// The structure that this socket option will be set to
3124 const ip_mreq_source* value) {
3125 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3126 + ", " + std::to_string(level)
3127 + ", " + std::to_string(option)
3128 + ", ip_mreq_source{addr=" + std::to_string(value->imr_multiaddr.s_addr)
3129 + ", source=" + std::to_string(value->imr_sourceaddr.s_addr)
3130 + ", iface=" + std::to_string(value->imr_interface.s_addr) + "}"
3131 + ", " + std::to_string(sizeof(value))
3132 + ");");
3133 } // -x- int __debug_sockopt -x-
3134 int __debug_sockopt(
3135 /// Name of function or method
3136 const std::string& func,
3137 /// The level at which the option resides; typically @c SOL_SOCKET
3138 const int level,
3139 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3140 const int option,
3141 /// The structure that this socket option will be set to
3142 const ip_mreqn* value) {
3143 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3144 + ", " + std::to_string(level)
3145 + ", " + std::to_string(option)
3146 + ", ip_mreqn{multiaddr=" + std::to_string(value->imr_multiaddr.s_addr)
3147 + ", address=" + std::to_string(value->imr_address.s_addr) + "}"
3148 + ", ifindex=" + std::to_string(value->imr_ifindex) + "}"
3149 + ", " + std::to_string(sizeof(value))
3150 + ");");
3151 } // -x- int __debug_sockopt -x-
3152 int __debug_sockopt(
3153 /// Name of function or method
3154 const std::string& func,
3155 /// The level at which the option resides; typically @c SOL_SOCKET
3156 const int level,
3157 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3158 const int option,
3159 /// The structure that this socket option will be set to
3160 const icmp6_filter* value) {
3161 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3162 + ", " + std::to_string(level)
3163 + ", " + std::to_string(option)
3164 + ", icmp6_filter{[0]=" + std::to_string(value->icmp6_filt[0])
3165 + ", [1]=" + std::to_string(value->icmp6_filt[1])
3166 + ", [2]=" + std::to_string(value->icmp6_filt[2])
3167 + ", [3]=" + std::to_string(value->icmp6_filt[3])
3168 + ", [4]=" + std::to_string(value->icmp6_filt[4])
3169 + ", [5]=" + std::to_string(value->icmp6_filt[5])
3170 + ", [6]=" + std::to_string(value->icmp6_filt[6])
3171 + ", [7]=" + std::to_string(value->icmp6_filt[7]) + "}"
3172 + ", " + std::to_string(sizeof(value))
3173 + ");");
3174 } // -x- int __debug_sockopt -x-
3175 int __debug_sockopt(
3176 /// Name of function or method
3177 const std::string& func,
3178 /// The level at which the option resides; typically @c SOL_SOCKET
3179 const int level,
3180 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3181 const int option,
3182 /// The structure that this socket option will be set to
3183 const sockaddr_in6* value) {
3184 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3185 + ", " + std::to_string(level)
3186 + ", " + std::to_string(option)
3187 + ", sockaddr_in6{family=" + std::to_string(value->sin6_family)
3188 + ", port=" + std::to_string(value->sin6_port)
3189 + ", flowinfo=" + std::to_string(value->sin6_flowinfo)
3190 + ", addr=" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[0])
3191 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[1])
3192 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[2])
3193 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[3])
3194 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[4])
3195 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[5])
3196 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[6])
3197 + ":" + randolf::rtools::to_hex(value->sin6_addr.__in6_u.__u6_addr16[7])
3198 + ", scope_id=" + std::to_string(value->sin6_scope_id) + "}"
3199 + ", " + std::to_string(sizeof(value))
3200 + ");");
3201 } // -x- int __debug_sockopt -x-
3202 int __debug_sockopt(
3203 /// Name of function or method
3204 const std::string& func,
3205 /// The level at which the option resides; typically @c SOL_SOCKET
3206 const int level,
3207 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3208 const int option,
3209 /// The structure that this socket option will be set to
3210 const ip6_mtuinfo* value) {
3211 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3212 + ", " + std::to_string(level)
3213 + ", " + std::to_string(option)
3214 + ", ip6_mtuinfo{addr=" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[0])
3215 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[1])
3216 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[2])
3217 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[3])
3218 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[4])
3219 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[5])
3220 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[6])
3221 + ":" + randolf::rtools::to_hex(value->ip6m_addr.sin6_addr.__in6_u.__u6_addr16[7])
3222 + ", mtu=" + std::to_string(value->ip6m_mtu) + "}"
3223 + ", " + std::to_string(sizeof(value))
3224 + ");");
3225 } // -x- int __debug_sockopt -x-
3226 int __debug_sockopt(
3227 /// Name of function or method
3228 const std::string& func,
3229 /// The level at which the option resides; typically @c SOL_SOCKET
3230 const int level,
3231 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3232 const int option,
3233 /// The structure that this socket option will be set to
3234 const ipv6_mreq* value) {
3235 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3236 + ", " + std::to_string(level)
3237 + ", " + std::to_string(option)
3238 + ", ipv6_mreq{addr=" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[0])
3239 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[1])
3240 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[2])
3241 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[3])
3242 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[4])
3243 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[5])
3244 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[6])
3245 + ":" + randolf::rtools::to_hex(value->ipv6mr_multiaddr.__in6_u.__u6_addr16[7])
3246 + ", iface=" + std::to_string(value->ipv6mr_interface) + "}"
3247 + ", " + std::to_string(sizeof(value))
3248 + ");");
3249 } // -x- int __debug_sockopt -x-
3250 int __debug_sockopt(
3251 /// Name of function or method
3252 const std::string& func,
3253 /// The level at which the option resides; typically @c SOL_SOCKET
3254 const int level,
3255 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3256 const int option,
3257 /// The structure that this socket option will be set to
3258 const group_req* value) {
3259 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3260 + ", " + std::to_string(level)
3261 + ", " + std::to_string(option)
3262 + ", group_req{iface=" + std::to_string(value->gr_interface)
3263 + ", <group_req>}"
3264 + ", " + std::to_string(sizeof(value))
3265 + ");");
3266 } // -x- int __debug_sockopt -x-
3267 int __debug_sockopt(
3268 /// Name of function or method
3269 const std::string& func,
3270 /// The level at which the option resides; typically @c SOL_SOCKET
3271 const int level,
3272 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3273 const int option,
3274 /// The structure that this socket option will be set to
3275 const group_source_req* value) {
3276 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3277 + ", " + std::to_string(level)
3278 + ", " + std::to_string(option)
3279 + ", group_source_req{iface=" + std::to_string(value->gsr_interface)
3280 + ", <group_source_req>}"
3281 + ", " + std::to_string(sizeof(value))
3282 + ");");
3283 } // -x- int __debug_sockopt -x-
3284 int __debug_sockopt_other(
3285 /// Name of function or method
3286 const std::string& func,
3287 /// The level at which the option resides; typically @c SOL_SOCKET
3288 const int level,
3289 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
3290 const int option,
3291 /// The structure that this socket option will be set to
3292 const socklen_t size) {
3293 return debug(func + randolf::rtools::to_hex(__socket_fd) + "}"
3294 + ", " + std::to_string(level)
3295 + ", " + std::to_string(option)
3296 + ", other=?"
3297 + ", " + std::to_string(size)
3298 + ");");
3299 } // -x- int __debug_sockopt_other -x-
3300
3301 public:
3302 /*======================================================================*//**
3303 @brief
3304 Discards the specified number of 8-bit bytes efficiently, and without closing
3305 the stream, and without consuming excessive quantities of memory.
3306 @exception randolf::rex::xERANGE An invalid value was specified for either
3307 the @c nbytes or @c memory_size parameter (e.g., a negative value,
3308 except for -1 with the @c nbytes parameter)
3309 @returns Number of bytes that were successfully discarded
3310 @see buffer_size
3311 @see pending
3312 @see recv
3313 @qualifier TLS
3314 *///=========================================================================
3315 int discard(
3316 /// Number of bytes to discard@n
3317 /// 0 = use internal @ref buffer_size() @n
3318 /// -1 = all remaining data waiting to be received (in other words, this
3319 /// method will repeatedly consume all data until encountering @ref eos)
3320 int nbytes,
3321 /// MSG_OOB@n
3322 /// MSG_PEEK (ignored, to prevent an endless loop)@n
3323 /// MSG_WAITALL@n
3324 /// MSG_DONTWAIT@n
3325 /// MSG_CMSG_CLOEXEC
3326 int posix_flags = 0,
3327 /// Maximum internal read size@n
3328 /// 0 = default to @ref buffer_size (when @c nbytes is larger than this size,
3329 /// multiple reads into this buffer will be utilized behind-the-scenes to
3330 /// consume the quantity of data specified by the @c nbytes parameter)
3331 const size_t memory_size = 0) {
3332 int buf_size = memory_size != 0 ? memory_size : __buffer_size;
3333 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3334 + ", " + std::to_string(nbytes)
3335 + ", " + std::to_string(posix_flags)
3336 + ", " + std::to_string(buf_size)
3337 + ");");
3338
3339 // --------------------------------------------------------------------------
3340 // Syntax checks.
3341 // --------------------------------------------------------------------------
3342 if (nbytes == 0) nbytes = __buffer_size;
3343 else if (nbytes < -1) throw randolf::rex::xERANGE( "nbytes parameter of " + std::to_string(nbytes) + " is below -1");
3344 if (memory_size < 0) throw randolf::rex::xERANGE("memory_size parameter of " + std::to_string(memory_size) + " is below 0");
3345
3346 // --------------------------------------------------------------------------
3347 // The MSG_PEEK flag is incompatible with this method, so we're turning it
3348 // off to prevent an endless loop. We're providing a debug message for this
3349 // in case someone's trying to track something down where they're using the
3350 // MSG_PEEK flag with a misunderstanding of the purpose of this method.
3351 // --------------------------------------------------------------------------
3352 if (posix_flags & MSG_PEEK) {
3353 posix_flags &= (~MSG_PEEK); // Remove MSG_PEEK flag if it was enabled
3354 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3355 + " ignoring incompatible MSG_PEEK flag"
3356 + ");");
3357 } // -x- if MSG_PEEK -x-
3358
3359 // --------------------------------------------------------------------------
3360 // Internal variables.
3361 // --------------------------------------------------------------------------
3362 int discarded = 0; // Total number of bytes discarded
3363 char buf[buf_size];
3364
3365 // --------------------------------------------------------------------------
3366 // Discard loop. We are intentionally handling nbytes in two different ways
3367 // hear with a little bit more code to benefit from the trade-off of faster
3368 // overall performance.
3369 // --------------------------------------------------------------------------
3370 try {
3371
3372 // --------------------------------------------------------------------------
3373 // Discard all remaining data waiting to be received.
3374 // --------------------------------------------------------------------------
3375 if (nbytes < 0) {
3376 while (!eos()) {
3377 discarded += __recv(buf, buf_size, posix_flags);
3378 } // -x- while n -x-
3379
3380 // --------------------------------------------------------------------------
3381 // Discard specific amount of data waiting to be received.
3382 // --------------------------------------------------------------------------
3383 } else {
3384 int n = 0;
3385 int inbytes = nbytes;
3386 while (inbytes > 0 && !eos()) {
3387 int n = __recv(buf, std::min(inbytes, buf_size), posix_flags);
3388 inbytes -= n;
3389 discarded += n;
3390 } // -x- while nbytes -x-
3391 } // -x- if nbytes -x-
3392
3393 // --------------------------------------------------------------------------
3394 // Ignore exception so that we can simply indicate how many bytes were
3395 // successfully received and discarded.
3396 // --------------------------------------------------------------------------
3397 } catch (const rex::xALL& e) {}
3398
3399 // --------------------------------------------------------------------------
3400 // Erase all data to prevent data from being unexpectedly exposed through any
3401 // other memory allocations later that use some or all of the same memory.
3402 // --------------------------------------------------------------------------
3403 //for (int i = std::min(discarded, buf_size); i > 0; i--) buf[i] = '\0';
3404 memset(buf, 0, std::min(discarded, buf_size));
3405
3406 return discarded;
3407 } // -x- int discard -x-
3408
3409 /*======================================================================*//**
3410 @brief
3411 Discards the specified number of 8-bit bytes efficiently, but stops upon
3412 encountering an EoL sequence (which will also be discarded; to find out which
3413 EoL sequence was consumed, use the @ref eol_consumed_seq method which will
3414 also return an empty string if no EoL sequence was consumed), and without
3415 closing the stream, and without consuming excessive quantities of memory.
3416
3417 This method is particularly useful for discarding lines that are too long,
3418 which, for example, with the return value makes it possible to optionally
3419 present a helpful error message to an end user and/or add details to an error
3420 log before continue receiving any subsequent lines.
3421
3422 @exception randolf::rex::xERANGE An invalid value was specified for either
3423 the @c nbytes or @c memory_size parameter (e.g., a negative value,
3424 except for -1 with the @c nbytes parameter)
3425 @returns Number of bytes that were successfully discarded
3426 @see buffer_size
3427 @see pending
3428 @see recv
3429 @qualifier TLS
3430 *///=========================================================================
3431 int discard_line(
3432 /// Number of bytes to discard@n
3433 /// 0 = use internal @ref buffer_size() @n
3434 /// -1 = all remaining data waiting to be received (in other words, this
3435 /// method will repeatedly consume all data until encountering an EoL
3436 /// sequence or once @ref eos is reached)
3437 int nbytes = -1,
3438 /// MSG_OOB@n
3439 /// MSG_PEEK (ignored, to prevent an endless loop)@n
3440 /// MSG_WAITALL@n
3441 /// MSG_DONTWAIT@n
3442 /// MSG_CMSG_CLOEXEC
3443 int posix_flags = 0,
3444 /// Line timeout (in seconds)@n
3445 /// 0 = no timeout (default), unless it was configured by way of the
3446 /// @ref timeout_recvline(long) method
3447 long timeout = 0,
3448 /// Maximum internal read size@n
3449 /// 0 = default to internal buffer's base buffer size (when @c nbytes is
3450 /// larger than this size, multiple reads into this buffer will be utilized
3451 /// behind-the-scenes to consume @c nbytes quantity of data)
3452 const size_t memory_size = 0) {
3453 int buf_size = memory_size != 0 ? memory_size : __buffer->get_base_size();
3454 if (__debug) debug("discard_line(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3455 + ", " + std::to_string(nbytes)
3456 + ", " + std::to_string(posix_flags)
3457 + ", " + std::to_string(buf_size)
3458 + ");");
3459
3460 // --------------------------------------------------------------------------
3461 // Syntax checks.
3462 // --------------------------------------------------------------------------
3463 bool infinite = nbytes == -1; // Keep track of whether nbytes was -1 before changing its value
3464 if (nbytes == 0) nbytes = __buffer_size;
3465 else if (nbytes == -1) nbytes = INT_MAX;
3466 else if (nbytes < -1) throw randolf::rex::xERANGE("nbytes parameter of " + std::to_string(nbytes) + " is below -1");
3467//std::cout << "buf_size=" << buf_size << std::endl;
3468
3469 // --------------------------------------------------------------------------
3470 // Syntax checks as part of preparation of timeout variable for faster
3471 // comparisons within line-reading loop.
3472 // --------------------------------------------------------------------------
3473 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
3474 if (timeout == 0) timeout = __recvline_timeout;
3475 timeout = timeout == 0 ? LONG_MAX : timeout + time(0);
3476
3477 // --------------------------------------------------------------------------
3478 // The MSG_PEEK flag is incompatible with this method, so we're turning it
3479 // off to prevent an endless loop. We're providing a debug message for this
3480 // in case someone's trying to track something down where they're using the
3481 // MSG_PEEK flag with a misunderstanding of the purpose of this method.
3482 // --------------------------------------------------------------------------
3483 if (posix_flags & MSG_PEEK) {
3484 posix_flags &= (~MSG_PEEK); // Remove MSG_PEEK flag if it was enabled
3485 if (__debug) debug("discard(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
3486 + " ignoring incompatible MSG_PEEK flag"
3487 + ");");
3488 } // -x- if MSG_PEEK -x-
3489
3490 // --------------------------------------------------------------------------
3491 // Internal variables.
3492 // --------------------------------------------------------------------------
3493 int discarded = 0; // Total number of bytes discarded
3494 int buf_size_eol = buf_size + 1;
3495 char buf[buf_size_eol];
3496// std::string buf;
3497// buf.resize(buf_size_eol); // Pre-allocate underlying array to prevent memory corruption when __recv writes directly into it, with +1 for straddled EoL
3498
3499 // --------------------------------------------------------------------------
3500 // Discard loop. We are intentionally handling nbytes in two different ways
3501 // hear with a little bit more code to benefit from the trade-off of faster
3502 // overall performance.
3503 // --------------------------------------------------------------------------
3504 try {
3505
3506 // --------------------------------------------------------------------------
3507 // Discard all remaining data waiting to be received.
3508 // --------------------------------------------------------------------------
3509 int len_with_eol = 0;
3510 int n = -1;
3511 if (infinite) {
3512 while (len_with_eol == 0 && n != 0 && !eos()) {
3513 n = __recv(buf, buf_size_eol, posix_flags | MSG_PEEK);
3514
3515 // --------------------------------------------------------------------------
3516 // Affect an actual discard of the correct amount of data. If "len" is -1,
3517 // then just discard everything because it means that we didn't encounter an
3518 // EoL sequence in this iteration.
3519 // --------------------------------------------------------------------------
3520 int len = eol_index(buf, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
3521//std::cout << "///// n=" << n << " len=" << len << " len_with_eol=" << len_with_eol << " buf_size_eol=" << buf_size_eol << std::endl;
3522 n = __recv(buf, len > 0 ? len_with_eol : n, posix_flags); // Consume data
3523//std::cout << " n=" << n << std::endl;
3524 discarded += n;
3525 } // -x- while n -x-
3526
3527 // --------------------------------------------------------------------------
3528 // Discard specific amount of data waiting to be received.
3529 // --------------------------------------------------------------------------
3530 } else {
3531 int inbytes = nbytes;
3532 while (len_with_eol == 0 && n != 0 && inbytes > 0 && !eos()) {
3533 n = __recv(buf, std::min(inbytes + 1, buf_size_eol), posix_flags | MSG_PEEK);
3534
3535 // --------------------------------------------------------------------------
3536 // Affect an actual discard of the correct amount of data. If "len" is -1,
3537 // then just discard everything because it means that we didn't encounter an
3538 // EoL sequence in this iteration.
3539 // --------------------------------------------------------------------------
3540 int len = eol_index(buf, &len_with_eol); // Measure length of string with EoL (-1 = no EoL found)
3541//std::cout << "||||| n=" << n << " len=" << len << " len_with_eol=" << len_with_eol << " buf_size_eol=" << buf_size_eol << std::endl;
3542 n = __recv(buf, len >= 0 ? len_with_eol : n, posix_flags); // Consume data
3543//std::cout << " n=" << n << std::endl;
3544 inbytes -= n;
3545 discarded += n;
3546 } // -x- while nbytes -x-
3547 } // -x- if nbytes -x-
3548
3549 // --------------------------------------------------------------------------
3550 // Ignore exception so that we can simply indicate how many bytes were
3551 // successfully received and discarded.
3552 // --------------------------------------------------------------------------
3553 } catch (const rex::xALL& e) {
3554//std::cout << "Exception: " << e.what() << std::endl;
3555 }
3556
3557//std::cout << "discarded=" << discarded << std::endl;
3558
3559 // --------------------------------------------------------------------------
3560 // Erase all data to prevent data from being unexpectedly leaked to any other
3561 // memory allocations later that use some or all of the same memory.
3562 //
3563 // Note: The +1 in the buffer size calcuation is to include a straddled EoL.
3564 // --------------------------------------------------------------------------
3565 //for (int i = std::min(discarded, buf_size); i > 0; i--) buf[i] = '\0';
3566 memset(buf, 0, std::min(discarded, buf_size));
3567
3568 return discarded;
3569 } // -x- int discard_line -x-
3570
3571 /*======================================================================*//**
3572 @brief
3573 Set EoL (End of Line) sequence. This sequence is used by recvline(),
3574 recv_rline(), sendline(), and related functions, and it defaults to an empty
3575 string which results in the EoL sequence being detected automatically
3576 on-the-fly.
3577
3578 - @c "" (empty string) = automatically detect on-the-fly (default)
3579 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
3580 - @c \\r (CR) = Carriage Return (typical for MacOS)
3581 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
3582
3583 @note
3584 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
3585 IMAP4, FINGER, NICNAME/WHOIS, etc.).
3586 @returns The same rsocket object so as to facilitate stacking
3587 @see eol_adoption
3588 @see eol_fix_printf
3589 @see get_eol
3590 @see printfline
3591 @see vprintfline
3592 @see recv_rline
3593 @see recvline
3594 @see sendline
3595 @see send_eol
3596 @qualifier TLS
3597 *///=========================================================================
3598 rsocket& eol(
3599 /// EoL sequence as an ASCIIZ string
3600 const char* eol) noexcept {
3601 __eol.assign(eol);
3602 __eol_out = __eol.empty() ? __CRLF : __eol;
3603 return *this;
3604 } // -x- rsocket& eol -x-
3605
3606 /*======================================================================*//**
3607 @brief
3608 Set EoL (End of Line) sequence. This sequence is used by recvline(),
3609 recv_rline(), sendline(), and related functions, and it defaults to an empty
3610 string which results in the EoL sequence being detected automatically
3611 on-the-fly.
3612
3613 - @c "" (empty string) = automatically detect on-the-fly (default)
3614 - @c \\r\\n (CRLF) = Carriage Return + Linefeed (typical for DOS)
3615 - @c \\r (CR) = Carriage Return (typical for MacOS)
3616 - @c \\n (LF) = Linefeed (typical for UNIX/Linux)
3617
3618 @note
3619 CRLF (@c \\r\\n) is used by most internet protocols (e.g., HTTP, SMTP, POP3,
3620 IMAP4, FINGER, NICNAME/WHOIS, etc.).
3621 @returns The same rsocket object so as to facilitate stacking
3622 @see eol_fix_printf
3623 @see get_eol
3624 @see printfline
3625 @see vprintfline
3626 @see recv_rline
3627 @see recvline
3628 @see sendline
3629 @see send_eol
3630 @qualifier TLS
3631 *///=========================================================================
3632 rsocket& eol(
3633 /// EoL sequence as an std::string object
3634 const std::string& eol) noexcept {
3635 __eol = eol;
3636 return *this;
3637 } // -x- rsocket& eol -x-
3638
3639 /*======================================================================*//**
3640 @brief
3641 Configure EoL adoption policy for the @ref recvline() and @ref recv_rline()
3642 methods. By default, @ref rsocket is configured with the EoL adoption policy
3643 enabled alongside an empty @ref eol() sequence, which results in the default
3644 operation being that the EoL sequence automatically gets detected and updated
3645 internally upon the first use of either the @ref recvline() or
3646 @ref recv_rline method.
3647
3648 The EoL adoption policy is only effective when the @ref eol() sequence is not
3649 defined (which is indicated by an empty EoL sequence string).
3650
3651 The EoL sequence is updated only when the EoL sequence string is empty, and
3652 when this EoL adoption policy is enabled.
3653 @returns The same rsocket object so as to facilitate stacking
3654 @see eol
3655 @see eol_index
3656 @see get_eol
3657 @see is_eol_adoption
3658 @see recv_rline
3659 @see recvline
3660 @see sendline
3661 @see send_eol
3662 @qualifier TLS
3663 *///=========================================================================
3664 rsocket& eol_adoption(
3665 /// TRUE = enable EoL adoption (default)@n
3666 /// FALSE = disable EoL adoption
3667 const bool flag) noexcept {
3668 __eol_adoption = flag;
3669 return *this;
3670 } // -x- rsocket& eol_adoption -x-
3671
3672 /*======================================================================*//**
3673 @brief
3674 Returns a String containing the EoL character sequence that was consumed by
3675 the most recent successful call to the @ref recvline() or @ref recv_rline
3676 method ("successful" in this context means that the received line was
3677 terminated by a valid EoL character sequence; otherwise the
3678 previous/unmodified value is returned).
3679 @warning
3680 This method must not be used to determine whether the @ref recvline() method
3681 successfully consumed an EoL character sequence.&nbsp; The reason for this is
3682 that @ref recvline() doesn't update this string when it doesn't consume an
3683 EoL character sequence, hence interpreting its results can (and likely will)
3684 lead to false positives.
3685 @returns EoL character sequence
3686 @see eol
3687 @see eol_adoption
3688 @see get_eol
3689 @see recv_rline
3690 @see recvline
3691 @qualifier TLS
3692 *///=========================================================================
3693 std::string eol_consumed_seq() noexcept {
3694 return __eol_consumed_seq;
3695 } // -x- std::string eol_consumed_seq -x-
3696
3697 /*======================================================================*//**
3698 @brief
3699 Configure EoL substitution policy for the @ref printf(), @ref printfline(),
3700 @ref vprintf(), and @ref vprintfline() methods. By default, @ref rsocket
3701 substitutes printf's @c \\n sequence with the EoL sequence (if defined), but
3702 this method can be used to disable this behaviour.
3703 @note
3704 The @c \\n sequence used in the printf format string normally coincides with
3705 the local Operating System's newline standard, which is very likely different
3706 from the @ref rsocket endpoint's newline standard, and/or the newline
3707 standard of the protocol being implemented. This policy setting makes it
3708 possible to control whether to use the configured EoL sequence when sending a
3709 formatted string to the endpoint.
3710 @returns The same rsocket object so as to facilitate stacking
3711 @see eol
3712 @see get_eol
3713 @see is_eol_fix_printf
3714 @see printf
3715 @see printfline
3716 @see vprintf
3717 @see vprintfline
3718 @qualifier TLS
3719 *///=========================================================================
3720 rsocket& eol_fix_printf(
3721 /// TRUE = enable EoL substitution (default)@n
3722 /// FALSE = disable EoL substitution
3723 const bool flag) noexcept {
3724 __eol_fix_printf = flag;
3725 return *this;
3726 } // -x- rsocket& eol_fix_printf -x-
3727
3728 /*======================================================================*//**
3729 @brief
3730 Finds the first instance of the EoL sequence and returns its offset (which is
3731 effectively the same as the size of the text, not including the characters
3732 that the EoL sequence is comprised of).
3733
3734 @note
3735 This method is specialized primarily for internal use by the @ref recvline()
3736 and @ref recv_rline() methods, but is made available here in case there's a
3737 need to check in-memory text using this rsocket's EoL detection policy.
3738 @returns Size of EoL sequence
3739 @returns -1 if EoL sequence wasn't found
3740 @see eol
3741 @see eol_adoption
3742 @see get_eol
3743 @qualifier TLS
3744 *///=========================================================================
3745 int eol_index(
3746 /// Buffer that probably contains at least one EoL sequence
3747 const std::string& buffer,
3748 /// Size of string with EoL character sequence included (will be updated by this method)
3749 int* with_eol_size) noexcept {
3750
3751 // --------------------------------------------------------------------------
3752 // An EoL character sequence was specified, so a simple search will suffice.
3753 // --------------------------------------------------------------------------
3754 if (!__eol.empty()) {
3755//std::cout << "!__eol.empty() ------------------------------ __eol_adoption=" << (__eol_adoption ? "yes" : "no") << " eol=" << randolf::rtools::to_hex(__eol) << std::endl;
3756 int pos = buffer.find(__eol);
3757 if (pos >= 0) {
3758 *with_eol_size = pos + __eol.size(); // Update size of EoL sequence
3759//std::cout << "------0------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3760 return pos;
3761 } // -x- if pos -x-
3762//std::cout << "------1------ pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3763 if (!__eol_adoption) return pos; // Return the -1 error if we're not adopting different EoF sequences on-the-fly
3764 } // -x- if !__eol.empty() -x-
3765//std::cout << "__eol.empty() ------------------------------ String is empty" << std::endl;
3766
3767 // --------------------------------------------------------------------------
3768 // Automatic detection of EoL sequence, so a more flexible approach will be
3769 // required.
3770 //
3771 // Search for all four possible EoL sequences (as indicated by an empty EoL
3772 // character sequence string):
3773 //
3774 // CRLF: Common for text lines with internet protocols such as SMTP, POP3,
3775 // IMAP4, HTTP, FINGER, WHOIS, etc. Also: DOS and OS/2 EoL sequence.
3776 //
3777 // CR: MacOS EoL character.
3778 //
3779 // LF: UNIX/Linux EoL character.
3780 //
3781 // LFCR: Extremely rare, but I've encountered multiple instances where the
3782 // intended CRLF was reversed to LFCR, possibly due to a programming
3783 // error or an artifact of inappropriate/unintended endian conversion.
3784 // --------------------------------------------------------------------------
3785 int pos = buffer.find(__CR); // CR or CRLF
3786//std::cout << "------2------ <CR> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3787 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)
3788 *with_eol_size = pos;// + 1;
3789// *with_eol_size = 1;
3790//std::cout << "------3------ <!CR> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3791 return pos;
3792 } else if (pos >= 0) { // EoL sequence found
3793 *with_eol_size = pos + (buffer[pos + 1] == __LF ? 2 : 1); // 2=CRLF, 1=CR
3794// *with_eol_size = buffer[pos + 1] == __LF ? 2 : 1; // 2=CRLF, 1=CR
3795 if (__eol_adoption) {
3796 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
3797 __eol.resize(*with_eol_size - pos); // Truncate extra characters
3798 } // -x- if __eol_adoption -x-
3799//std::cout << "------4------ <CR/CRLF-adopted> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3800 return pos; // Either way, we're done
3801 } // -x- if pos -x-
3802
3803 pos = buffer.find(__LF); // LF or LFCR
3804 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)
3805 *with_eol_size = pos;// + 1;
3806// *with_eol_size = 1;
3807//std::cout << "------5------ <LF> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3808 return pos;
3809 } else if (pos >= 0) { // EoL sequence found
3810 *with_eol_size = pos + (buffer[pos + 1] == __CR ? 2 : 1); // 2=LFCR, 1=LF
3811// *with_eol_size = buffer[pos + 1] == __CR ? 2 : 1; // 2=LFCR, 1=LF
3812 if (__eol_adoption) {
3813 __eol = buffer.substr(pos, *with_eol_size - pos); // Adopt EoL sequence
3814 __eol.resize(*with_eol_size - pos); // Truncate extra characters
3815 } // -x- if __eol_adoption -x-
3816//std::cout << "------6------ <LF/LFCR-adopted> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3817 return pos; // Either way, we're done
3818 } // -x- if pos -x-
3819
3820//std::cout << "------7------ <LF> pos=" << pos << " with_eol_size=" << *with_eol_size << " __eol.size()=" << __eol.size() << std::endl;
3821 return pos;
3822 } // -x- int eol_index -x-
3823
3824 /*======================================================================*//**
3825 @brief
3826 Find out if the stream is at its end and that this @ref rsocket's internal
3827 buffer (if one had been set up by the @ref recvline() method) is empty. This
3828 doesn't necessarily mean that the stream is closed; but rather that the
3829 endpoint just hasn't sent any more data (yet).
3830
3831 If the stream isn't open, then this method will always return @c true to
3832 implicitly indicate that there's no data pending to be received.
3833
3834 @pre
3835 You can optionally specify a timeout with the timeout parameter, which will
3836 cause this method to wait for the specified period of time for pending data.
3837
3838 It's better (more efficient) to use this method instead of the @c MSG_PEEK
3839 flag (with the various @c recv methods) to determine whether any data is
3840 waiting on the stream (e.g., data that's received by the sockets, but not by
3841 any @c recv methods yet) because this method is specialized in handling this
3842 particular condition and responds with an easy-to-use boolean flag.
3843
3844 @note
3845 EoS is an acronym for: End of Stream
3846
3847 @throws randolf::rex::xEBADF The underlying socket is not open
3848 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
3849 part of the user address space
3850 @throws randolf::rex::xEINTR Interrupted by a signal
3851 @throws randolf::rex::xENOMEM Insufficient memory
3852 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
3853 doesn't refer to a socket
3854 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
3855 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
3856 is a highly improbable chance that a timeout could still occur if the
3857 data is read by another thread before the `recv(..., MSG_PEEK)` call)
3858 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
3859 there's no new data
3860
3861 @returns TRUE = End of Stream (no buffered data, the stream @ref is_closed(),
3862 or there's no pending data, checked in this order)
3863 @returns FALSE = Pending data available
3864 @see is_closed
3865 @see is_open
3866 @see pending
3867 @see recv
3868 @qualifier TLS
3869 *///=========================================================================
3870 bool eos(
3871 /// Number of milliseconds to wait@n
3872 /// 0 = no waiting (default)
3873 const int timeout = 0) {
3874
3875 // --------------------------------------------------------------------------
3876 // Check internal buffer first, if there is one, and return immediately if
3877 // the buffer is not empty.
3878 // --------------------------------------------------------------------------
3879 if (__buffer != nullptr && !__buffer->empty()) return false;
3880
3881 // --------------------------------------------------------------------------
3882 // If socket is closed, then just indicate EoS to be done with it. No need
3883 // to fall through to timeouts as this is not applicable.
3884 // --------------------------------------------------------------------------
3885 if (!__socket_open) return true;
3886
3887 // --------------------------------------------------------------------------
3888 // Start by checking whether OpenSSL has pending data, and if it doesn't (or
3889 // there's an error, which is also indicated by 0) then fall through to
3890 // checking the underlying socket using ioctl's FIONREAD...
3891 //
3892 // Use ioctl's FIONREAD to determine if any data is waiting to be read. If
3893 // ioctl returns non-zero, then an error occurred which always equates to
3894 // EoS (otherwise the FIONREAD operation would have been successful).
3895 // --------------------------------------------------------------------------
3896 if (__tls && SSL_has_pending(__tls_fd) == 1) return false;
3897 int n = 0; // Number of bytes waiting (default to 0 in case of ioctl error)
3898 ioctl(__socket_fd, FIONREAD, &n); // Ignore return code because "n" will be zero if there's an error
3899 if (n > 0) return false; // Indicate !EoS because 0 bytes remaining means EoS
3900
3901 // --------------------------------------------------------------------------
3902 // Timeout polling (if a timeout was specified).
3903 // --------------------------------------------------------------------------
3904 if (timeout != 0) {
3905 try {
3906 poll(POLLIN, timeout); // This is calling internally, and not ::poll (POSIX) directly
3907 return false; // EoS, because the "poll" method was triggered by new data
3908 } catch (const randolf::rex::xETIMEDOUT& e) {
3909 return true; // EoS, because the "poll" method timed out while waiting for new data
3910 } // This is a specialized catch situation -- the ::recv function could still return an ETIMEDOUT error
3911 } // -x- if timeout -x-
3912
3913 return true; // EoS, because new data was not detected
3914 } // -x- bool eos -x-
3915
3916 /*======================================================================*//**
3917 @brief
3918 Find out what the specified IP address string's family (@c SO_DOMAIN) is.
3919 @throws randolf::rex::xEADDRNOTAVAIL The interface doesn't exist or the
3920 address is not available on any local interface
3921 @throws randolf::rex::xEAI_NONAME If @c address is an empty string
3922 @returns @c AF_UNSPEC (if family couldn't be determined)
3923 @returns @c AF_INET (IPv4 address)
3924 @returns @c AF_INET6 (IPv6 address)
3925 @returns @c AF_UNIX (UNIX Domain address)
3926 @returns ...or other family as applicable
3927 @see socket_family()
3928 @see socket_protocol()
3929 @see socket_type()
3930 @qualifier TLS
3931 *///=========================================================================
3932 int static family(
3933 /// Address, similar to @ref bind() addressing, including non-standard "if="
3934 /// variant that names a network interface
3935 const std::string& address,
3936 /// Preferred family to return first (used only with interface mode where the
3937 /// network interface is specified after the "if=" prefix); the default value
3938 /// of @c AF_UNSPEC will return the first family interface found
3939 const int preferred_family = AF_UNSPEC) {
3940
3941 // --------------------------------------------------------------------------
3942 // Simple checks first.
3943 // --------------------------------------------------------------------------
3944 if (address.size() == 0) randolf::rex::mk_exception("", EAI_NONAME);
3945 if (address.front() == '/') return AF_UNIX;
3946
3947 // --------------------------------------------------------------------------
3948 // if=<interface-name>: Same "interface" option that we support in bind()
3949 // --------------------------------------------------------------------------
3950 if (address.starts_with("if=")) {
3951 //std::cout << "address=" << address.substr(3).data() << std::endl; // Debug
3952 struct ifaddrs* ifaddr; // Chained interface addresses structure
3953 if (::getifaddrs(&ifaddr) == -1) { // Error occurred, so we're done here
3954 freeifaddrs(ifaddr); // Memory management
3955 randolf::rex::mk_exception("getifaddrs() error (prequisite to searching for " + address + ")", 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
3956 } // -x- if getifaddrs -x
3957
3958 // --------------------------------------------------------------------------
3959 // Iterate through interface addresses. Each address should have a different
3960 // address family/domain, and is never AF_UNSPEC (this should never happen
3961 // because it wouldn't make sense, but if the network implementation was
3962 // broken and including an AF_UNSPEC family/domain, it would be useless for
3963 // the purposes of calling the ::socket() function so our code will just
3964 // ignore it anyway.
3965 // --------------------------------------------------------------------------
3966 int first_family = AF_UNSPEC; // We're using AF_UNSPEC (0) to indicate that there isn't a family/domain
3967 for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
3968 if (ifa->ifa_addr == nullptr) continue; // No address structure, so this isn't useful to us at all
3969 if (ifa->ifa_addr->sa_family == AF_UNSPEC) continue; // Skip AF_UNSPEC family/domain, as we can't use this
3970 if (address.compare(3, -1, ifa->ifa_name) != 0) continue; // Not the interface name we're looking for
3971
3972 // --------------------------------------------------------------------------
3973 // Return current address's family if preferred_family is not specified (if
3974 // it's set to AF_UNSPEC {0}), or if it matches the current address.
3975 // --------------------------------------------------------------------------
3976 //std::cout << "if=" << ifa->ifa_name << " family=" << ifa->ifa_addr->sa_family << std::endl; // Debug
3977 if (preferred_family == AF_UNSPEC || preferred_family == ifa->ifa_addr->sa_family) {
3978 first_family = ifa->ifa_addr->sa_family; // Arbitrarily re-using first_family variable
3979 //freeifaddrs(ifaddr); // Memory management
3980 //return first_family; // Return current family/domain (re-used first_family variable)
3981 break;
3982 } // -x- if preferred_family -x-
3983
3984 // --------------------------------------------------------------------------
3985 // Keep track of only the first_family/domain that we found; if we can't find
3986 // the preferred_family, then we'll be able to return the first one we found
3987 // once the current loop is exhausted without finding a preferred familiy
3988 // (this is, in effect, a fallback).
3989 // --------------------------------------------------------------------------
3990 if (first_family == AF_UNSPEC && ifa->ifa_addr->sa_family != AF_UNSPEC)
3991 first_family = ifa->ifa_addr->sa_family;
3992 //std::cout << " first_family=" << first_family << std::endl; // Debug
3993 } // -x- for *ifa -x-
3994
3995 // --------------------------------------------------------------------------
3996 // Return first_family/domain (or throw an exception if it wasn't found).
3997 // --------------------------------------------------------------------------
3998 freeifaddrs(ifaddr); // Memory management
3999 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);
4000 return first_family;
4001
4002 } // -x- if /^if=/ -x-
4003
4004 // --------------------------------------------------------------------------
4005 // Attempt to detect IPv4 or IPv6 using a simple shortcut with ::inet_pton(),
4006 // which requires some additional memory allocation (that the simple checks
4007 // from earlier don't need).
4008 // --------------------------------------------------------------------------
4009 char buf[sizeof(struct in6_addr)]; // Binary address storage
4010 if (::inet_pton(AF_INET, address.data(), buf) == 1) return AF_INET;
4011 if (::inet_pton(AF_INET6, address.data(), buf) == 1) return AF_INET6;
4012
4013 // --------------------------------------------------------------------------
4014 // Throw xEAI_FAMILY exception because no family/domain was found.
4015 // --------------------------------------------------------------------------
4016 randolf::rex::mk_exception("No family/domain available for: " + address, EAI_FAMILY);
4017
4018 } // -x- int family -x-
4019
4020 /*======================================================================*//**
4021 @brief
4022 Find out what this rsocket's default listen backlog is.
4023 @returns The default listen backlog
4024 @see backlog
4025 @see listen
4026 @qualifier TLS
4027 *///=========================================================================
4028 int get_backlog() noexcept {
4029 return __socket_backlog;
4030 } // -x- int get_backlog -x-
4031
4032 /*======================================================================*//**
4033 @brief
4034 Find out what buffer size is used by the various recv() methods.
4035 @returns Buffer size (in bytes)
4036 @see buffer_size(const size_t nbytes)
4037 @see buffer_size_reset
4038 @see is_buffered
4039 @qualifier TLS
4040 *///=========================================================================
4041 size_t get_buffer_size() noexcept {
4042 return __buffer->get_size();
4043 } // -x- size_t get_buffer_size -x-
4044
4045 /*======================================================================*//**
4046 @brief
4047 Find out what the current EoL (End of Line) sequence is set to.
4048 @warning
4049 To send an EoL sequence do not use `send(r.eol())` because it may not be
4050 initialized yet and the endpoint you're sending to may seem unresponsive or
4051 other unexpected behaviour may occur.@n
4052 @n
4053 To send an EoL sequence properly, use @ref sendline(); although specifying no
4054 parameters is more efficient than specifying an empty string (@c ""), the
4055 specialized @ref send_eol() method is the most efficient option for sending
4056 an EoL sequence separately.
4057 @note
4058 An empty EoL sequence means that CRLF will be sent by @ref sendline(), and
4059 that @ref recvline(), and @ref recv_rline() will automatically detect from
4060 one of @c CR, @c CRLF, @c LF, and @c LFCR.
4061 @returns Current EoL sequence
4062 @see eol
4063 @see eol_adoption
4064 @see eol_consumed_seq
4065 @see eol_fix_printf
4066 @see eol_index
4067 @see printfline
4068 @see recvline
4069 @see recv_rline
4070 @see sendline
4071 @see send_eol
4072 @see vprintfline
4073 @qualifier TLS
4074 *///=========================================================================
4075 std::string get_eol() noexcept {
4076 return __eol;
4077 } // -x- std::string get_eol -x-
4078
4079 /*======================================================================*//**
4080 @brief
4081 Find out what this rsocket's name is.
4082
4083 The built-in SNI mechanism will overwrite this data to indicate the hostname
4084 that was specified by the TLS-encrypted endpoint that triggered an internal
4085 SNI callback -- use the @ref name_sni() method to find out which hostname is
4086 actually being used by TLS.
4087 @returns The name of this rsocket (or an empty @c std::string if this rsocket
4088 doesn't have a name)
4089 @see name
4090 @see name_sni
4091 @see tls_sni
4092 *///=========================================================================
4093 std::string get_name() noexcept {
4094 return __name;
4095 } // -x- std::string name -x-
4096
4097 /*======================================================================*//**
4098 @brief
4099 Find out what this rsocket's actual TLS SNI hostname is.
4100
4101 This is the exact - or closest-matching (in the case of wildcards) - hostname
4102 associated with an actual TLS certificate that was provided in the configured
4103 @ref rsocket_sni object.
4104 @returns The hostname associated with the TLS certificate selected by SNI
4105 @see get_name
4106 @see name
4107 @see tls_sni
4108 @see tls_sni_has_name
4109 @qualifier TLS
4110 *///=========================================================================
4111 std::string get_name_sni() noexcept {
4112 return __name_sni;
4113 } // -x- std::string get_name_sni -x-
4114
4115 /*======================================================================*//**
4116 @brief
4117 Get underlying socket family/domain constant (SO_DOMAIN).
4118 @returns socket family/domain constant
4119 @see family()
4120 @see port()
4121 @see socket()
4122 @see get_socket_fd()
4123 @see get_socket_protocol()
4124 @see get_socket_type()
4125 @qualifier TLS
4126 *///=========================================================================
4127 int get_socket_family() noexcept {
4128 return __socket_addr->ss_family;
4129 } // -x- int get_socket_family -x-
4130
4131 /*======================================================================*//**
4132 @brief
4133 Get underlying socket descriptor/handle.
4134 @returns socket descriptor/handle
4135 @returns 0 = socket not yet allocated
4136 @see port()
4137 @see socket()
4138 @see socket_fd()
4139 @see get_socket_family()
4140 @see get_socket_protocol()
4141 @see get_socket_type()
4142 @qualifier TLS
4143 *///=========================================================================
4144 int get_socket_fd() noexcept {
4145 return __socket_fd;
4146 } // -x- int get_socket_fd -x-
4147
4148 /*======================================================================*//**
4149 @brief
4150 Get underlying socket protocol constant (SO_PROTOCOL).
4151 @returns socket protocol constant
4152 @see port()
4153 @see socket()
4154 @see get_socket_family()
4155 @see get_socket_fd()
4156 @see get_socket_type()
4157 @qualifier TLS
4158 *///=========================================================================
4159 int get_socket_protocol() noexcept {
4160 return __socket_protocol;
4161 } // -x- int get_socket_protocol -x-
4162
4163 /*======================================================================*//**
4164 @brief
4165 Get underlying socket type constant (SO_TYPE).
4166 @returns socket type constant
4167 @see port()
4168 @see socket()
4169 @see get_socket_family()
4170 @see get_socket_fd()
4171 @see get_socket_protocol()
4172 @qualifier TLS
4173 *///=========================================================================
4174 int get_socket_type() noexcept {
4175 return __socket_type;
4176 } // -x- int get_socket_type -x-
4177
4178 /*======================================================================*//**
4179 @brief
4180 Obtain the receive (read) or send (write/transmit) timeout is set to on the
4181 underlying socket.
4182
4183 Since getting the read timeout is such a common operation, this specialized
4184 method was created to ease software development efforts; internally we're
4185 just calling getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO).
4186
4187 @throws randolf::rex::xEBADF The underlying socket is not open
4188 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4189 part of the user address space
4190 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
4191 valid for this socket's family (a.k.a., communication domain)
4192 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
4193 is not supported
4194 @throws randolf::rex::xENOTDIR If the @c direction parameter is supplied with
4195 an incorrect value (not @c SO_RCVTIMEO or @c SO_SNDTIMEO); this
4196 exception is normally a file-system related error, so we're using it
4197 here instead of EINVAL to make detecting this problem simpler for
4198 software developers (plus, "DIR" relates well to "direction"); this
4199 exception shouldn't need to be caught in the vast majority of uses,
4200 although one use where it should be caught is if the end-user has
4201 free reign to set the @c direction parameter to any value (e.g., a
4202 customizable software debugging interface)
4203 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4204 doesn't refer to a socket
4205
4206 @returns @c timeval socket option structure wrapped in std::unique_ptr
4207 @see timeout
4208 @see timeout_recvline
4209 @see recvline
4210 @see recv_rline
4211 @qualifier TLS
4212 *///=========================================================================
4213 std::unique_ptr<timeval> get_timeout(
4214 /// Direction:@n
4215 /// @c SO_RCVTIMEO (default)@n
4216 /// @c SO_SNDTIMEO
4217 const int direction = SO_RCVTIMEO) {
4218
4219 // --------------------------------------------------------------------------
4220 // Get timeout for the specified direction.
4221 // --------------------------------------------------------------------------
4222 if (direction == SO_RCVTIMEO || direction == SO_SNDTIMEO) {
4223 return getsockopt_timeval(SOL_SOCKET, direction); // TODO: Does this need to be wrapped up in std::shared_ptr?
4224 } // -x- if direction -x-
4225
4226 // --------------------------------------------------------------------------
4227 // Throw exception because direction is invalid.
4228 // --------------------------------------------------------------------------
4229 throw randolf::rex::xENOTDIR("invalid timeout direction (must be SO_RCVTIMEO or SO_SNDTIMEO)");
4230
4231 } // -x- std::unique_ptr<timeval> get_timeout -x-
4232
4233 /*======================================================================*//**
4234 @brief
4235 Find out what the read timeout is set to when using the @ref recvline()
4236 method.
4237
4238 @returns @c long value (0 = no timeout)
4239 @see get_timeout
4240 @see recvline
4241 @see recv_rline
4242 @see timeout
4243 @see timeout_recvline(long)
4244 @qualifier TLS
4245 *///=========================================================================
4246 long get_timeout_recvline() {
4247 return __recvline_timeout;
4248 } // -x- long get_timeout_recvline -x-
4249
4250 /*======================================================================*//**
4251 @brief
4252 Get peer name returns the address of the socket as a sockaddr_storage
4253 structure.
4254
4255 The resulting structure is wrapped in std::unique_ptr (a C++ smart pointer
4256 that aids in the prevention of resource leaks).
4257
4258 @throws randolf::rex::xEBADF The underlying socket is not open
4259 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4260 part of the user address space
4261 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
4262 valid for this socket's family (a.k.a., communication domain)
4263 @throws randolf::rex::xENOBUFS Insufficient memory
4264 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4265 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4266 doesn't refer to a socket
4267
4268 @returns sockaddr_storage structure
4269 @qualifier POSIX
4270 @qualifier TLS
4271 *///=========================================================================
4272 std::unique_ptr<sockaddr_storage> getpeername() {
4273 std::unique_ptr sa = std::make_unique_for_overwrite<sockaddr_storage>();
4274 socklen_t len = sizeof(sockaddr_storage);
4275 __rc_check(::getpeername(__socket_fd, (struct sockaddr*)sa.get(), &len));
4276 return sa;
4277 } // -x- std::unique_ptr<sockaddr_storage> getpeername -x-
4278
4279 /*======================================================================*//**
4280 @brief
4281 Get peer name returns the address of the socket as a std::string object.
4282
4283 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
4284 (e.g., because the @c family doesn't utilize or support an address
4285 {or the format isn't known}
4286 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
4287 @throws randolf::rex::xEBADF The underlying socket is not open
4288 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4289 part of the user address space
4290 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
4291 valid for this socket's family (a.k.a., communication domain)
4292 @throws randolf::rex::xENOBUFS Insufficient memory
4293 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
4294 @throws randolf::rex::xENOTCONN Underlying socket is not connected
4295 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4296 doesn't refer to a socket
4297
4298 @returns string representation of peer name
4299 @qualifier TLS
4300 *///=========================================================================
4301 std::string getpeername_ntop() {
4302 sockaddr_storage sa;
4303 socklen_t len = sizeof(sockaddr_storage);
4304 __rc_check(::getpeername(__socket_fd, (sockaddr*)&sa, &len));
4305 return inet_ntop(&sa);
4306 } // -x- std::string getpeername_ntop -x-
4307
4308 /*======================================================================*//**
4309 @brief
4310 Get specified "sockaddr_storage" structure's address as a "sockaddr"
4311 structure, for sockets in one of the supported families:
4312
4313 - AF_INET (IPv4)
4314 - AF_INET6 (IPv6)
4315 - AF_UNIX (Domain socket path)
4316 - AF_PACKET (Ethernet node/mac. address)
4317
4318 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
4319 (e.g., because the @c family doesn't utilize or support an address
4320 {or the format isn't known}
4321
4322 @returns pointer to sockaddr structure within provided sockaddr_storage
4323 @see bind
4324 @see mk_sockaddr_storage
4325 @see recvfrom
4326 @see sendto
4327 @see sendzto
4328 @qualifier POSIX
4329 @qualifier TLS
4330 *///=========================================================================
4331 static sockaddr* getsockaddr(
4332 /// The @c sockaddr_storage structure wherein @c sockaddr will be referenced from
4333 const sockaddr_storage* sa) {
4334 switch (sa->ss_family) {
4335 case AF_INET: // IPv4 address
4336 return (sockaddr*)&(((struct sockaddr_in*)sa)->sin_addr);//->sin_addr);
4337 case AF_INET6: // IPv6 address
4338 return (sockaddr*)&(((struct sockaddr_in6*)sa)->sin6_addr);//->sin6_addr);
4339 case AF_UNIX: // UNIX (path) domain socket
4340 return (sockaddr*)&(((struct sockaddr_un*)sa)->sun_path);//->sun_path);
4341 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
4342 // case /*AF_LINK* /18: // Link layer interface (arp)
4343 // break;
4344 case AF_PACKET: // Packet (ethernet) address (packet capturing)
4345 return (sockaddr*)&(((struct sockaddr_ll*)sa)->sll_addr);//->sll_addr);
4346 } // -x- switch family -x-
4347 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY"); // Everything else
4348 } // -x- sockaddr* getsockaddr -x-
4349
4350 /*======================================================================*//**
4351 @brief
4352 Get socket name returns the address of the socket as a "sockaddr_storage"
4353 structure.
4354
4355 The resulting structure is wrapped in std::unique_ptr (a C++ smart pointer
4356 that aids in the prevention of resource leaks).
4357
4358 @throws randolf::rex::xEBADF The underlying socket is not open
4359 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4360 part of the user address space
4361 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
4362 valid for this socket's family (a.k.a., communication domain)
4363 @throws randolf::rex::xENOBUFS Insufficient memory
4364 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4365 doesn't refer to a socket
4366
4367 @returns sockaddr_storage structure
4368 @qualifier POSIX
4369 @qualifier TLS
4370 *///=========================================================================
4371 std::unique_ptr<sockaddr_storage> getsockname() {
4372 std::unique_ptr sa = std::make_unique_for_overwrite<sockaddr_storage>();
4373 socklen_t len = sizeof(sockaddr_storage);
4374 __rc_check(::getsockname(__socket_fd, (struct sockaddr*)sa.get(), &len));
4375 return sa;
4376 } // -x- std::unique_ptr<sockaddr_storage> getsockname -x-
4377
4378 /*======================================================================*//**
4379 @brief
4380 Get socket name returns the name of the socket as a std::string object.
4381
4382 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
4383 (e.g., because the @c family doesn't utilize or support an address
4384 {or the format isn't known}
4385 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
4386 @throws randolf::rex::xEBADF The underlying socket is not open
4387 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4388 part of the user address space
4389 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
4390 valid for this socket's family (a.k.a., communication domain)
4391 @throws randolf::rex::xENOBUFS Insufficient memory
4392 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
4393 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4394 doesn't refer to a socket
4395
4396 @returns string representation of socket name
4397 @qualifier TLS
4398 *///=========================================================================
4399 std::string getsockname_ntop() {
4400 sockaddr_storage sa;
4401 socklen_t len = sizeof(sockaddr_storage);
4402 __rc_check(::getsockname(__socket_fd, (sockaddr*)&sa, &len));
4403 return inet_ntop(&sa);
4404 } // -x- std::string getsockname_ntop -x-
4405
4406 /*======================================================================*//**
4407 @brief
4408 Get socket option details in the form of an integer.
4409
4410 Most options return an integer, with the remaining options returning a
4411 pointer to a structure wrapped in a std::unique_ptr (a C++ smart pointer that
4412 aids in the prevention of resource leaks); the primitive types int, u_int,
4413 and u_char are not wrapped in C++ smart pointers because returning them by
4414 value is more efficient since allocating memory for an entire structure isn't
4415 needed.
4416
4417 @post
4418 It is up to the developer to know which return type is needed according to
4419 the socket option, otherwise an exception will likely be thrown -- in some
4420 cases where the wrong type will seem to work, this is due to the wrong type
4421 providing a minimally sufficient amount of memory for the storage of the
4422 resulting structure.
4423
4424 @par Notes
4425 The returned values/structures are not marked as "const" because they may
4426 need to be modified for unforseen purposes. Modifying the returend values or
4427 structures is fine because they are intended to be independent and are
4428 expected to have no direct impact on the rsocket's internal variables and
4429 structures.
4430
4431 Templates in C++ aren't used here because they don't work properly for our
4432 needs due to neccesity to handle both fundamental types and structures; it
4433 turns out that mixing these is impossible when using the same function name,
4434 so this just doesn't work as well as we'd like it to. (We may try to work on
4435 this again in the future as time permits to provide an additional method for
4436 obtaining socket options, but with the intention of never removing this
4437 current set of methods so as to ensure backward compatibility in the future.)
4438
4439 @throws randolf::rex::xEBADF The underlying socket is not open
4440 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
4441 part of the user address space
4442 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
4443 valid for this socket's family (a.k.a., communication domain)
4444 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
4445 is not supported
4446 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
4447 doesn't refer to a socket
4448
4449 @returns socket option value
4450 @see setsockopt(const int, const int)
4451 @see setsockopt(const int, const int, const int)
4452 @qualifier TLS
4453 *///=========================================================================
4454 int getsockopt_int(
4455 /// The level at which the option resides; typically @c SOL_SOCKET
4456 const int level,
4457 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4458 const int option) {
4459 int value = 0;
4460 socklen_t slt{sizeof(value)};
4461 __rc_check(::getsockopt(__socket_fd, level, option, &value, &slt));
4462 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
4463 return value;
4464 } // -x- int getsockopt_int -x-
4465
4466 /*======================================================================*//**
4467 @brief
4468 Get socket option details in the form of an unsigned integer.
4469 @copydetails getsockopt_int(const int, const int)
4470 @returns socket option value
4471 @see setsockopt(const int, const int, const u_int)
4472 @qualifier TLS
4473 *///=========================================================================
4474 u_int getsockopt_u_int(
4475 /// The level at which the option resides; typically @c SOL_SOCKET
4476 const int level,
4477 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4478 const int option) {
4479 u_int value = 0;
4480 socklen_t slt{sizeof(value)};
4481 __rc_check(::getsockopt(__socket_fd, level, option, &value, &slt));
4482 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
4483 return value;
4484 } // -x- u_int getsockopt_u_int -x-
4485
4486 /*======================================================================*//**
4487 @brief
4488 Get socket option details in the form of an unsigned character.
4489 @copydetails getsockopt_int(const int, const int)
4490 @returns socket option value
4491 @see setsockopt(const int, const int, const u_char)
4492 @qualifier TLS
4493 *///=========================================================================
4494 u_char getsockopt_u_char(
4495 /// The level at which the option resides; typically @c SOL_SOCKET
4496 const int level,
4497 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4498 const int option) {
4499 u_char value = 0;
4500 socklen_t slt{sizeof(value)};
4501 __rc_check(::getsockopt(__socket_fd, level, option, &value, &slt));
4502 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value);
4503 return value;
4504 } // -x- u_char getsockopt_u_char -x-
4505
4506 /*======================================================================*//**
4507 @brief
4508 Get socket option details in the form of a structure.
4509 @copydetails getsockopt_int(const int, const int);
4510 @returns socket option structure wrapped in std::unique_ptr
4511 @see setsockopt(const int, const int, const linger&)
4512 @qualifier TLS
4513 *///=========================================================================
4514 std::unique_ptr<linger> getsockopt_linger(
4515 /// The level at which the option resides; typically @c SOL_SOCKET
4516 const int level,
4517 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4518 const int option) {
4519 std::unique_ptr value = std::make_unique_for_overwrite<linger>();
4520 socklen_t slt{sizeof(value)};
4521 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4522 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4523 return value;
4524 } // -x- std::unique_ptr<linger> getsockopt_linger -x-
4525
4526 /*======================================================================*//**
4527 @copydoc getsockopt_linger(const int, const int)
4528 @see setsockopt(const int, const int, const timeval&)
4529 @qualifier TLS
4530 *///=========================================================================
4531 std::unique_ptr<timeval> getsockopt_timeval(
4532 /// The level at which the option resides; typically @c SOL_SOCKET
4533 const int level,
4534 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4535 const int option) {
4536 std::unique_ptr value = std::make_unique_for_overwrite<timeval>();
4537 socklen_t slt{sizeof(value)};
4538 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4539 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4540 return value;
4541 } // -x- std::unique_ptr<timeval> getsockopt_timeval -x-
4542
4543 /*======================================================================*//**
4544 @copydoc getsockopt_linger(const int, const int)
4545 @see setsockopt(const int, const int, const in_addr&)
4546 @qualifier TLS
4547 *///=========================================================================
4548 std::unique_ptr<in_addr> getsockopt_in_addr(
4549 /// The level at which the option resides; typically @c SOL_SOCKET
4550 const int level,
4551 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4552 const int option) {
4553 std::unique_ptr value = std::make_unique_for_overwrite<in_addr>();
4554 socklen_t slt{sizeof(value)};
4555 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4556 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4557 return value;
4558 } // -x- std::unique_ptr<in_addr> getsockopt_in_addr -x-
4559
4560 /*======================================================================*//**
4561 @copydoc getsockopt_linger(const int, const int)
4562 @see setsockopt(const int, const int, const ip_mreq&)
4563 @qualifier TLS
4564 *///=========================================================================
4565 std::unique_ptr<ip_mreq> getsockopt_ip_mreq(
4566 /// The level at which the option resides; typically @c SOL_SOCKET
4567 const int level,
4568 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4569 const int option) {
4570 std::unique_ptr value = std::make_unique_for_overwrite<ip_mreq>();
4571 socklen_t slt{sizeof(value)};
4572 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4573 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4574 return value;
4575 } // -x- std::unique_ptr<ip_mreq> getsockopt_ip_mreq -x-
4576
4577 /*======================================================================*//**
4578 @copydoc getsockopt_linger(const int, const int)
4579 @see setsockopt(const int, const int, const ip_mreq_source&)
4580 @qualifier TLS
4581 *///=========================================================================
4582 std::unique_ptr<ip_mreq_source> getsockopt_ip_mreq_source(
4583 /// The level at which the option resides; typically @c SOL_SOCKET
4584 const int level,
4585 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4586 const int option) {
4587 std::unique_ptr value = std::make_unique_for_overwrite<ip_mreq_source>();
4588 socklen_t slt{sizeof(value)};
4589 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4590 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4591 return value;
4592 } // -x- std::unique_ptr<ip_mreq_source> getsockopt_ip_mreq_source -x-
4593
4594 /*======================================================================*//**
4595 @copydoc getsockopt_linger(const int, const int)
4596 @see setsockopt(const int, const int, const ip_mreqn&)
4597 @qualifier TLS
4598 *///=========================================================================
4599 std::unique_ptr<ip_mreqn> getsockopt_ip_mreqn(
4600 /// The level at which the option resides; typically @c SOL_SOCKET
4601 const int level,
4602 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4603 const int option) {
4604 std::unique_ptr value = std::make_unique_for_overwrite<ip_mreqn>();
4605 socklen_t slt{sizeof(value)};
4606 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4607 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4608 return value;
4609 } // -x- std::unique_ptr<ip_mreqn> getsockopt_ip_mreqn -x-
4610
4611 /*======================================================================*//**
4612 @copydoc getsockopt_linger(const int, const int)
4613 @see setsockopt(const int, const int, const icmp6_filter&)
4614 @qualifier TLS
4615 *///=========================================================================
4616 std::unique_ptr<icmp6_filter> getsockopt_icmp6_filter(
4617 /// The level at which the option resides; typically @c SOL_SOCKET
4618 const int level,
4619 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4620 const int option) {
4621 std::unique_ptr value = std::make_unique_for_overwrite<icmp6_filter>();
4622 socklen_t slt{sizeof(value)};
4623 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4624 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4625 return value;
4626 } // -x- std::unique_ptr<icmp6_filter> getsockopt_icmp6_filter -x-
4627
4628 /*======================================================================*//**
4629 @copydoc getsockopt_linger(const int, const int)
4630 @see setsockopt(const int, const int, const sockaddr_in6&)
4631 @qualifier TLS
4632 *///=========================================================================
4633 std::unique_ptr<sockaddr_in6> getsockopt_sockaddr_in6(
4634 /// The level at which the option resides; typically @c SOL_SOCKET
4635 const int level,
4636 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4637 const int option) {
4638 std::unique_ptr value = std::make_unique_for_overwrite<sockaddr_in6>();
4639 socklen_t slt{sizeof(value)};
4640 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4641 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4642 return value;
4643 } // -x- std::unique_ptr<sockaddr_in6> getsockopt_sockaddr_in6 -x-
4644
4645 /*======================================================================*//**
4646 @copydoc getsockopt_linger(const int, const int)
4647 @see setsockopt(const int, const int, const ip6_mtuinfo&)
4648 @qualifier TLS
4649 *///=========================================================================
4650 std::unique_ptr<ip6_mtuinfo> getsockopt_ip6_mtuinfo(
4651 /// The level at which the option resides; typically @c SOL_SOCKET
4652 const int level,
4653 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4654 const int option) {
4655 std::unique_ptr value = std::make_unique_for_overwrite<ip6_mtuinfo>();
4656 socklen_t slt{sizeof(value)};
4657 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4658 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4659 return value;
4660 } // -x- std::unique_ptr<ip6_mtuinfo> getsockopt_ip6_mtuinfo -x-
4661
4662 /*======================================================================*//**
4663 @copydoc getsockopt_linger(const int, const int)
4664 @see setsockopt(const int, const int, const ipv6_mreq&)
4665 @qualifier TLS
4666 *///=========================================================================
4667 std::unique_ptr<ipv6_mreq> getsockopt_ipv6_mreq(
4668 /// The level at which the option resides; typically @c SOL_SOCKET
4669 const int level,
4670 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4671 const int option) {
4672 std::unique_ptr value = std::make_unique_for_overwrite<ipv6_mreq>();
4673 socklen_t slt{sizeof(value)};
4674 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4675 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4676 return value;
4677 } // -x- std::unique_ptr<ipv6_mreq> getsockopt_ipv6_mreq -x-
4678
4679 /*======================================================================*//**
4680 @copydoc getsockopt_linger(const int, const int)
4681 @see setsockopt(const int, const int, const group_req&)
4682 @qualifier TLS
4683 *///=========================================================================
4684 std::unique_ptr<group_req> getsockopt_group_req(
4685 /// The level at which the option resides; typically @c SOL_SOCKET
4686 const int level,
4687 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4688 const int option) {
4689 std::unique_ptr value = std::make_unique_for_overwrite<group_req>();
4690 socklen_t slt{sizeof(value)};
4691 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4692 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4693 return value;
4694 } // -x- std::unique_ptr<group_req> getsockopt_group_req -x-
4695
4696 /*======================================================================*//**
4697 @copydoc getsockopt_linger(const int, const int)
4698 @see setsockopt(const int, const int, const group_source_req&)
4699 @qualifier TLS
4700 *///=========================================================================
4701 std::unique_ptr<group_source_req> getsockopt_group_source_req(
4702 /// The level at which the option resides; typically @c SOL_SOCKET
4703 const int level,
4704 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4705 const int option) {
4706 std::unique_ptr value = std::make_unique_for_overwrite<group_source_req>();
4707 socklen_t slt{sizeof(value)};
4708 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4709 if (__debug) __debug_sockopt("getsockopt(socket{0x", level, option, value.get());
4710 return value;
4711 } // -x- std::unique_ptr<group_source_req> getsockopt_group_source_req -x-
4712
4713 /*======================================================================*//**
4714 @copydoc getsockopt_linger(const int, const int)
4715 @see setsockopt(const int, const int)
4716 @see setsockopt(const int, const int, const int)
4717 @qualifier TLS
4718 *///=========================================================================
4719 template<class T> std::unique_ptr<T> getsockopt_other(
4720 /// The level at which the option resides; typically @c SOL_SOCKET
4721 const int level,
4722 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
4723 const int option) {
4724 std::unique_ptr value = std::make_unique_for_overwrite<T>();
4725 socklen_t slt{sizeof(value)};
4726 __rc_check(::getsockopt(__socket_fd, level, option, value.get(), &slt));
4727 if (__debug) __debug_sockopt_other("getsockopt_other(socket{0x", level, option, socklen_t(sizeof(value)));
4728 return value;
4729 } // -x- std::unique_ptr<T> getsockopt_other -x-
4730
4731 /*======================================================================*//**
4732 @brief
4733 Get underlying socket's address as an @cstd::string object, for sockets in
4734 one of the supported families:
4735
4736 - AF_INET (IPv4)
4737 - AF_INET6 (IPv6)
4738 - AF_UNIX (Domain socket path)
4739 - AF_PACKET (Ethernet node/mac. address)
4740
4741 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
4742 (e.g., because the @c family doesn't utilize or support an address
4743 {or the format isn't known}
4744 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
4745 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
4746
4747 @returns string representation of underlying socket's address
4748 @see inet_ntop(sockaddr_storage*) static method
4749 @qualifier POSIX
4750 @qualifier TLS
4751 *///=========================================================================
4752 std::string inet_ntop() {
4753 return inet_ntop((sockaddr_storage*)__socket_addr);
4754 } // -x- std::string inet_ntop -x-
4755
4756 /*======================================================================*//**
4757 @brief
4758 Get specified "sockaddr_storage" structure's address as a std::string, for
4759 sockets in one of the supported families:
4760
4761 - AF_INET (IPv4)
4762 - AF_INET6 (IPv6)
4763 - AF_UNIX (Domain socket path)
4764 - AF_PACKET (Ethernet node/mac. address)
4765
4766 @throws randolf::rex::xEAI_ADDRFAMILY if the address can't be determined
4767 (e.g., because the @c family doesn't utilize or support an address
4768 {or the format isn't known}
4769 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
4770 @throws randolf::rex::xENOSPC Resulting address string exceeds maximum size
4771
4772 @returns string representation of underlying socket's address
4773 @see inet_ntop() non-static method
4774 @qualifier POSIX
4775 @qualifier TLS
4776 *///=========================================================================
4777// static const std::string inet_ntop(
4778 static std::string inet_ntop(
4779 /// Source structure that [should] contain address data
4780 sockaddr_storage* sa) {
4781 std::string str;
4782 switch (sa->ss_family) {
4783 case AF_INET: // IPv4 address
4784 {
4785 char ntop[sizeof(sockaddr_in)]{0};
4786 const char* rc = ::inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), ntop, sizeof(ntop));
4787// TODO: if (rc == nullptr) _rc...
4788 str.assign(ntop);
4789 }
4790 break;
4791 case AF_INET6: // IPv6 address
4792 { // Debug
4793 char ntop[sizeof(sockaddr_in6)]{0};
4794 const char* rc = ::inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), ntop, sizeof(ntop));
4795// TODO: if (rc == nullptr) _rc...
4796 str.assign(ntop);
4797 }
4798 break;
4799 case AF_UNIX: // UNIX (path) domain socket
4800 str.assign(((struct sockaddr_un *)sa)->sun_path);
4801 break;
4802 // AF_LINK is not supported at this time due to lack of clarity on obtaining and presenting the address
4803 // case /*AF_LINK* /18: // Link layer interface (arp)
4804 // break;
4805 case AF_PACKET: // Packet (ethernet) address (packet capturing)
4806 str.assign(to_mac(((struct sockaddr_ll *)sa)->sll_addr).get());
4807 break;
4808 default: // Everything else
4809 throw randolf::rex::xEAI_ADDRFAMILY("EAI_ADDRFAMILY");
4810 } // -x- switch family -x-
4811 return str;
4812 } // -x- std::string inet_ntop -x-
4813
4814 /*======================================================================*//**
4815 @brief
4816 Find out whether an internal read buffer was allocated (this is most likely
4817 triggered by an attempt to read a line of text).
4818 @note
4819 The buffer_size() methods report on how much memory was allocated for the
4820 internal read buffer or to set its size (in bytes).
4821 @returns TRUE = an internal read buffer was allocated
4822 @returns FALSE = an internal read buffer was not allocated
4823 @see buffer_size
4824 @see buffer_size(const size_t nbytes)
4825 @qualifier TLS
4826 *///=========================================================================
4827 bool is_buffered() noexcept {
4828 return __buffer != nullptr;
4829 } // -x- bool is_buffered -x-
4830
4831 /*======================================================================*//**
4832 @brief
4833 Find out whether the underlying socket is not open (which may not be the same
4834 as specifically "closed" since a newly instantiated empty socket begins in a
4835 "not open" state despite the underlying socket not explicitly having been
4836 closed).
4837 @returns TRUE = not open
4838 @returns FALSE = open
4839 @see is_closed
4840 @see is_open
4841 @qualifier TLS
4842 *///=========================================================================
4843 bool is_closed() noexcept {
4844 return !__socket_open;
4845 } // -x- bool is_closed -x-
4846
4847 /*======================================================================*//**
4848 @brief
4849 Find out whether the underlying socket is connected with/to an endpoint.
4850 @returns TRUE = open
4851 @returns FALSE = not open
4852 @see connect
4853 @see is_closed
4854 @see is_open
4855 @qualifier TLS
4856 *///=========================================================================
4857 bool is_connected() noexcept {
4858 return __socket_connected;
4859 } // -x- bool is_connected -x-
4860
4861 /*======================================================================*//**
4862 @brief
4863 Find out whether the default byte order for this host is LSB (small endian).
4864 @note
4865 If you're trying to choose which endian type to use when designing a new
4866 internet protocol, then big endian is normally the better option. However,
4867 if your new protocol will only be used by hardware that all share the same
4868 endianness, then that endianness is probably the more optimal option since it
4869 will translate to an overall lesser consumption of CPU cycles by reducing or
4870 eliminating endianness conversions.
4871 @returns TRUE = LSB (little endian)
4872 @returns FALSE = MSB (big endian / network byte order)
4873 @see is_endian_msb
4874 @qualifier TLS
4875 *///=========================================================================
4876 bool is_endian_lsb() noexcept {
4877 return !__endian_is_msb;
4878 } // -x- bool is_endian_lsb -x-
4879
4880 /*======================================================================*//**
4881 @brief
4882 Find out whether the default byte order for this host is MSB (big endian).
4883 @note
4884 Big endian is the standard known as "network byte order" that's also used in
4885 various header fields in internet packets.
4886 @n@n
4887 If you're trying to choose which endian type to use when designing a new
4888 internet protocol, then big endian is normally the better option. However,
4889 if your new protocol will only be used by hardware that all share the same
4890 endianness, then that endianness is probably the more optimal option since it
4891 will translate to an overall lesser consumption of CPU cycles by reducing or
4892 eliminating endianness conversions.
4893 @returns TRUE = MSB (big endian / network byte order)
4894 @returns FALSE = LSB (little endian)
4895 @see is_endian_lsb
4896 @qualifier TLS
4897 *///=========================================================================
4898 bool is_endian_msb() noexcept {
4899 return __endian_is_msb;
4900 } // -x- bool is_endian_msb -x-
4901
4902 /*======================================================================*//**
4903 @brief
4904 Find out if the EoL adoption policy is enabled for the @ref recvline() method
4905 (see the @ref eol_adoption method to find out how the dynamically-detected
4906 EoL sequence gets adopted, and under what conditions).
4907 @returns TRUE = EoL adoption is enabled
4908 @returns FALSE = EoL adoption is disabled
4909 @see eol
4910 @see eol_adoption
4911 @see eol_index
4912 @see recvline
4913 @see sendline
4914 @see send_eol
4915 @qualifier TLS
4916 *///=========================================================================
4917 bool is_eol_adoption() noexcept {
4918 return __eol_adoption;
4919 } // -x- bool is_eol_adoption -x-
4920
4921 /*======================================================================*//**
4922 @brief
4923 Find out if the EoL substitution policy is enabled for the @ref printf(),
4924 @ref printfline(), @ref vprintf(), and @ref vprintfline() methods.
4925 @returns TRUE = EoL substitution is enabled
4926 @returns FALSE = EoL substitution is disabled
4927 @see eol
4928 @see eol_fix_printf
4929 @see printf
4930 @see printfline
4931 @see sendline
4932 @see send_eol
4933 @see vprintf
4934 @see vprintfline
4935 @qualifier TLS
4936 *///=========================================================================
4937 bool is_eol_fix_printf() noexcept {
4938 return __eol_fix_printf;
4939 } // -x- bool is_eol_fix_printf -x-
4940
4941 /*======================================================================*//**
4942 @brief
4943 Find out whether the @c IPPROTO_MPTCP protocol will be replaced automatically
4944 with @c IPPROTO_TCP internally when using certain POSIX functions that are
4945 known to not support MPTCP.
4946 @returns TRUE = substitution will occur (only for @c IPPROTO_MPTCP)
4947 @returns FALSE = no subsitution will occur
4948 @see mptcp
4949 @see rsocket
4950 @see socket
4951 @qualifier TLS
4952 *///=========================================================================
4953 bool is_mptcp() noexcept {
4954 return __socket_mptcp_flag;
4955 } // -x- bool is_mptcp -x-
4956
4957 /*======================================================================*//**
4958 @brief
4959 Find out whether the underlying socket is open.
4960 @returns TRUE = open
4961 @returns FALSE = not open
4962 @see is_closed()
4963 @qualifier TLS
4964 *///=========================================================================
4965 bool is_open() noexcept {
4966 return __socket_open;
4967 } // -x- bool is_open -x-
4968
4969 /*======================================================================*//**
4970 @brief
4971 Find out whether encrypted communications is enabled or disabled.
4972 @returns TRUE = encrypted communications is enabled
4973 @returns FALSE = encrypted communications is disabled
4974 @see tls(bool, TLS_FLAGS)
4975 @see is_tls_sni
4976 @qualifier TLS
4977 *///=========================================================================
4978 bool is_tls() noexcept {
4979 return __tls;
4980 } // -x- bool is_tls -x-
4981
4982 /*======================================================================*//**
4983 @brief
4984 Find out whether TLS context is in TLS_CLIENT mode.
4985 @returns TRUE = TLS context is in TLS_CLIENT mode
4986 @returns FALSE = TLS context is in TLS_SERVER mode
4987 @see TLS_CLIENT
4988 @see tls()
4989 @qualifier TLS
4990 *///=========================================================================
4991 bool is_tls_client_mode() noexcept {
4992 return !__tls_server_mode;
4993 } // -x- bool is_tls_client_mode -x-
4994
4995 /*======================================================================*//**
4996 @brief
4997 Find out whether egress from encryption (to unencrypted mode) is allowed.
4998 @returns TRUE = egress from encrypted communications is allowed
4999 @returns FALSE = egress from encrypted communications is not allowed
5000 @see TLS_NO_EGRESS
5001 @see tls()
5002 @qualifier TLS
5003 *///=========================================================================
5004 bool is_tls_egress_okay() noexcept {
5005 return __tls_egress;
5006 } // -x- bool is_tls_egress_okay -x-
5007
5008 /*======================================================================*//**
5009 @brief
5010 Find out whether encrypted communications is exclusive.
5011 @returns TRUE = encrypted communications is exclusive
5012 @returns FALSE = encrypted communications is not exclusive
5013 @see tls()
5014 @qualifier TLS
5015 *///=========================================================================
5016 bool is_tls_exclusive() noexcept {
5017 return __tls_exclusive;
5018 } // -x- bool is_tls_exclusive -x-
5019
5020 /*======================================================================*//**
5021 @brief
5022 Find out whether ingress to encryption (from unencrypted mode) is allowed.
5023 @returns TRUE = ingress to encrypted communications is allowed
5024 @returns FALSE = ingress to encrypted communications is not allowed
5025 @see TLS_NO_INGRESS
5026 @see tls()
5027 @qualifier TLS
5028 *///=========================================================================
5029 bool is_tls_ingress_okay() noexcept {
5030 return __tls_ingress;
5031 } // -x- bool is_tls_ingress_okay -x-
5032
5033 /*======================================================================*//**
5034 @brief
5035 Find out whether read-ahead mode will automatically be enabled internally by
5036 the @ref tls and @ref tls_ctx methods.
5037 @returns TRUE = read-ahead mode was/will-be enabled (default)
5038 @returns FALSE = read-ahead mode wasn't/will-not-be enabled
5039 @see TLS_NO_READ_AHEAD
5040 @see tls()
5041 @see tls_ctx()
5042 @qualifier TLS
5043 *///=========================================================================
5044 bool is_tls_read_ahead_enabled() noexcept {
5045 return !__tls_no_read_ahead;
5046 } // -x- bool is_tls_read_ahead_enabled -x-
5047
5048 /*======================================================================*//**
5049 @brief
5050 Find out whether TLS context is in TLS_SERVER mode.
5051 @returns TRUE = TLS context is in TLS_SERVER mode
5052 @returns FALSE = TLS context is in TLS_CLIENT mode
5053 @see TLS_SERVER
5054 @see tls()
5055 @qualifier TLS
5056 *///=========================================================================
5057 bool is_tls_server_mode() noexcept {
5058 return __tls_server_mode;
5059 } // -x- bool is_tls_server_mode -x-
5060
5061 /*======================================================================*//**
5062 @brief
5063 Find out whether SNI (Server Name Identifier) is enabled (configured, which
5064 implies that an internal callback function was also set up).
5065 @returns TRUE = SNI is enabled
5066 @returns FALSE = SNI is disabled
5067 @see is_tls
5068 @see tls_sni()
5069 @qualifier TLS
5070 *///=========================================================================
5071 bool is_tls_sni() noexcept {
5072 return __tls_sni != nullptr;
5073 } // -x- bool is_tls_sni -x-
5074
5075 /*======================================================================*//**
5076 @brief
5077 Find out whether SNI (Server Name Identifier) received an empty hostname or
5078 if no hostname was provided.
5079 @returns TRUE = SNI hostname was received and it was not empty
5080 @returns FALSE = SNI hostname was not received or it was empty
5081 @see is_tls_sni_match()
5082 @see name_sni()
5083 @qualifier TLS
5084 *///=========================================================================
5085 bool is_tls_sni_has_name() noexcept {
5086 return __tls_sni_has_name;
5087 } // -x- bool is_tls_sni_has_name -x-
5088
5089 /*======================================================================*//**
5090 @brief
5091 Find out whether SNI (Server Name Identifier) was matched, which means that
5092 we're using one of the supplementary TLS certificates that are included in
5093 the associated @ref rsocket_sni object as separate TLS contexts.
5094
5095 When this method returns @c TRUE, it means the @c OpenSSL callback (which
5096 occurs during the TLS handshake process) completed the TLS handshake with one
5097 of the TLS certificates that's included in this @c rsocket's @ref rsocket_sni
5098 object instead of the primary TLS certificate assigned to this @c rsocket,
5099 and this @c rsocket is using the respective TLS context instead of the
5100 primary (default) one.
5101 @returns TRUE = SNI was matched
5102 @returns FALSE = SNI wasn't matched
5103 @see name_sni
5104 @see tls_sni
5105 @see tls_sni_has_name
5106 @qualifier TLS
5107 *///=========================================================================
5108 bool is_tls_sni_match() noexcept {
5109 return __tls_sni_match;
5110 } // -x- bool is_tls_sni_match -x-
5111
5112 /*======================================================================*//**
5113 @brief
5114 Enable listening mode for this rsocket to prepare it to accept() new inbound
5115 connections.
5116
5117 The backlog defaults to SOMAXCONN (4096 on Linux, and 128 on older systems),
5118 which is common on most systems. If a higher value is supplied that exceeds
5119 @c kern.somaxconn, the kernel will automatically override the backlog with
5120 the value stored in @c kern.somaxconn without generating any errors or
5121 warnings.
5122
5123 @throws randolf::rex::xEADDRINUSE Address and/or port number already in use
5124 @throws randolf::rex::xEADDRINUSE No ephemeral ports are available for
5125 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
5126 but it really isn't)
5127 @throws randolf::rex::xEBADF The underlying socket is not open
5128 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5129 doesn't refer to a socket
5130 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
5131 @returns The same rsocket object so as to facilitate stacking
5132 @see accept()
5133 @see accept_sp()
5134 @see accept4()
5135 @see accept4_sp()
5136 @see backlog()
5137 @see bind()
5138 @qualifier POSIX
5139 @qualifier TLS
5140 *///=========================================================================
5141 rsocket& listen(
5142 /// Backlog queue size (0 = uses rsocket's default; see @ref backlog for more
5143 /// details about this); specifying a non-zero backlog also updates rocket's
5144 /// internal default (SOMAXCONN is 4096 on Linux, and 128 on older systems)
5145 int backlog = 0) {
5146 if (__debug) debug("listen(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5147 + ", " + std::to_string(backlog)
5148 + ");");
5149
5150 // --------------------------------------------------------------------------
5151 // Use rsocket's default backlog if not specified, otherwise update it
5152 // with the new value specified here.
5153 // --------------------------------------------------------------------------
5154 if (backlog = 0) backlog = __socket_backlog;
5155 else __socket_backlog = backlog;
5156
5157 // --------------------------------------------------------------------------
5158 // Configure underlying socket to queue up to "backlog" inbound connections.
5159 // --------------------------------------------------------------------------
5160 __rc_check(::listen(__socket_fd, backlog));
5161
5162 return *this;
5163 } // -x- rsocket& listen -x-
5164
5165 /*======================================================================*//**
5166 @brief
5167 Convert an IPv4 address, IPv6 address, ethernet packet, or UNIX domain
5168 socket to a sockaddr_storage structure.
5169
5170 If service_name is an absolute path (that begins with a "/" charcter) and the
5171 family is set to AF_UNSPEC (the default), then the resulting family will be
5172 set to AF_UNIX.
5173
5174 @par Notes
5175 This method utilizes the results of getaddrinfo().
5176
5177 Other families like AF_LINK and AF_PACKET should work, but haven't been
5178 tested thoroughly. The additional support we provide for IPv4 and IPv6
5179 addresses is to copy the port number into the resulting structure (as a
5180 convenience).
5181
5182 @post
5183 The resulting sockaddr_storage structure is wrapped in std::unique_ptr (a C++
5184 smart pointer that aids in the prevention of resource leaks).
5185
5186 @par Threads
5187 This method is thread-safe.
5188
5189 @throws randolf::rex::xEAI_ADDRFAMILY If specified network host doesn't have
5190 any addresses in the specified address family
5191 @throws randolf::rex::xEAI_AGAIN Temporary failure code from DNS server (try
5192 again later)
5193 @throws randolf::rex::xEAI_BADFLAGS Invalid flags in @c hints.ai_flags (or
5194 `hints.ai_flags` included @c AI_CANONNAME with @c nullptr as @c name)
5195 @throws randolf::rex::xEAI_FAIL Permanent failure code from DNS server
5196 @throws randolf::rex::xEAI_FAMILY The specified family is not supported
5197 @throws randolf::rex::xEAI_MEMORY Out of memory
5198 @throws randolf::rex::xEAI_NONAME If node_name is nullptr or an empty string
5199 @throws randolf::rex::xEAI_SERVICE The specified service is not available
5200 for the specified socket type
5201 @throws randolf::rex::xEAI_SOCKTYPE The specified socket type is not
5202 supported
5203 @throws randolf::rex::xEAI_SYSTEM Other system error (use errno to determine
5204 what the error is, then run use @ref randolf::rex::rex::mk_exception
5205 to throw the correct exception)
5206
5207 @returns sockaddr_storage structure
5208 @see getsockaddr
5209 *///=========================================================================
5210// static std::unique_ptr<sockaddr_storage> mk_sockaddr_storage(
5211 static struct sockaddr_storage* mk_sockaddr_storage(
5212 /// IP address or UNIX domain socket address to convert
5213 const char* node_name,
5214 /// Port number (or service name used by some other families)
5215 const char* service_name = nullptr,
5216 /// Optional pointer to a helpful addrinfo structure
5217 const addrinfo* hints = nullptr) {
5218
5219 // --------------------------------------------------------------------------
5220 // Initial sanity checks.
5221 // --------------------------------------------------------------------------
5222 if (node_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME); // if (node_name == nullptr && service_name == nullptr) randolf::rex::mk_exception("", EAI_NONAME);
5223
5224 // --------------------------------------------------------------------------
5225 // Handle any special cases.
5226 // --------------------------------------------------------------------------
5227 switch (hints == nullptr ? AF_UNSPEC : hints->ai_family) { // Default to AF_UNSPEC in the absence of hints
5228 case AF_UNSPEC:
5229 if (node_name[0] != '/') break;
5230 // Next entry MUST be "case AF_UNIX" for this fall-through to work
5231 case AF_UNIX:
5232 {
5233 // For some unknown reason, clang++ can't compile this line that
5234 // g++ has absolutely no trouble with at all:
5235 // std::unique_ptr sa = std::make_unique_for_overwrite<sockaddr_storage>(AF_UNIX);
5236 // So, after wasting more than a year trying to figure out what the
5237 // hundreds of lines of cryptic errors meant, I eventually gave up
5238 // on clang++ for being such a dumbfuck, and used this work-around:
5239 struct sockaddr_storage* sa = (struct sockaddr_storage*)::malloc(sizeof(sockaddr_storage));
5240 sa->ss_family = AF_UNIX;
5241// std::unique_ptr sa = std::make_unique_for_overwrite<sockaddr_storage>();
5242// sa->ss_family = AF_UNIX;
5243 std::strcpy(((struct sockaddr_un *)sa)->sun_path, node_name); // Copy path
5244 return sa;
5245 }
5246 break;
5247 } // -x- switch hints->family -x-
5248
5249 // --------------------------------------------------------------------------
5250 // Acquire addrinfo[] linked-list array.
5251 // --------------------------------------------------------------------------
5252 addrinfo* __addr_result = nullptr; // This is temporary
5253//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;
5254 randolf::rex::mk_exception("", getaddrinfo(node_name,
5255 service_name,
5256 hints,
5257 &__addr_result));
5258
5259 // --------------------------------------------------------------------------
5260 // Find first valid addrinfo[] array by trying to open a socket with it.
5261 // --------------------------------------------------------------------------
5262 for (addrinfo* ar = __addr_result; ar != nullptr; ar = ar->ai_next) {
5263//std::cout << " ... ai_family=" << ar->ai_family << " ai_socktype=" << ar->ai_socktype << " ai_flags=" << ar->ai_flags << std::endl; // *******************
5264// ***************************** TODO: Make sure this loop is working properly
5265 } // -x- for ar -x-
5266//std::cout << " !!! ai_family=" << __addr_result->ai_family << std::endl; // *******************
5267
5268 // --------------------------------------------------------------------------
5269 // Copy the address to "sa" and return it. The "addr" data for IPv4 and IPv6
5270 // addresses include the TCP/UDP port number (service) in first two bytes as
5271 // as an unsigned integer (u_int16), followed immediately by the IP address.
5272 //
5273 // We're taking the first record only. We assume that hints{} is defined on
5274 // an as-needed basis.
5275 //
5276 // Note: AF_LINK and AF_PACKET are not widely supported, but we've made an
5277 // effort to accomodate their usage.
5278 // --------------------------------------------------------------------------
5279 struct sockaddr_storage* sa = (struct sockaddr_storage*)::malloc(sizeof(sockaddr_storage));
5280 //std::unique_ptr sa = std::make_unique_for_overwrite<sockaddr_storage>();
5281 switch (__addr_result->ai_family) {
5282 case AF_INET: // IPv4 address
5283 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_in));
5284 break;
5285 case AF_INET6: // IPv6 address
5286 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_in6));
5287 break;
5288 /* // We're handling this earlier as a special case: TODO: Move special handling to here
5289 case AF_UNIX: // UNIX (path) domain socket
5290 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_un));
5291 break;
5292 */
5293 case /*AF_LINK*/18: // Link layer interface (arp)
5294 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_dl));
5295 break;
5296 case AF_PACKET: // Packet (ethernet) address (packet capturing)
5297 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_ll));
5298 break;
5299 default: // Everything else
5300 std::memcpy(sa, __addr_result->ai_addr, sizeof(sockaddr_storage));
5301 } // -x- switch family -x-
5302 freeaddrinfo(__addr_result); // We don't need this anymore
5303 return sa;
5304 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
5305
5306 /*======================================================================*//**
5307 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
5308 *///=========================================================================
5309 static struct sockaddr_storage* mk_sockaddr_storage(
5310 /// IP address or UNIX domain socket address to convert
5311 const char* node_name,
5312 /// Port number
5313 const u_int16_t service_name,
5314 /// Optional pointer to a helpful addrinfo structure
5315 const addrinfo* hints = nullptr) {
5316 std::string port = std::to_string(service_name);
5317 return mk_sockaddr_storage(node_name, port.data(), hints);
5318 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
5319
5320 /*======================================================================*//**
5321 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
5322 *///=========================================================================
5323 static struct sockaddr_storage* mk_sockaddr_storage(
5324 /// IP address or UNIX domain socket address to convert
5325 const std::string node_name,
5326 /// Port number
5327 const u_int16_t service_name,
5328 /// Optional pointer to a helpful addrinfo structure
5329 const addrinfo* hints = nullptr) {
5330 std::string port = std::to_string(service_name);
5331 return mk_sockaddr_storage(node_name.data(), port.data(), hints);
5332 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
5333
5334 /*======================================================================*//**
5335 @copydoc mk_sockaddr_storage(const char*, const char*, const addrinfo*)
5336 *///=========================================================================
5337 static struct sockaddr_storage* mk_sockaddr_storage(
5338 /// IP address or UNIX domain socket address to convert
5339 const std::string node_name,
5340 /// Port number (or server name used by some other families)
5341 const std::string service_name,
5342 /// Optional pointer to a helpful addrinfo structure
5343 const addrinfo* hints = nullptr) {
5344 return mk_sockaddr_storage(node_name.data(), service_name.data(), hints);
5345 } // -x- sockaddr_storage* mk_sockaddr_storage -x-
5346
5347 /*======================================================================*//**
5348 @brief
5349 Set the special internal MPTCP protocol substitution policy, which determines
5350 whether to automatically substitute the @c IPPROTO_MPTCP protocol internally
5351 with the @c IPPROTO_TCP when using certain POSIX functions that are known to
5352 not support MPTCP.
5353
5354 This method can be used before or after configuring a new socket.
5355 @returns The same rsocket object so as to facilitate stacking
5356 @see is_mptcp
5357 @see rsocket
5358 @see socket
5359 @qualifier TLS
5360 *///=========================================================================
5361 rsocket& mptcp(
5362 /// Set special internal MPTCP protocol substitution policy@n
5363 /// TRUE = substitution will occur (only for @c IPPROTO_MPTCP); this is the
5364 /// default for a new rsocket@n
5365 /// FALSE = no subsitution will occur
5366 const bool flag) noexcept {
5367 __socket_mptcp_flag = flag;
5368 return *this;
5369 } // -x- rsocket& mptcp -x-
5370
5371 /*======================================================================*//**
5372 @brief
5373 Specify a name for this rsocket.
5374
5375 This is an arbitrary name that is entirely optional, and should be regarded
5376 as similar to the naming of threads.
5377 @returns The same rsocket object so as to facilitate stacking
5378 @see get_name
5379 @see get_name_sni
5380 *///=========================================================================
5381 rsocket& name(
5382 /// Name to assign to this @c rsocket
5383 const std::string& name) noexcept {
5384 // const std::lock_guard<std::mutex> lock(__name);
5385 __name = name;
5386 return *this;
5387 } // -x- rsocket& name -x-
5388
5389 /*======================================================================*//**
5390 @brief
5391 Get socket I/O statistics from internally-tracked socket I/O counters.
5392
5393 The number of bytes transmitted and received is tracked internally, so that
5394 the information can be used later in logging. These statistics are available
5395 at all times, but for logging purposes it makes the most sense to copy this
5396 information after the rsocket is closed, at which time the final statistics
5397 will continue to be available until the rsocket's destructor takes over.
5398
5399 @par Threads
5400 This method is threadsafe.
5401 @returns rsocket_io wrapped in a std::unique_ptr object to help ease memory
5402 management efforts
5403 @see net_io_final
5404 @see net_io_update
5405 @see printf
5406 @qualifier TLS
5407 *///=========================================================================
5408 std::unique_ptr<rsocket_io> net_io() noexcept {
5409 std::unique_ptr stats = std::make_unique_for_overwrite<rsocket_io>();
5410 stats->bytes_rx = __bytes_rx;
5411 stats->bytes_tx = __bytes_tx;
5412 stats->bytes_sx = __tls ? 0 : (__buffer != nullptr ? __buffer->get_utilized() : 0); // <bytes in buffer; are these lost bytes that should be subtracted from __bytes_rx?>
5413 stats->crypt_rx = __crypt_rx;
5414 stats->crypt_tx = __crypt_tx;
5415 stats->crypt_sx = __tls ? (__buffer != nullptr ? __buffer->get_utilized() : 0) : 0; // <bytes in buffer; are these lost bytes that should be subtracted from __crypt_rx?>
5416 stats->is_final = false;
5417 return stats;
5418 } // -x- std::unique_ptr<rsocket_io> net_io -x-
5419
5420 /*======================================================================*//**
5421 @brief
5422 Get socket I/O statistics from internally-tracked socket I/O counters as a
5423 pre-formatted std::string object.
5424
5425 The number of bytes transmitted and received is tracked internally, so that
5426 the information can be used later in logging. These statistics are available
5427 at all times, but for logging purposes it makes the most sense to copy this
5428 information after the rsocket is closed, at which time the final statistics
5429 will continue to be available until the rsocket's destructor takes over.
5430
5431 @par Formatting
5432 The format string may contain any characters, with only instances of the
5433 following case-sensitive command sequences to be interpolated accordingly
5434 (invalid commands will be ignored and will remain in place, unmodified):
5435
5436 <table cellpadding=8 cellspacing=0 border=1>
5437 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
5438 <tr><td>$$</td><td>One dollar sign ("$")</td><td>@e N/A</td></tr>
5439 <tr><td>$aR</td><td>Total (all) bytes received</td><td>bytes_rx @c + crypt_rx</td></tr>
5440 <tr><td>$aS</td><td>Total (all) spare bytes recevied in ring buffer</td><td>bytes_sx @c + crypt_sx</td></tr>
5441 <tr><td>$aT</td><td>Total (all) bytes transmitted</td><td>bytes_tx @c + crypt_tx</td></tr>
5442 <tr><td>$bR</td><td>Unencrypted bytes received</td><td>bytes_rx</td></tr>
5443 <tr><td>$bS</td><td>Unencrypted spare bytes received in ring buffer</td><td>bytes_sx</td></tr>
5444 <tr><td>$bT</td><td>Unencrypted bytes transmitted</td><td>bytes_tx</td></tr>
5445 <tr><td>$cR</td><td>Encrypted bytes recevied</td><td>crypt_rx</td></tr>
5446 <tr><td>$cS</td><td>Encrypted spare bytes received in ring buffer</td><td>crypt_sx</td></tr>
5447 <tr><td>$cT</td><td>Encrypted bytes transmitted</td><td>crypt_tx</td></tr>
5448 <tr><th>Command</th><th>Replacement text</th><th>Data source (@ref rsocket_io)</th></tr>
5449 </table>
5450
5451 Why do we use dollar signs instead of percent symbols, like other formatting
5452 functions like printf() does? So that the format string won't be in conflict
5453 with any percent-prefixed commands in printf() and similar funcions. This
5454 means that a printf() format string can be put through a first pass here to
5455 get the needed statistics interpolated into the needed positions.
5456
5457 @par Threads
5458 This method is threadsafe.
5459 @returns An interpolated format string as an std::string object.
5460 @see net_io_final
5461 @see net_io_update
5462 @see printf
5463 @qualifier TLS
5464 *///=========================================================================
5465 std::string net_io(
5466 /// Format string
5467 const char* format,
5468 /// Length of format string (in bytes), or 0 to auto-detect length if format string is an ASCIIZ string
5469 size_t len = 0,
5470 /// Pointer to an @ref rsocket_io structure to read the statistics from (nullptr = use internal copy of current statistics)
5471 // 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)
5472 rsocket_io* addr = nullptr) noexcept {
5473
5474 // --------------------------------------------------------------------------
5475 // Measure size of format string if an ASCIIZ string was indicated.
5476 // --------------------------------------------------------------------------
5477 if (len == 0) len = std::strlen(format);
5478
5479 // --------------------------------------------------------------------------
5480 // Internal variables.
5481 // --------------------------------------------------------------------------
5482 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
5483 std::string stats; // Formatted result
5484 ulong val; // Value (to be inserted)
5485 char c; // Current character
5486 std::string cmd; // Accumulated command (may be subsituted)
5487 bool flag_commas = false; // Thousands separators flag
5488 bool flag_right = false; // Flush-right flag
5489
5490 // --------------------------------------------------------------------------
5491 // Process format string and build resulting formatted stats string.
5492 //
5493 // This is designed to be fast, and I'm taking huge shortcuts here since the
5494 // commands are all the same size (except for the first one, which is only
5495 // two dollar sign characters). Processing in this loop should be quite fast
5496 // compared to using library functions to search for dollar sign characters
5497 // since data needs to be copied anyway -- why run a loop over the text twice
5498 // when can get away with doing it faster by doing it only once? This is an
5499 // optimized approach, although it probably could be even faster by not using
5500 // std::string for temporary command storage (there are other ways to do this
5501 // but I think this should suffice for now since it isn't expected to be used
5502 // very often).
5503 // --------------------------------------------------------------------------
5504 for (int i = 0; i < len; i++) {
5505
5506 // --------------------------------------------------------------------------
5507 // First character (potentially).
5508 // --------------------------------------------------------------------------
5509 if ((c = format[i]) != '$') { // Not a dollar sign -- save it and move on
5510 stats.push_back(c);
5511 continue;
5512 } // -x- if !$ -x-
5513 cmd = c; // Start building up the command string
5514
5515 // --------------------------------------------------------------------------
5516 // Second character: Part 1 of 2
5517 //
5518 // TODO: Add support for "," commas, and "r" right-alignment.
5519 // --------------------------------------------------------------------------
5520 if (++i == len) continue; // End of format string, so we're done
5521 cmd.push_back(c = format[i]);
5522 if (c == '$') { // Consume the previous dollar sign so that $$ turns into $
5523 stats.push_back(c); // Add $ to stats string (we're only keeping one dollar sign)
5524 continue;
5525 } // -x- if $ -x-
5526 flag_commas = flag_right = false; // Reset these flags for a clean start
5527
5528 // --------------------------------------------------------------------------
5529 // Flag checks:
5530 // , = Include thousands separators (commas)
5531 // r = Flush right (leading spaces will be added)
5532 // --------------------------------------------------------------------------
5533 net_io_flags_loop:
5534 if (c == ',') {
5535 if (++i == len) continue; // End of format string, so we're done
5536 cmd.push_back(c = format[i]);
5537 flag_commas = true;
5538 goto net_io_flags_loop;
5539 } else if (c == 'r') {
5540 if (++i == len) continue; // End of format string, so we're done
5541 cmd.push_back(c = format[i]);
5542 flag_right = true;
5543 goto net_io_flags_loop;
5544 } // -x- if [,r] -x-
5545 // TODO: Do more
5546
5547 // --------------------------------------------------------------------------
5548 // Second character: Part 1 of 2
5549 // --------------------------------------------------------------------------
5550 if (c < 'a' || c > 'c') { // Not a, b, or c, so treat it as a regular character
5551 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
5552 continue;
5553 } // -x- if !~[abc] -x-
5554
5555 // --------------------------------------------------------------------------
5556 // Third character.
5557 // --------------------------------------------------------------------------
5558 if (++i == len) continue; // End of format string, so we're done
5559 cmd.push_back(c = format[i]);
5560 if (c != 'R' && c != 'T') { // Not R or T, so treat it as a regular character
5561 stats.append(cmd); // Add invalid command string to stats string (we're effectively ignoring it)
5562 continue;
5563 } // -x- if !~[RT] -x-
5564
5565 // --------------------------------------------------------------------------
5566 // Command processing. If the command is valid, then it will be interpolated
5567 // with its expected result by replacing it with the resulting value.
5568 // --------------------------------------------------------------------------
5569 //std::cout << "[" << cmd << "]"; // Debug
5570 if (cmd.ends_with("aR")) val = data->bytes_rx + data->crypt_rx;
5571 else if (cmd.ends_with("aS")) val = data->bytes_sx + data->crypt_sx;
5572 else if (cmd.ends_with("aT")) val = data->bytes_tx + data->crypt_tx;
5573 else if (cmd.ends_with("bR")) val = data->bytes_rx ;
5574 else if (cmd.ends_with("bS")) val = data->bytes_sx ;
5575 else if (cmd.ends_with("bT")) val = data->bytes_tx ;
5576 else if (cmd.ends_with("cR")) val = data->crypt_rx;
5577 else if (cmd.ends_with("cS")) val = data->crypt_sx;
5578 else if (cmd.ends_with("cT")) val = data->crypt_tx;
5579 else { stats.append(cmd); continue; } // This is wrong, so ignore it
5580
5581 // --------------------------------------------------------------------------
5582 // Re-use cmd to generate formatted value.
5583 // --------------------------------------------------------------------------
5584 cmd.clear();
5585 cmd.resize(21); // Maximum length without commas (plus character zero): 18446744073709551615
5586 cmd.resize(sprintf(cmd.data(), flag_right ? "%20lu" : "%lu", val)); // The ::sprintf function can't use cmd.data() here
5587 if (flag_commas) cmd = randolf::rtools::insert_commas(cmd.data()); // Insert commas (this makes the string longer)
5588
5589 // --------------------------------------------------------------------------
5590 // Convert to std::string and add to the stats string.
5591 // --------------------------------------------------------------------------
5592 stats.append(cmd);
5593
5594 } // -x- for i -x-
5595
5596 return stats;
5597 } // -x- std::string net_io -x-
5598
5599 /*======================================================================*//**
5600 @brief
5601 Where the destructor should save final I/O statistics before this rsocket's
5602 resources are completely freed/deallocated.
5603 @attention
5604 Developers should take care to check that the @ref rsocket_io::is_final flag
5605 is set to @c TRUE prior to relying on the results of the @ref rsocket_io data
5606 since there's no guarantee that the destructor will necessarily be executed
5607 in a timely manner (this flag is set last, after all other statistics are
5608 copied into the structure while in a mutex-locked state).
5609 @returns The same rsocket object so as to facilitate stacking
5610 @see ~rsocket
5611 @see net_io
5612 @see net_io_update
5613 @see printf
5614 @qualifier TLS
5615 *///=========================================================================
5616 rsocket& net_io_final(
5617 /// Pointer to @ref rsocket_io structure (nullptr = disabled)
5618 rsocket_io* addr) noexcept {
5619 __io_final_addr = addr;
5620 return *this;
5621 } // -x- rsocket& net_io_final -x-
5622
5623 /*======================================================================*//**
5624 @brief
5625 Where the destructor should save current I/O statistics.
5626 @attention
5627 Statistics are copied into the structure while in a mutex-locked state, but
5628 the copy itself is not an overall atomic snapshot of the I/O statistics even
5629 though each statistic is copied atomically. This means that the actual
5630 statistics could be slightly different if updates occur independently (e.g.,
5631 due to activities in a different thread), but this likely won't matter since
5632 the anticipated use for these statistics is to display or otherwise present
5633 general statistical information to a user at regular intervals.
5634 @returns The same rsocket object so as to facilitate stacking
5635 @see ~rsocket
5636 @see net_io
5637 @see net_io_final
5638 @see printf
5639 @qualifier TLS
5640 *///=========================================================================
5641 rsocket& net_io_update(
5642 /// Pointer to @ref rsocket_io structure (nullptr = do nothing)
5643 rsocket_io* addr) noexcept {
5644
5645 // --------------------------------------------------------------------------
5646 // Copy statistics to final location. (We do this last in case there's an
5647 // issue with locking; there shouldn't be, but if there is then at least we
5648 // already we're not inadvertently hanging on to resources at this point that
5649 // need to be freed/closed anyway.)
5650 // --------------------------------------------------------------------------
5651 if (addr != nullptr) {
5652 addr->lock();
5653 addr->bytes_rx = __bytes_rx;
5654 addr->bytes_tx = __bytes_tx;
5655 addr->bytes_sx = __tls ? 0 : (__buffer != nullptr ? __buffer->get_utilized() : 0); // <bytes in buffer; are these lost bytes that should be subtracted from __bytes_rx?>
5656 addr->crypt_rx = __crypt_rx;
5657 addr->crypt_tx = __crypt_tx;
5658 addr->crypt_sx = __tls ? (__buffer != nullptr ? __buffer->get_utilized() : 0) : 0; // <bytes in buffer; are these lost bytes that should be subtracted from __crypt_rx?>
5659 addr->is_final = false;
5660 addr->unlock();
5661 } // -x- if addr -x-
5662
5663 return *this;
5664 } // -x- rsocket& net_io_update -x-
5665
5666 /*======================================================================*//**
5667 @brief
5668 Return the number of bytes pending to be received, without actually receiving
5669 any data.
5670
5671 @note
5672 When using TLS, OpenSSL may not report all the pending data, but this is
5673 resolved here by also adding the total amount of pending raw data that is not
5674 currently being processed in the current record by OpenSSL, even when "read
5675 ahead" is enabled.
5676
5677 @throws randolf::rex::xEBADF The underlying socket is not open
5678 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5679 part of the user address space
5680 @throws randolf::rex::xEINVAL Socket is not in @ref listen() mode or the
5681 @throws randolf::rex::xENOTTY The underlying socket is not associated with a
5682 character special device, or the specified operation does not apply
5683 to the kind of object that the file descriptor fd references (this
5684 exception will probably never occur unless the underlying socket
5685 handle was arbitrarily replaced with the type of handle that can
5686 cause this error to occur)
5687
5688 @returns Total number of bytes pending (0 = none)
5689 @see eos
5690 @see recv
5691 @qualifier TLS
5692 *///=========================================================================
5693 ulong pending() {
5694 if (__debug) debug("pending(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5695 + ");");
5696
5697 // --------------------------------------------------------------------------
5698 // Get pending bytes from raw socket.
5699 // --------------------------------------------------------------------------
5700 ulong pending_bytes = 0; // BSD, glibc, etc., use ulong; older UNIX systems use int // TODO: Figure out how to detect what ioctl needs
5701 __rc_check(ioctl(__socket_fd, FIONREAD, &pending_bytes));
5702
5703 // --------------------------------------------------------------------------
5704 // Add pending bytes from OpenSSL if TLS is active, because this total isn't
5705 // included in the raw socket count since OpenSSL received these bytes.
5706 // --------------------------------------------------------------------------
5707 return __tls ? pending_bytes + SSL_pending(__tls_fd) : pending_bytes;
5708
5709 } // -x- ulong pending -x-
5710
5711 /*======================================================================*//**
5712 @brief
5713 Poll the underlying socket using the poll() method for data that's ready for
5714 receiving (default), etc.
5715
5716 @note
5717 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
5718 select(), and accept()/accept4() when using multiple sockets.
5719
5720 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5721 part of the user address space
5722 @throws randolf::rex::xEINTR Interrupted by a signal
5723 @throws randolf::rex::xENOMEM Insufficient memory
5724 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5725 doesn't refer to a socket
5726 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
5727 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
5728 is a highly improbable chance that a timeout could still occur if the
5729 data is read by another thread before the `recv(..., MSG_PEEK)` call)
5730 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5731 there's no new data
5732
5733 @returns The resulting (short)pollfd.revents bitfield
5734 @returns 0 if @c timeout_behaviour is set to TIMEOUT_ZERO
5735 @see ppoll
5736 @see rsocket_mux
5737 @qualifier POSIX
5738 @qualifier TLS
5739 *///=========================================================================
5740 // 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
5741 short poll(
5742 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
5743 const short events = POLLIN,
5744 /// Number of milliseconds to wait
5745 const int timeout = 0,
5746 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
5747 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
5748 if (__debug) debug("poll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5749 + ", pollfd.events=" + std::to_string(events)
5750 + ", timeout=" + std::to_string(timeout)
5751 + ");");
5752 struct pollfd fds{__socket_fd, events, 0};
5753 if (__rc_check(::poll(&fds, 1, timeout)) == 0) { // Timeout occurred when ::poll returns 0
5754 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
5755 else return 0;
5756 } // -x- if 0 -x-
5757 return fds.revents;
5758 } // -x- short poll -x-
5759
5760 /*======================================================================*//**
5761 @brief
5762 Get port number associated with underlying socket descriptor/handle.
5763
5764 Returns 0 if:
5765 - an emphemeral port number assignment (dynamic) is intended
5766 - the underlying socket details are not defined (e.g., in the case of an
5767 empty rsocket instantiation)
5768 - port numbers are not supported/utilized by the current family (e.g., not
5769 AF_INET {IPv4} or AF_INET6 {IPv6})
5770
5771 The port number can be set in most constructors, or via one of the socket()
5772 or bind() methods.
5773 @returns Port number (typically TCP and UDP, although some other families
5774 such as SCTP and DCCP also utilize port numbers)
5775 @see socket_family()
5776 @see socket_protocol()
5777 @see socket_type()
5778 @qualifier TLS
5779 *///=========================================================================
5780 uint16_t port() noexcept {
5781 switch (__socket_addr->ss_family) {
5782 case AF_INET: // IPv4
5783// return ntohs(((sockaddr_in*)&__socket_addr)->sin_port);
5784 return ntohs(((sockaddr_in*)__socket_addr)->sin_port);
5785 case AF_INET6: // IPv6
5786// return ntohs(((sockaddr_in6*)&__socket_addr)->sin6_port);
5787 return ntohs(((sockaddr_in6*)__socket_addr)->sin6_port);
5788 default: // Everything else
5789 return 0;
5790 } // -x- switch __socket_addr->ss_family -x-
5791 } // -x- uint16_t port -x-
5792
5793 /*======================================================================*//**
5794 @brief
5795 Poll the underlying socket using the ppoll() method for data that's ready for
5796 receiving (default), etc.
5797
5798 @note
5799 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
5800 select(), and accept()/accept4() when using multiple sockets.
5801
5802 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5803 part of the user address space
5804 @throws randolf::rex::xEINTR Interrupted by a signal
5805 @throws randolf::rex::xENOMEM Insufficient memory
5806 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5807 doesn't refer to a socket
5808 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
5809 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
5810 is a highly improbable chance that a timeout could still occur if the
5811 data is read by another thread before the `recv(..., MSG_PEEK)` call)
5812 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5813 there's no new data
5814
5815 @returns The resulting (short)pollfd.revents bitfield
5816 @see poll
5817 @see rsocket_mux
5818 @qualifier POSIX
5819 @qualifier TLS
5820 *///=========================================================================
5821 // 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
5822 short ppoll(
5823 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
5824 const short events = POLLIN,
5825 /// Timeout
5826 const struct timespec* tmo_p = nullptr,
5827 /// Signal mask
5828 const sigset_t* sigmask = nullptr,
5829 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
5830 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
5831 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5832 + ", pollfd.events=" + std::to_string(events)
5833 + ", timespec{tv_sec=" + std::to_string(tmo_p->tv_sec)
5834 + ", tv_nsec=" + std::to_string(tmo_p->tv_nsec) + "}"
5835 + ", sigset_t"
5836 + ");");
5837 struct pollfd fds{__socket_fd, events, 0};
5838 if (__rc_check(::ppoll(&fds, 1, tmo_p, sigmask)) == 0) { // Timeout occurred when ::ppoll returns 0
5839 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
5840 else return 0;
5841 } // -x- if 0 -x-
5842 return fds.revents;
5843 } // -x- short ppoll -x-
5844
5845 /*======================================================================*//**
5846 @brief
5847 Poll the underlying socket using the ppoll() method for data that's ready for
5848 receiving (default), etc.
5849
5850 @note
5851 The @ref rsocket_mux class provides methods that support poll(), ppoll(),
5852 select(), and accept()/accept4() when using multiple sockets.
5853
5854 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
5855 part of the user address space
5856 @throws randolf::rex::xEINTR Interrupted by a signal
5857 @throws randolf::rex::xENOMEM Insufficient memory
5858 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
5859 doesn't refer to a socket
5860 @throws randolf::rex::xETIMEDOUT Timeout period elapsed (even if the @c
5861 TIMEOUT_BEHAVIOUR flag is @em not set to @c TIMEOUT_EXCEPTION, there
5862 is a highly improbable chance that a timeout could still occur if the
5863 data is read by another thread before the `recv(..., MSG_PEEK)` call)
5864 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
5865 there's no new data
5866
5867 @returns The resulting (short)pollfd.revents bitfield
5868 @see ppoll
5869 @see rsocket_mux
5870 @qualifier TLS
5871 *///=========================================================================
5872 // 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
5873 short ppoll(
5874 /// Events bitfield (e.g., @c POLLIN, @c POLLOUT, and @c POLLERR)
5875 const short events = POLLIN,
5876 /// Timeout in seconds
5877 const long tv_sec = 0,
5878 /// Timeout in nanoseconds
5879 const long tv_nsec = 0,
5880 /// Signal mask
5881 const sigset_t* sigmask = nullptr,
5882 /// Timeout behaviour (see @ref TIMEOUT_BEHAVIOUR for details)
5883 const bool timeout_behaviour = TIMEOUT_EXCEPTION) {
5884 struct timespec tmo_p{tv_sec, tv_nsec};
5885 if (__debug) debug("ppoll(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
5886 + ", pollfd.events=" + std::to_string(events)
5887 + ", timespec{tv_sec=" + std::to_string(tmo_p.tv_sec)
5888 + ", tv_nsec=" + std::to_string(tmo_p.tv_nsec) + "}"
5889 + ", sigset_t"
5890 + ");");
5891 struct pollfd fds{__socket_fd, events, 0};
5892 if (__rc_check(::ppoll(&fds, 1, &tmo_p, sigmask)) == 0) { // Timeout occurred when ::ppoll returns 0
5893 if (timeout_behaviour) throw randolf::rex::xETIMEDOUT("ETIMEDOUT");
5894 else return 0;
5895 } // -x- if 0 -x-
5896 return fds.revents;
5897 } // -x- short ppoll -x-
5898
5899 /*======================================================================*//**
5900 @brief
5901 Send a formatted string to the @ref rsocket endpoint.
5902
5903 The @c format is described in the documentation for the POSIX or Standard C
5904 Library @c printf() function.
5905 @throws randolf::rex::xEBADF The underlying socket is not open
5906 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
5907 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
5908 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
5909 @throws randolf::rex::xENOMEM Insufficient memory
5910 @returns The same rsocket object so as to facilitate stacking
5911 @see eol_fix_printf
5912 @see is_eol_fix_printf
5913 @see net_io
5914 @see printfline
5915 @see vprintf
5916 @see vprintfline
5917 @qualifier POSIX
5918 @qualifier TLS
5919 *///=========================================================================
5920 rsocket& printf(
5921 /// Format string to use
5922 const char* format,
5923 /// Variadic arguments
5924 ...) {
5925 char* buf = nullptr;
5926 ::va_list args;
5927 ::va_start(args, format);
5928 int rc = ::vasprintf(&buf, format, args);
5929 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
5930 if (rc < 0 && buf != nullptr) ::free(buf);
5931 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
5932 if (__eol_fix_printf && !__eol.empty()) {
5933 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
5934 ::free(buf);
5935 __send(str.data(), str.length());
5936 } else {
5937 try {
5938 __send(buf, rc);
5939 ::free(buf);
5940 } catch (std::exception& e) { // Free buf then re-throw the exception
5941 ::free(buf); // Prevent memory leak when an exception is thrown
5942 throw;
5943 }
5944 } // -x- if __eol_fix_printf -x-
5945 return *this;
5946 } // -x- rsocket& printf -x-
5947
5948 /*======================================================================*//**
5949 @brief
5950 Send a formatted string to the @ref rsocket endpoint, and append an EoL
5951 sequence.
5952
5953 The @c format is described in the documentation for the POSIX or Standard C
5954 Library @c printf() function.
5955 @throws randolf::rex::xEBADF The underlying socket is not open
5956 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
5957 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
5958 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
5959 @throws randolf::rex::xENOMEM Insufficient memory
5960 @returns The same rsocket object so as to facilitate stacking
5961 @see eol
5962 @see eol_fix_printf
5963 @see is_eol_fix_printf
5964 @see net_io
5965 @see printf
5966 @see sendline
5967 @see vprintf
5968 @see vprintfline
5969 @qualifier TLS
5970 *///=========================================================================
5971 rsocket& printfline(
5972 /// Format string to use
5973 const char* format,
5974 /// Variadic arguments
5975 ...) {
5976 char* buf = nullptr;
5977 ::va_list args;
5978 ::va_start(args, format);
5979 int rc = ::vasprintf(&buf, format, args);
5980 ::va_end(args); // Cleanup must occur before calling __rc_check to prevent memory leak in case of exception
5981 if (rc < 0 && buf != nullptr) ::free(buf);
5982 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
5983 if (__eol_fix_printf && !__eol.empty()) {
5984 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
5985 .append(__eol);
5986 ::free(buf);
5987 __send(str.data(), str.length());
5988 } else {
5989 try {
5990 __sendline(buf, rc);
5991 ::free(buf);
5992 } catch (std::exception& e) { // Free buf then re-throw the exception
5993 ::free(buf); // Prevent memory leak when an exception is thrown
5994 throw;
5995 }
5996 } // -x- if __eol_fix_printf -x-
5997 return *this;
5998 } // -x- rsocket& printfline -x-
5999
6000 /*======================================================================*//**
6001 @brief
6002 Receive data from the endpoint into a @c std::vector<char> that is allocated
6003 on-the-fly.
6004
6005 If nbytes is 0, then the internal @ref buffer_size() will be used as the
6006 default, which can also be changed from its compiled-in default of 1024 by
6007 using one of the buffer_size() methods.
6008
6009 @par Notes
6010 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6011 reception, but it's important to note that it does implement temporary
6012 blocking while waiting for data.
6013
6014 @throws randolf::rex::xEBADF The underlying socket is not open
6015 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6016 connections
6017 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6018 part of the user address space
6019 @throws randolf::rex::xEINTR Interrupted by a signal
6020 @throws randolf::rex::xEINVAL Invalid argument passed
6021 @throws randolf::rex::xENOMEM Insufficient memory
6022 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6023 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6024 doesn't refer to a socket
6025 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6026 there's no new data
6027
6028 @returns appropriately-sized vector of characters
6029 @see recv(std::vector<char>, const int)
6030 @see recvz(const size_t, const int)
6031 @see send
6032 @see send(const std::vector<char>, const int)
6033 @qualifier TLS
6034 *///=========================================================================
6035 std::vector<char> recv(
6036 /// Maximum number of bytes to receive
6037 const size_t nbytes = 0,
6038 /// MSG_OOB@n
6039 /// MSG_PEEK@n
6040 /// MSG_WAITALL@n
6041 /// MSG_DONTWAIT@n
6042 /// MSG_CMSG_CLOEXEC
6043 const int posix_flags = 0) {
6044 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6045 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6046 + ", <buf>"
6047 + ", " + std::to_string(buf_size)
6048 + ", " + std::to_string(posix_flags)
6049 + ");");
6050 std::vector<char> buf(buf_size);
6051 buf.resize(__recv(buf.data(), buf.size(), posix_flags));
6052 return buf;
6053 } // -x- std::vector<char> recv -x-
6054
6055 /*======================================================================*//**
6056 @brief
6057 Receive data from the endpoint into the @c std::vector object supplied in the
6058 @c buf parameter, overwriting any pre-existing data.
6059
6060 The maximum number of bytes that can be received won't exceed the number of
6061 bytes that the supplied @c std::vector<char> was initialized or resized to.
6062
6063 @pre
6064 For @c std::vector it's important that the @c resize() method is used to
6065 pre-allocate the underlying char[] array, instead of the unfortunately-named
6066 @c reserve() method that doesn't pre-allocate, to avoid causing segmentation
6067 faults or other undefined behaviours.
6068
6069 @par Notes
6070 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6071 reception, but it's important to note that it does implement temporary
6072 blocking while waiting for data.
6073
6074 @throws randolf::rex::xEBADF The underlying socket is not open
6075 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6076 connections
6077 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6078 part of the user address space
6079 @throws randolf::rex::xEINTR Interrupted by a signal
6080 @throws randolf::rex::xEINVAL Invalid argument passed
6081 @throws randolf::rex::xENOMEM Insufficient memory
6082 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6083 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6084 doesn't refer to a socket
6085 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6086 there's no new data
6087
6088 @returns The same array that was specified in the @c buf parameter
6089 @see recv(const size_t, const int)
6090 @see recvz(const size_t, const int)
6091 @see recv_append_to(std::vector&, const int)
6092 @see send(const std::vector<char>, const int)
6093 @qualifier POSIX
6094 @qualifier TLS
6095 *///=========================================================================
6096 std::vector<char>& recv(
6097 /// Target std::vector<char> to receive data into (any existing data residing
6098 /// in this @c vector will be overwritten)
6099 std::vector<char>& buf,
6100 /// MSG_OOB@n
6101 /// MSG_PEEK@n
6102 /// MSG_WAITALL@n
6103 /// MSG_DONTWAIT@n
6104 /// MSG_CMSG_CLOEXEC
6105 const int posix_flags = 0) {
6106 if (__debug) debug("recv{vector}(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6107 + ", <buf>"
6108 + ", " + std::to_string(buf.size())
6109 + ", " + std::to_string(posix_flags)
6110 + ");");
6111 buf.resize(__recv(buf.data(), buf.size(), posix_flags));
6112 return buf;
6113 } // -x- std::vector<char>& recv -x-
6114
6115 /*======================================================================*//**
6116 @brief
6117 Receive data from the endpoint, appending the received data directly into a
6118 @c std::string object.
6119
6120 If nbytes is 0, then the internal @ref buffer_size() will be used as the
6121 default, which can also be changed from its compiled-in default of 1024 by
6122 using one of the buffer_size() methods.
6123
6124 @par Notes
6125 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6126 reception, but it's important to note that it does implement temporary
6127 blocking while waiting for data.
6128
6129 @throws randolf::rex::xEBADF The underlying socket is not open
6130 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6131 connections
6132 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6133 part of the user address space
6134 @throws randolf::rex::xEINTR Interrupted by a signal
6135 @throws randolf::rex::xEINVAL Invalid argument passed
6136 @throws randolf::rex::xENOMEM Insufficient memory
6137 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6138 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6139 doesn't refer to a socket
6140 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6141 there's no new data
6142
6143 @returns reference to the same std::string that data was appended to,
6144 appropriately sized
6145 @see recv
6146 @see recv(std::vector<char>, const int)
6147 @see recv_append_to(std::vector<char>&, const int)
6148 @see recv_as_string
6149 @see recvz(const size_t, const int)
6150 @see send(const std::string, const int)
6151 @qualifier TLS
6152 *///=========================================================================
6153 std::string& recv_append_to(
6154 /// Where to append the data
6155 std::string& buf,
6156 /// Maximum number of bytes to receive
6157 const size_t nbytes = 0,
6158 /// MSG_OOB@n
6159 /// MSG_PEEK@n
6160 /// MSG_WAITALL@n
6161 /// MSG_DONTWAIT@n
6162 /// MSG_CMSG_CLOEXEC
6163 const int posix_flags = 0) {
6164 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6165 if (__debug) debug("recv_append_to(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6166 + ", <(std::string&)buf>"
6167 + ", " + std::to_string(buf_size)
6168 + ", " + std::to_string(posix_flags)
6169 + ");");
6170 int target_size = buf.size();
6171 buf.resize(target_size + buf_size); // Pre-fill additional anticipated string size
6172 buf.resize(target_size + __recv((void*)(buf.data() + target_size), buf_size, posix_flags)); // Shorten string
6173 return buf;
6174 } // -x- std::string& recv_append_to -x-
6175
6176 /*======================================================================*//**
6177 @brief
6178 Receive data from the endpoint, appending the received data directly into a
6179 `std::vector<char>` object.
6180
6181 If nbytes is 0, then the internal @ref buffer_size() will be used as the
6182 default, which can also be changed from its compiled-in default of 1024 by
6183 using one of the buffer_size() methods.
6184
6185 @par Notes
6186 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6187 reception, but it's important to note that it does implement temporary
6188 blocking while waiting for data.
6189
6190 @throws randolf::rex::xEBADF The underlying socket is not open
6191 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6192 connections
6193 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6194 part of the user address space
6195 @throws randolf::rex::xEINTR Interrupted by a signal
6196 @throws randolf::rex::xEINVAL Invalid argument passed
6197 @throws randolf::rex::xENOMEM Insufficient memory
6198 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6199 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6200 doesn't refer to a socket
6201 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6202 there's no new data
6203
6204 @returns reference to the same std::string that data was appended to,
6205 appropriately sized
6206 @see recv
6207 @see recv(std::vector<char>, const int)
6208 @see recv_append_to(std::string&, const int)
6209 @see recvz(const size_t, const int)
6210 @see send(const std::string, const int)
6211 @qualifier TLS
6212 *///=========================================================================
6213 std::vector<char>& recv_append_to(
6214 /// Where to append the data
6215 std::vector<char>& buf,
6216 /// Maximum number of bytes to receive
6217 const size_t nbytes = 0,
6218 /// MSG_OOB@n
6219 /// MSG_PEEK@n
6220 /// MSG_WAITALL@n
6221 /// MSG_DONTWAIT@n
6222 /// MSG_CMSG_CLOEXEC
6223 const int posix_flags = 0) {
6224 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6225 if (__debug) debug("recv_append_to(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6226 + ", <(std::vector<char>&)buf>"
6227 + ", " + std::to_string(buf_size)
6228 + ", " + std::to_string(posix_flags)
6229 + ");");
6230 int target_size = buf.size();
6231 buf.resize(target_size + buf_size); // Pre-fill additional anticipated string size
6232 buf.resize(target_size + __recv((void*)(buf.data() + target_size), buf_size, posix_flags)); // Shorten string
6233 return buf;
6234 } // -x- std::vector<char>& recv_append_to -x-
6235
6236 /*======================================================================*//**
6237 @brief
6238 Receive data from the endpoint into a @c std::string object that is allocated
6239 on-the-fly.
6240
6241 If nbytes is 0, then the internal @ref buffer_size() will be used as the
6242 default, which can also be changed from its compiled-in default of 1024 by
6243 using one of the buffer_size() methods.
6244
6245 @par Notes
6246 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6247 reception, but it's important to note that it does implement temporary
6248 blocking while waiting for data.
6249
6250 @throws randolf::rex::xEBADF The underlying socket is not open
6251 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6252 connections
6253 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6254 part of the user address space
6255 @throws randolf::rex::xEINTR Interrupted by a signal
6256 @throws randolf::rex::xEINVAL Invalid argument passed
6257 @throws randolf::rex::xENOMEM Insufficient memory
6258 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6259 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6260 doesn't refer to a socket
6261 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6262 there's no new data
6263
6264 @returns appropriately-sized std::string of characters
6265 @see recv
6266 @see recv(std::vector<char>, const int)
6267 @see recv_append_to(std::string&, const int)
6268 @see recvz(const size_t, const int)
6269 @see send(const std::string, const int)
6270 @qualifier TLS
6271 *///=========================================================================
6272 std::string recv_as_string(
6273 /// Maximum number of bytes to receive
6274 const size_t nbytes = 0,
6275 /// MSG_OOB@n
6276 /// MSG_PEEK@n
6277 /// MSG_WAITALL@n
6278 /// MSG_DONTWAIT@n
6279 /// MSG_CMSG_CLOEXEC
6280 const int posix_flags = 0) {
6281 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6282 if (__debug) debug("recv_as_string(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6283 + ", " + std::to_string(buf_size)
6284 + ", " + std::to_string(posix_flags)
6285 + ");");
6286 std::string buf;
6287 buf.resize(buf_size); // Pre-fill anticipated string size
6288 buf.resize(__recv(buf.data(), buf_size, posix_flags)); // Shorten string
6289 return buf;
6290 } // -x- std::string recv_as_string -x-
6291
6292 /*======================================================================*//**
6293 @brief
6294 Receive an ASCIIZ string from the endpoint, including the NULL terminator.
6295
6296 @throws randolf::rex::xEBADF The underlying socket is not open
6297 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6298 connections
6299 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6300 part of the user address space
6301 @throws randolf::rex::xEINTR Interrupted by a signal
6302 @throws randolf::rex::xEINVAL Invalid argument passed
6303 @throws randolf::rex::xENOMEM Insufficient memory
6304 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6305 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6306 doesn't refer to a socket
6307 @throws randolf::rex::xERANGE if no NULL terminator is detected (this may
6308 occur before the underlying ASCIIZ string char* array is allocated)
6309 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6310 there's no new data
6311
6312 @returns Pointer to ASCIIZ string (will need to be deleted by caller)
6313 @see send_asciiz(const char*, const int)
6314 @qualifier TLS
6315 *///=========================================================================
6316 char* recv_asciiz(
6317 /// Maximum number of bytes to receive
6318 const size_t nbytes = 0,
6319 /// MSG_OOB@n
6320 /// MSG_PEEK@n
6321 /// MSG_WAITALL@n
6322 /// MSG_DONTWAIT@n
6323 /// MSG_CMSG_CLOEXEC
6324 const int posix_flags = 0) {
6325 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
6326 if (__debug) debug("recv_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6327 + ", " + std::to_string(buf_size)
6328 + ", " + std::to_string(posix_flags)
6329 + ");");
6330
6331 // --------------------------------------------------------------------------
6332 // Calculate size of buffer (includes NULL terminator since we'll also be
6333 // receiving this from the endpoint).
6334 // --------------------------------------------------------------------------
6335 char buf[buf_size];
6336
6337 // --------------------------------------------------------------------------
6338 // Reduce buffer size to what is actually read (remember: we don't actually
6339 // know where the EoL sequence is yet, or if there even is one).
6340 // --------------------------------------------------------------------------
6341 int max = __recv(&buf, buf_size, posix_flags | MSG_PEEK); // Look-ahead at socket stream (buffer was inflated to include EoL sequence)
6342 int len = -1;
6343 for (int i = 0; i < max; i++) {
6344 if (buf[i] == 0) { // EoD
6345 len = i;
6346 break;
6347 } // -x- if v[i] -x-
6348 } // -x- for i -x-
6349 if (len < 0) throw randolf::rex::xERANGE("ERANGE: NULL terminator not found");
6350
6351 // --------------------------------------------------------------------------
6352 // I'd love to use std::unique_ptr<char*> for this next part, but it leads
6353 // to intermittent segmentation faults and/or "malloc(): corrupted top size
6354 // occurs" errors outside of this method. So, my conclusion is that char*
6355 // (and char[], as I've tried with this too) are not properly supported by:
6356 // std::unique_ptr<char*> v = std::make_shared<char*>(new char[len + 1]);
6357 //
6358 // Using std::string() is really not what we want because there is confusion
6359 // concerning the NULL terminator -- with char* strings, it's crystal clear
6360 // that there must be a NULL terminator (since a length isn't tracked). Now,
6361 // if you want an std::string, just initialize as follows:
6362 //
6363 // char* temp_string = r.recv_asciiz();
6364 // std::string mystr = std::string(temp_string);
6365 // delete temp_string;
6366 // --------------------------------------------------------------------------
6367 char* v = new char[len + 1];
6368 int rc = __recv(v, len + 1, posix_flags); // Consume with NULL terminator from socket stream
6369// TODO: Free "v" in a catch-and-rethrow block (check other calls to __recv and __send too)
6370 return v;
6371
6372 } // -x- char* recv_asciiz -x-
6373
6374 /*======================================================================*//**
6375 @brief
6376 Receive one byte (unsigned 8-bit byte) of data from the endpoint.
6377 @par Notes
6378 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6379 reception, but it's important to note that it does implement temporary
6380 blocking while waiting for data.
6381
6382 @throws randolf::rex::xEBADF The underlying socket is not open
6383 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6384 connections
6385 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6386 part of the user address space
6387 @throws randolf::rex::xEINTR Interrupted by a signal
6388 @throws randolf::rex::xEINVAL Invalid argument passed
6389 @throws randolf::rex::xENOMEM Insufficient memory
6390 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6391 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6392 doesn't refer to a socket
6393 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6394 there's no new data
6395
6396 @returns one unsigned character
6397 @see recv(std::vector<char>, const int)
6398 @see recvz(const size_t, const int)
6399 @see recv_char
6400 @see send_byte
6401 @see send_char
6402 @qualifier TLS
6403 *///=========================================================================
6404 u_char recv_byte(
6405 /// MSG_OOB@n
6406 /// MSG_PEEK@n
6407 /// MSG_WAITALL@n
6408 /// MSG_DONTWAIT@n
6409 /// MSG_CMSG_CLOEXEC
6410 const int posix_flags = 0) {
6411 char buf(0);
6412 if (__debug) debug("recv_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6413 + ", <buf>"
6414 + ", " + std::to_string(sizeof(buf))
6415 + ", " + std::to_string(posix_flags)
6416 + ");");
6417 int rc = __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6418 return buf;
6419 } // -x- byte recv_byte -x-
6420
6421 /*======================================================================*//**
6422 @brief
6423 Receive one character (signed 8-bit byte) of data from the endpoint.
6424 @par Notes
6425 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6426 reception, but it's important to note that it does implement temporary
6427 blocking while waiting for data.
6428
6429 @throws randolf::rex::xEBADF The underlying socket is not open
6430 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6431 connections
6432 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6433 part of the user address space
6434 @throws randolf::rex::xEINTR Interrupted by a signal
6435 @throws randolf::rex::xEINVAL Invalid argument passed
6436 @throws randolf::rex::xENOMEM Insufficient memory
6437 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6438 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6439 doesn't refer to a socket
6440 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6441 there's no new data
6442
6443 @returns one signed character
6444 @see recv(std::vector<char>, const int)
6445 @see recvz(const size_t, const int)
6446 @see recv_byte
6447 @see send_byte
6448 @see send_char
6449 @qualifier TLS
6450 *///=========================================================================
6451 char recv_char(
6452 /// MSG_OOB@n
6453 /// MSG_PEEK@n
6454 /// MSG_WAITALL@n
6455 /// MSG_DONTWAIT@n
6456 /// MSG_CMSG_CLOEXEC
6457 const int posix_flags = 0) {
6458 char buf(0);
6459 if (__debug) debug("recv_char(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6460 + ", <buf>"
6461 + ", " + std::to_string(sizeof(buf))
6462 + ", " + std::to_string(posix_flags)
6463 + ");");
6464 int rc = __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6465 // How to detect disconnected stream when telnet user presses CTRL-C?
6466 //std::cout << "rc=" << std::to_string(rc) << std::endl; // Debug (to be removed)
6467// TODO: Investigate checking platform's char size and moving signing bit accordingly
6468 return buf;
6469 } // -x- char recv_char -x-
6470
6471 /*======================================================================*//**
6472 @brief
6473 Receive a line of data from the endpoint, into a new @ref randolf::rline
6474 object, with the EoL character(s) isolated. This is meant for multiline
6475 ASCII and UTF-8 text, and while it will also work with binary data that
6476 doesn't include any EoL sequence characters (ASCII `10` and `13`),
6477 @ref recv(), @ref recvz(), and other methods are much better-suited to
6478 receive binary data.
6479
6480 This is essentially a wrapper around what recvline() does, but returns both
6481 the line of text and the EoL sequence together in a new @ref randolf::rline
6482 object.
6483 @note
6484 For additional details on the other parameters, please see the @ref recvline
6485 method's documentation for the remaining details.
6486 @warning
6487 If you're using a customzied EoL sequence, then it's important to note that
6488 it probably won't be recognized by the @ref randolf::rline class, but do also
6489 check that documentation to confirm this.
6490
6491 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
6492 @throws randolf::rex::xEBADF The underlying socket is not open
6493 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6494 connections
6495 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6496 part of the user address space
6497 @throws randolf::rex::xEINTR Interrupted by a signal
6498 @throws randolf::rex::xEINVAL Invalid argument passed
6499 @throws randolf::rex::xENOMEM Insufficient memory
6500 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6501 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6502 doesn't refer to a socket
6503 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
6504 no EoL character sequence is detected after recvline's buffer became
6505 full (whatever data is waiting may still be received using any of the
6506 recv_ methods {with or without the @c MSG_PEEK flag set}, including
6507 @c recvline() with a larger buffer {see the @c nbytes parameter})
6508 @throws randolf::rex::xERANGE if the timeout parameter is below 0
6509 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6510 there's no new data
6511
6512 @returns One line of text as a randolf::rline object.
6513 @see discard
6514 @see eol()
6515 @see eol_consumed_seq()
6516 @see randolf::rline
6517 @see recvline
6518 @see send_rline(const randolf::rline, const int)
6519 @see sendline(const std::string, const int)
6520 @see timeout
6521 @see timeout_recvline
6522 @see timeout_recvline(long)
6523 @qualifier TLS
6524 *///=========================================================================
6525 randolf::rline recv_rline(
6526 /// Maximum number of bytes to receive (including EoL character sequence)
6527 const size_t nbytes = 0,
6528 /// MSG_OOB@n
6529 /// MSG_PEEK@n
6530 /// MSG_WAITALL@n
6531 /// MSG_DONTWAIT@n
6532 /// MSG_CMSG_CLOEXEC
6533 const int posix_flags = 0,
6534 /// Line timeout (in seconds)@n
6535 /// 0 = no timeout (default), unless it was configured by way of the
6536 /// @ref timeout_recvline(long) method
6537 long timeout = 0,
6538 /// Configuration parameters
6539 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
6540 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
6541 // + (__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
6542 // Internally, the length of the @ref eol() sequence is added to the buffer size
6543 // so that the maximum line length without EoL characters matches the maximum
6544 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
6545 if (__debug) debug("recv_rline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6546 + ", " + std::to_string(buf_size)
6547 + ", " + std::to_string(posix_flags)
6548 + ");");
6549 std::string str;
6550 __recvline(str, buf_size, posix_flags, timeout, recvline_flags);
6551 return randolf::rline(str, __eol_consumed_seq);
6552 } // -x- randolf::rline recv_rline -x-
6553
6554 /*======================================================================*//**
6555 @brief
6556 Receive a line of data from the endpoint assigned into the specifieid
6557 @ref randolf::rline object, with the EoL character(s) isolated. This is
6558 meant for multiline ASCII and UTF-8 text, and while it will also work with
6559 binary data that doesn't include any EoL sequence characters (ASCII `10` and
6560 `13`), @ref recv(), @ref recvz(), and other methods are much better-suited to
6561 receive binary data.
6562
6563 @note
6564 Be sure to read the docuemntation for the @c eol and @c eol_adoption methods,
6565 especially if you're using @c recvline or @c recv_rline to implement an
6566 internet daemon, because pre-configuring the EoL sequence is an important
6567 consideration for some protocols.
6568
6569 This is essentially a wrapper around what recvline() does, but returns both
6570 the line of text and the EoL sequence together, assigned into the existing
6571 @ref randolf::rline object.
6572 @note
6573 For additional details on the other parameters, please see the @ref recvline
6574 method's documentation for the remaining details.
6575 @warning
6576 If you're using a customzied EoL sequence, then it's important to note that
6577 it probably won't be recognized by the @ref randolf::rline class, but do also
6578 check that documentation to confirm this.
6579 @post
6580 When providing @c nullptr for the line parameter, a new @ref randolf::rline
6581 object will be instantiated that will then need to be deleted after it's no
6582 longer needed.
6583
6584 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
6585 @throws randolf::rex::xEBADF The underlying socket is not open
6586 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6587 connections
6588 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6589 part of the user address space
6590 @throws randolf::rex::xEINTR Interrupted by a signal
6591 @throws randolf::rex::xEINVAL Invalid argument passed
6592 @throws randolf::rex::xENOMEM Insufficient memory
6593 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6594 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6595 doesn't refer to a socket
6596 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
6597 no EoL character sequence is detected after recvline's buffer became
6598 full (whatever data is waiting may still be received using any of the
6599 recv_ methods {with or without the @c MSG_PEEK flag set}, including
6600 @c recvline() with a larger buffer {see the @c nbytes parameter})
6601 @throws randolf::rex::xERANGE if the timeout parameter is below 0
6602 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6603 there's no new data
6604
6605 @returns One line of text as a randolf::rline object.
6606 @see discard
6607 @see eol()
6608 @see eol_consumed_seq()
6609 @see eol_adoption()
6610 @see randolf::rline
6611 @see recvline
6612 @see send_rline(const randolf::rline, const int)
6613 @see sendline(const std::string, const int)
6614 @see timeout
6615 @see timeout_recvline
6616 @see timeout_recvline(long)
6617 @qualifier TLS
6618 *///=========================================================================
6619 randolf::rline* recv_rline(
6620 /// Pointer to the pre-instantiated randolf::rline object to assign into@n
6621 /// nullptr = instantiate a new randolf::rline object internally, with this
6622 /// rsocket's current EoL sequence set to override the default
6623 randolf::rline* line,
6624 /// Maximum number of bytes to receive (including EoL character sequence)
6625 const size_t nbytes = 0,
6626 /// MSG_OOB@n
6627 /// MSG_PEEK@n
6628 /// MSG_WAITALL@n
6629 /// MSG_DONTWAIT@n
6630 /// MSG_CMSG_CLOEXEC
6631 const int posix_flags = 0,
6632 /// Line timeout (in seconds)@n
6633 /// 0 = no timeout (default), unless it was configured by way of the
6634 /// @ref timeout_recvline(long) method
6635 long timeout = 0,
6636 /// Configuration parameters
6637 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
6638 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
6639 // + (__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
6640 // Internally, the length of the @ref eol() sequence is added to the buffer size
6641 // so that the maximum line length without EoL characters matches the maximum
6642 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
6643 if (__debug) debug("recv_rline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6644 + ", <randolf::rline*>"
6645 + ", " + std::to_string(buf_size)
6646 + ", " + std::to_string(posix_flags)
6647 + ", " + std::to_string(timeout)
6648 + ", " + std::to_string(recvline_flags)
6649 + ");");
6650
6651 // --------------------------------------------------------------------------
6652 // Instantiate new object if nullptr was provided.
6653 // --------------------------------------------------------------------------
6654 if (line == nullptr) line = new randolf::rline("", __eol);
6655
6656 // --------------------------------------------------------------------------
6657 // Append received line of data to line, and return it.
6658 // --------------------------------------------------------------------------
6659 std::string str;
6660 __recvline(str, buf_size, posix_flags, timeout, recvline_flags);
6661 line->assign(str + __eol_consumed_seq);
6662
6663 return line;
6664 } // -x- randolf::rline* recv_rline -x-
6665
6666 /*======================================================================*//**
6667 @brief
6668 This method is the same as the @ref recv_rline() method, except that it also
6669 appends data to an existing @ref randolf::rline line. If the line provided
6670 is longer than @c nbytes bytes, then the line will be returned as is, without
6671 any attempts made to receive more data nor to search for an EoL sequence.
6672
6673 @note
6674 It's important to make sure that the EoL sequence is the same for both this
6675 rsocket object and for the rline object provided, otherwise the results could
6676 be undefined.
6677
6678 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
6679 @throws randolf::rex::xEBADF The underlying socket is not open
6680 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6681 connections
6682 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6683 part of the user address space
6684 @throws randolf::rex::xEINTR Interrupted by a signal
6685 @throws randolf::rex::xEINVAL Invalid argument passed
6686 @throws randolf::rex::xENOMEM Insufficient memory
6687 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6688 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6689 doesn't refer to a socket
6690 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
6691 no EoL character sequence is detected after recvline's buffer became
6692 full (whatever data is waiting may still be received using any of the
6693 recv_ methods {with or without the @c MSG_PEEK flag set}, including
6694 @c recvline() with a larger buffer {see the @c nbytes parameter})
6695 @throws randolf::rex::xERANGE if the timeout parameter is below 0
6696 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6697 there's no new data
6698
6699 @returns One line of text as a randolf::rline object.
6700 @see discard
6701 @see eol()
6702 @see eol_consumed_seq()
6703 @see eol_adoption()
6704 @see randolf::rline
6705 @see recvline
6706 @see send_rline(const randolf::rline, const int)
6707 @see sendline(const std::string, const int)
6708 @see timeout
6709 @see timeout_recvline
6710 @see timeout_recvline(long)
6711 @qualifier TLS
6712 *///=========================================================================
6713 randolf::rline& recv_rline_append_to(
6714 /// Where to append the data
6715 randolf::rline& line,
6716 /// Maximum number of bytes to receive (including EoL character sequence)
6717 const size_t nbytes = 0,
6718 /// MSG_OOB@n
6719 /// MSG_PEEK@n
6720 /// MSG_WAITALL@n
6721 /// MSG_DONTWAIT@n
6722 /// MSG_CMSG_CLOEXEC
6723 const int posix_flags = 0,
6724 /// Line timeout (in seconds)@n
6725 /// 0 = no timeout (default), unless it was configured by way of the
6726 /// @ref timeout_recvline(long) method
6727 long timeout = 0,
6728 /// Configuration parameters
6729 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
6730 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
6731 // + (__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
6732 // Internally, the length of the @ref eol() sequence is added to the buffer size
6733 // so that the maximum line length without EoL characters matches the maximum
6734 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
6735 if (__debug) debug("recv_rline_append_to(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6736 + ", <randolf::rline>"
6737 + ", " + std::to_string(buf_size)
6738 + ", " + std::to_string(posix_flags)
6739 + ", " + std::to_string(timeout)
6740 + ", " + std::to_string(recvline_flags)
6741 + ");");
6742
6743 // --------------------------------------------------------------------------
6744 // Append received line of data to line, and return it.
6745 // --------------------------------------------------------------------------
6746 if (line.size(true) < nbytes) {
6747 std::string str(line.get(true));
6748//std::cout << "str[0]=" << str << std::endl; // Debug (for advanced edge cases, which has been resolved)
6749 __recvline(str, buf_size, posix_flags, timeout, recvline_flags);
6750//std::cout << "str[1]=" << str << std::endl; // Debug (for advanced edge cases, which has been resolved)
6751 line.assign(str + __eol_consumed_seq);
6752 } // -x- if line.sizeI(true) -x-
6753
6754 return line;
6755 } // -x- randolf::rline* recv_rline_append_to -x-
6756
6757 /*======================================================================*//**
6758 @brief
6759 Receive a data structure from the endpoint.
6760 @post
6761 MSB/LSB considerations are important for any integers within your structure,
6762 so be sure to sanitize them with htons(), ntohs(), and related methods prior
6763 to sending, and then after receiving. This way, your data will be protected
6764 against corruption resulting from byte order differences when communicating
6765 between hardware architectures that differ in MSB and LSB byte ordering.
6766 @par Notes
6767 The @c MSG_WAITALL flag is particularly useful for preventing premature data
6768 reception, but it's important to note that it does implement temporary
6769 blocking while waiting for data.
6770
6771 @throws randolf::rex::xEBADF The underlying socket is not open
6772 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6773 connections
6774 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6775 part of the user address space
6776 @throws randolf::rex::xEINTR Interrupted by a signal
6777 @throws randolf::rex::xEINVAL Invalid argument passed
6778 @throws randolf::rex::xENOMEM Insufficient memory
6779 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6780 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6781 doesn't refer to a socket
6782 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6783 there's no new data
6784
6785 @returns Data structure
6786 @see send_struct
6787 @qualifier TLS
6788 *///=========================================================================
6789 template <typename T> T recv_struct(
6790 /// MSG_OOB@n
6791 /// MSG_PEEK@n
6792 /// MSG_WAITALL@n
6793 /// MSG_DONTWAIT@n
6794 /// MSG_CMSG_CLOEXEC
6795 const int posix_flags = 0) {
6796 T buf{0};
6797 if (__debug) debug("recv_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6798 + ", <buf>"
6799 + ", " + std::to_string(sizeof(buf))
6800 + ", " + std::to_string(posix_flags)
6801 + ");");
6802 __recv(&buf, sizeof(buf), posix_flags);
6803 return buf;
6804 } // -x- T recv_struct -x-
6805
6806 /*======================================================================*//**
6807 @brief
6808 Receive one 16-bit unsigned integer of data in LSB (little endian) order from
6809 the endpoint.
6810 @par Notes
6811 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
6812 reception, which implements temporary blocking while waiting for data.
6813
6814 @throws randolf::rex::xEBADF The underlying socket is not open
6815 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6816 connections
6817 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6818 part of the user address space
6819 @throws randolf::rex::xEINTR Interrupted by a signal
6820 @throws randolf::rex::xEINVAL Invalid argument passed
6821 @throws randolf::rex::xENOMEM Insufficient memory
6822 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6823 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6824 doesn't refer to a socket
6825 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6826 there's no new data
6827
6828 @returns uint16_t (converted to local endianness)
6829 @see recv_uint16_msb
6830 @see send_uint16_lsb
6831 @qualifier TLS
6832 *///=========================================================================
6833 uint16_t recv_uint16_lsb(
6834 /// MSG_OOB@n
6835 /// MSG_PEEK@n
6836 /// MSG_WAITALL@n
6837 /// MSG_DONTWAIT@n
6838 /// MSG_CMSG_CLOEXEC
6839 const int posix_flags = 0) {
6840 uint16_t buf(0);
6841 if (__debug) debug("recv_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6842 + ", <buf>"
6843 + ", " + std::to_string(sizeof(buf))
6844 + ", " + std::to_string(posix_flags)
6845 + ");");
6846 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6847 return !__endian_is_msb ? buf : ntohs(buf);
6848 } // -x- uint16_t recv_uint16_lsb -x-
6849
6850 /*======================================================================*//**
6851 @brief
6852 Receive one 16-bit unsigned integer of data in MSB (big endian) order from
6853 the endpoint.
6854 @par Notes
6855 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
6856 reception, which implements temporary blocking while waiting for data.
6857
6858 @throws randolf::rex::xEBADF The underlying socket is not open
6859 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6860 connections
6861 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6862 part of the user address space
6863 @throws randolf::rex::xEINTR Interrupted by a signal
6864 @throws randolf::rex::xEINVAL Invalid argument passed
6865 @throws randolf::rex::xENOMEM Insufficient memory
6866 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6867 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6868 doesn't refer to a socket
6869 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6870 there's no new data
6871
6872 @returns uint16_t (converted to local endianness)
6873 @see recv_uint16_lsb
6874 @see send_uint16_msb
6875 @qualifier TLS
6876 *///=========================================================================
6877 uint16_t recv_uint16_msb(
6878 /// MSG_OOB@n
6879 /// MSG_PEEK@n
6880 /// MSG_WAITALL@n
6881 /// MSG_DONTWAIT@n
6882 /// MSG_CMSG_CLOEXEC
6883 const int posix_flags = 0) {
6884 uint16_t buf(0);
6885 if (__debug) debug("recv_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6886 + ", <buf>"
6887 + ", " + std::to_string(sizeof(buf))
6888 + ", " + std::to_string(posix_flags)
6889 + ");");
6890 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6891 return __endian_is_msb ? buf : htons(buf);
6892 } // -x- uint16_t recv_uint16_msb -x-
6893
6894 /*======================================================================*//**
6895 @brief
6896 Receive one 32-bit unsigned integer of data in LSB (little endian) order from
6897 the endpoint.
6898 @par Notes
6899 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
6900 reception, which implements temporary blocking while waiting for data.
6901
6902 @throws randolf::rex::xEBADF The underlying socket is not open
6903 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6904 connections
6905 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6906 part of the user address space
6907 @throws randolf::rex::xEINTR Interrupted by a signal
6908 @throws randolf::rex::xEINVAL Invalid argument passed
6909 @throws randolf::rex::xENOMEM Insufficient memory
6910 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6911 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6912 doesn't refer to a socket
6913 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6914 there's no new data
6915
6916 @returns uint32_t (converted to local endianness)
6917 @see recv_uint32_msb
6918 @see send_uint32_lsb
6919 @qualifier TLS
6920 *///=========================================================================
6921 uint32_t recv_uint32_lsb(
6922 /// MSG_OOB@n
6923 /// MSG_PEEK@n
6924 /// MSG_WAITALL@n
6925 /// MSG_DONTWAIT@n
6926 /// MSG_CMSG_CLOEXEC
6927 const int posix_flags = 0) {
6928 uint32_t buf(0);
6929 if (__debug) debug("recv_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6930 + ", <buf>"
6931 + ", " + std::to_string(sizeof(buf))
6932 + ", " + std::to_string(posix_flags)
6933 + ");");
6934 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6935 return !__endian_is_msb ? buf : htonl(buf);
6936 } // -x- uint32_t recv_uint32_lsb -x-
6937
6938 /*======================================================================*//**
6939 @brief
6940 Receive one 32-bit unsigned integer of data in MSB (big endian) order from
6941 the endpoint.
6942 @par Notes
6943 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
6944 reception, which implements temporary blocking while waiting for data.
6945
6946 @throws randolf::rex::xEBADF The underlying socket is not open
6947 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6948 connections
6949 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6950 part of the user address space
6951 @throws randolf::rex::xEINTR Interrupted by a signal
6952 @throws randolf::rex::xEINVAL Invalid argument passed
6953 @throws randolf::rex::xENOMEM Insufficient memory
6954 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6955 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
6956 doesn't refer to a socket
6957 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
6958 there's no new data
6959
6960 @returns uint32_t (converted to local endianness)
6961 @see recv_uint32_lsb
6962 @see send_uint32_msb
6963 @qualifier TLS
6964 *///=========================================================================
6965 uint32_t recv_uint32_msb(
6966 /// MSG_OOB@n
6967 /// MSG_PEEK@n
6968 /// MSG_WAITALL@n
6969 /// MSG_DONTWAIT@n
6970 /// MSG_CMSG_CLOEXEC
6971 const int posix_flags = 0) {
6972 uint32_t buf(0);
6973 if (__debug) debug("recv_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
6974 + ", <buf>"
6975 + ", " + std::to_string(sizeof(buf))
6976 + ", " + std::to_string(posix_flags)
6977 + ");");
6978 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
6979 return __endian_is_msb ? buf : ntohl(buf);
6980 } // -x- uint32_t recv_uint32_msb -x-
6981
6982 /*======================================================================*//**
6983 @brief
6984 Receive one 64-bit unsigned integer of data in LSB (little endian) order from
6985 the endpoint.
6986 @par Notes
6987 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
6988 reception, which implements temporary blocking while waiting for data.
6989
6990 @throws randolf::rex::xEBADF The underlying socket is not open
6991 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
6992 connections
6993 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
6994 part of the user address space
6995 @throws randolf::rex::xEINTR Interrupted by a signal
6996 @throws randolf::rex::xEINVAL Invalid argument passed
6997 @throws randolf::rex::xENOMEM Insufficient memory
6998 @throws randolf::rex::xENOTCONN Underlying socket is not connected
6999 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7000 doesn't refer to a socket
7001 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7002 there's no new data
7003
7004 @returns uint64_t (converted to local endianness)
7005 @see recv_uint64_msb
7006 @see send_uint64_lsb
7007 @qualifier TLS
7008 *///=========================================================================
7009 uint64_t recv_uint64_lsb(
7010 /// MSG_OOB@n
7011 /// MSG_PEEK@n
7012 /// MSG_WAITALL@n
7013 /// MSG_DONTWAIT@n
7014 /// MSG_CMSG_CLOEXEC
7015 const int posix_flags = 0) {
7016 uint64_t buf(0);
7017 if (__debug) debug("recv_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7018 + ", <buf>"
7019 + ", " + std::to_string(sizeof(buf))
7020 + ", " + std::to_string(posix_flags)
7021 + ");");
7022 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
7023 return !__endian_is_msb ? buf : ((((uint64_t)ntohl((buf) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((buf) >> 32)));
7024 } // -x- uint64_t recv_uint64_lsb -x-
7025
7026 /*======================================================================*//**
7027 @brief
7028 Receive one 64-bit unsigned integer of data in MSB (big endian) order from
7029 the endpoint.
7030 @par Notes
7031 The @c MSG_WAITALL flag is used behind-the-scenes to prevent premature data
7032 reception, which implements temporary blocking while waiting for data.
7033
7034 @throws randolf::rex::xEBADF The underlying socket is not open
7035 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7036 connections
7037 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7038 part of the user address space
7039 @throws randolf::rex::xEINTR Interrupted by a signal
7040 @throws randolf::rex::xEINVAL Invalid argument passed
7041 @throws randolf::rex::xENOMEM Insufficient memory
7042 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7043 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7044 doesn't refer to a socket
7045 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7046 there's no new data
7047
7048 @returns uint64_t (converted to local endianness)
7049 @see recv_uint64_lsb
7050 @see send_uint64_msb
7051 @qualifier TLS
7052 *///=========================================================================
7053 uint64_t recv_uint64_msb(
7054 /// MSG_OOB@n
7055 /// MSG_PEEK@n
7056 /// MSG_WAITALL@n
7057 /// MSG_DONTWAIT@n
7058 /// MSG_CMSG_CLOEXEC
7059 const int posix_flags = 0) {
7060 uint64_t buf(0);
7061 if (__debug) debug("recv_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7062 + ", <buf>"
7063 + ", " + std::to_string(sizeof(buf))
7064 + ", " + std::to_string(posix_flags)
7065 + ");");
7066 __recv(&buf, sizeof(buf), posix_flags | MSG_WAITALL);
7067 return __endian_is_msb ? buf : ((((uint64_t)htonl((buf) & 0xffffffffUL)) << 32) | htonl((uint32_t)((buf) >> 32)));
7068 } // -x- uint64_t recv_uint64_msb -x-
7069
7070 /*======================================================================*//**
7071 @brief
7072 Receive data from a specific endpoint.
7073
7074 @par Notes
7075 The @c MSG_WAITALL flag is particularly useful for preventing premature data
7076 reception, but it's important to note that it does implement temporary
7077 blocking while waiting for data.
7078
7079 @warning
7080 This method is not compatible with TLS.
7081
7082 @throws randolf::rex::xEBADF The underlying socket is not open
7083 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7084 connections
7085 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7086 part of the user address space
7087 @throws randolf::rex::xEINTR Interrupted by a signal
7088 @throws randolf::rex::xEINVAL Invalid argument passed
7089 @throws randolf::rex::xENOMEM Insufficient memory
7090 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7091 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7092 doesn't refer to a socket
7093 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7094 there's no new data
7095
7096 @returns Appropriately-sized vector of characters
7097 @see sendto
7098 @qualifier POSIX
7099 *///=========================================================================
7100 std::vector<char>* recvfrom(
7101 /// Maximum number of bytes to receive
7102 const size_t nbytes,
7103 /// MSG_OOB@n
7104 /// MSG_PEEK@n
7105 /// MSG_WAITALL@n
7106 /// MSG_DONTWAIT@n
7107 /// MSG_CMSG_CLOEXEC
7108 const int posix_flags,
7109 /// Target endpoint address structure
7110 struct sockaddr *from,
7111 /// Size of target endpoint structure
7112 socklen_t fromlen = sizeof(sockaddr)) {
7113 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
7114 if (__debug) debug("recvfrom(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7115 + ", <buf>"
7116 + ", " + std::to_string(buf_size)
7117 + ", " + std::to_string(posix_flags)
7118 + ", <sockaddr>"
7119 + ", " + std::to_string(fromlen)
7120 + ");");
7121 std::vector<char>* v = new std::vector<char>(buf_size);
7122 v->resize(__track_bytes_rx(__rc_check(::recvfrom(__socket_fd, v->data(), v->size(), posix_flags, from, &fromlen))));
7123 return v;
7124 } // -x- std::vector<char>* recvfrom -x-
7125
7126 private:
7127 /*======================================================================*//**
7128 @brief
7129 This is an internal function that:
7130 1. receives data from the endpoint:
7131 - via underlying socket when TLS is not enabled
7132 - via the OpenSSL socket API when TLS is enabled
7133 2. checks for a socket I/O error (and throws an exception)
7134 3. tracks number of bytes received (if there were no errors)
7135
7136 @note
7137 The ring buffer will only be used if there is one. The @ref __recvline
7138 method is one method that instantiates the internal ring buffer, and there
7139 could be others in the future (although this is not strongly anticipated
7140 aside from a method that allows the software developer to explicitly choose
7141 to have a buffer active for all socket-read operations).
7142
7143 @throws randolf::rex::xEAGAIN The underlying socket timed out
7144 @throws randolf::rex::xEBADF The underlying socket is not open
7145 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7146 connections
7147 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7148 part of the user address space
7149 @throws randolf::rex::xEINTR Interrupted by a signal
7150 @throws randolf::rex::xEINVAL Invalid argument passed
7151 @throws randolf::rex::xENOMEM Insufficient memory
7152 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7153 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7154 doesn't refer to a socket
7155 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7156 there's no new data
7157
7158 @returns Number of bytes that were successfully received
7159 @see __send
7160 @qualifier TLS
7161 *///=========================================================================
7162 int __recv(
7163 /// Pointer to data
7164 void* data,
7165 /// Maximum number of bytes to receive into @c data array
7166 const int len,
7167 /// Flags (ignored with encrypted streams, except for the MSG_PEEK flag)
7168 const int posix_flags = 0) {
7169
7170 // --------------------------------------------------------------------------
7171 // When internal buffering is not set up (which is the default operation), we
7172 // simply pass data directly.
7173 // --------------------------------------------------------------------------
7174 if (__buffer == nullptr) {
7175 return __tls ? (posix_flags & MSG_PEEK ? __rc_check_tls(SSL_peek(__tls_fd, data, len))
7176 : __track_crypt_rx(__rc_check_tls(SSL_read(__tls_fd, data, len))))
7177 : __track_bytes_rx(__rc_check( ::recv(__socket_fd, data, len, posix_flags)));
7178 } // -x- if !__buffer -x-
7179
7180 // --------------------------------------------------------------------------
7181 // If the amount of data contained in the internal buffer is sufficient to
7182 // satisfy the amount of data that "len" represents, then all that's needed
7183 // is to return "len" bytes from the ring buffer without having to receive
7184 // more data from the socket.
7185 // --------------------------------------------------------------------------
7186 if (len <= __buffer->get_utilized()) {
7187 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
7188 else __buffer->remove_to_array((size_t)len, (char*)data); // Consume character(s) from ring buffer
7189 return len;
7190 } // -x- if len -x-
7191
7192 // --------------------------------------------------------------------------
7193 // Consume data from socket to make up for OpenSSL's inability to read beyond
7194 // one packet of data in its SSL_peek() and SSL_read() methods, and also some
7195 // inconsistencies with certain raw (unencrypted) socket implementations that
7196 // occasionally refuse to read beyond one packet of data (at least on a first
7197 // attempt to read with or without the MSG_PEEK flag).
7198 //
7199 // Fill internal buffer. It may be more than len is set to, but this is okay
7200 // because we're committed to using internal buffering now anyway (it's far
7201 // more likely that a protocol that requires the use of recvline() even once
7202 // is going to be using it repeatedly, but as long as the ring buffer is
7203 // large enough to cover the maximum length of a line of text, including its
7204 // EoL sequence, then this will always succeed).
7205 // --------------------------------------------------------------------------
7206 __buffer->defragment(true);
7207 __buffer_bam = __buffer->data_bam(__buffer_bam); // Update ring buffer setup (will be allocated automatically if "nullptr" default)
7208 size_t available = __buffer->get_available();
7209 int n = 0; // Number of bytes
7210 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
7211 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
7212 __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
7213
7214 // --------------------------------------------------------------------------
7215 // Copy or move data depending on whether POSIX flag MSG_PEEK was set.
7216 // --------------------------------------------------------------------------
7217 n = std::min(len, (int)__buffer->get_utilized()); // Work with the lesser number of bytes
7218 if (n > 0) { // At least 1 byte is available
7219 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
7220 else __buffer->remove_to_array((size_t)n, (char*)data); // Consume character(s) from ring buffer
7221 } // -x- if n -x-
7222 return n; // Return the number of bytes that were copied/moved
7223
7224 } // -x- int __recv -x-
7225
7226 /*======================================================================*//**
7227 @brief
7228 This is an internal function that:
7229 1. receives a line of data from the endpoint:
7230 - via underlying socket when TLS is not enabled
7231 - via the OpenSSL socket API when TLS is enabled
7232 2. checks for a socket I/O error (and throws an exception)
7233 3. tracks number of bytes received (if there were no errors)
7234 4. isolates the EoL sequence from the line of data, and records it for
7235 later reference (for those cases where it's needed)
7236
7237 @note
7238 The two exceptions that need to be handled are @ref randolf::rex::xEOVERFLOW
7239 (when the endpoint sends oversized lines) and @ref randolf::rex::xETIMEDOUT
7240 (when the endpoint takes too long to respond) for a robust usage.
7241
7242 @throws randolf::rex::xEBADF The underlying socket is not open
7243 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7244 connections
7245 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7246 part of the user address space
7247 @throws randolf::rex::xEINTR Interrupted by a signal
7248 @throws randolf::rex::xEINVAL Invalid argument passed
7249 @throws randolf::rex::xENOMEM Insufficient memory
7250 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7251 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7252 doesn't refer to a socket
7253 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
7254 no EoL character sequence is detected after recvline's buffer became
7255 full (whatever data is waiting may still be received using any of the
7256 recv_ methods {with or without the @c MSG_PEEK flag set}, including
7257 @c recvline() with a larger buffer {see the @c nbytes parameter})
7258 @throws randolf::rex::xERANGE if the timeout parameter is below 0
7259 @throws randolf::rex::xETIMEDOUT If a @c timeout occurs
7260 @see __sendline
7261 @qualifier TLS
7262 *///=========================================================================
7263 std::string& __recvline(
7264 /// Where to append the data
7265 std::string& buf,
7266 /// Maximum number of bytes to receive (including EoL character sequence) @n
7267 /// @c 0 (or below) = "buf" will be returned as is@n
7268 /// if this quantity is larger than @c buf.size() then an exception will be
7269 /// thrown before any socket/internal-buffer read attempts occur
7270 const int buf_size,
7271 /// MSG_OOB@n
7272 /// MSG_PEEK (with some limitations)@n
7273 /// MSG_WAITALL@n
7274 /// MSG_DONTWAIT (ineffective when @c timeout is not 0)@n
7275 /// MSG_CMSG_CLOEXEC
7276 const int posix_flags,
7277 /// Line timeout (in seconds)@n
7278 /// 0 = no timeout (default), unless it was configured by way of the
7279 /// @ref timeout_recvline(long) method
7280 const ulong timeout,
7281 /// Configuration parameters
7282 const int recvline_flags) {
7283
7284 // --------------------------------------------------------------------------
7285 // Prevent undefined behaviour (I don't like wasting CPU cycles, but line
7286 // reading does require more processing).
7287 // --------------------------------------------------------------------------
7288 if (buf_size <= 0) { // Buffer is empty or has an unrealistic size that can be silently ignored
7289 __eol_consumed_seq.clear(); // Indicate that there's no EoL sequence
7290 return buf; // Return "buf" as is
7291 } // -x- if buf_size -x-
7292
7293 // --------------------------------------------------------------------------
7294 // Internal variables.
7295 // --------------------------------------------------------------------------
7296 const int old_size = buf.size();
7297 if (old_size >= buf_size) throw new randolf::rex::xEOVERFLOW("Pre-defined line is too large"); //return buf;
7298 const int recv_size = buf_size - old_size; // This will not be below 1
7299
7300 // --------------------------------------------------------------------------
7301 // Pre-allocate data so we can write to it directly.
7302 // --------------------------------------------------------------------------
7303 buf.resize(buf_size + old_size);
7304
7305 // --------------------------------------------------------------------------
7306 // Perform a highly specialized check for whether the supplied buffer already
7307 // completes an EoL sequence. If it does, then stop now and return it as is
7308 // with one byte discarded from the stream (if needed; it almost always will
7309 // be needed).
7310 // --------------------------------------------------------------------------
7311 if (old_size > 0) {
7312
7313 // --------------------------------------------------------------------------
7314 // Use exceptions to handle timeouts and generate randolf::rex::xETIMEDOUT,
7315 // which is helpful because timeout errors vary on different Operating
7316 // Systems, systems configurations, and in different socket implementations.
7317 // --------------------------------------------------------------------------
7318 try {
7319
7320 // --------------------------------------------------------------------------
7321 // Peek ahead at the first character, just in case it's the second part of an
7322 // EoL sequence (this ensures that automatic determination will also work).
7323 // --------------------------------------------------------------------------
7324 __recv(buf.data() + old_size, 1, posix_flags | MSG_PEEK | MSG_WAITALL); // Read-ahead one character (one byte), or else timeout
7325
7326 } catch (const randolf::rex::xEAGAIN& e) { // Socket timeout derived from EAGAIN error
7327 throw randolf::rex::xETIMEDOUT("randolf::rex::xEAGAIN");
7328 } catch (const randolf::rex::xEWOULDBLOCK& e) { // Socket timeout derived from EWOULDBLOCK error
7329 throw randolf::rex::xETIMEDOUT("randolf::rex::xEWOULDBLOCK");
7330 } catch (const randolf::rex::xTLS_SYSCALL& e) { // Socket timeout derived from TLS_SYSCALL error
7331 throw randolf::rex::xETIMEDOUT("randolf::rex::xTLS_SYSCALL");
7332 //} catch (const std::exception& e) { // Debug
7333 // std::cout << "Unknown error " << e.what() << std::endl;
7334 // throw;
7335 }
7336
7337 // --------------------------------------------------------------------------
7338 // If EoL is found, then update internal variables and return the buffer
7339 // after truncating it to exclude the EoL sequence characters (and tracking
7340 // of the EoL sequence as is the expected behaviour).
7341 // --------------------------------------------------------------------------
7342 int len = -1;
7343 int len_with_eol = 0;
7344 if ((len = eol_index(buf, &len_with_eol)) >= 0) { // EoL found, so we're done now (-1 == no EoL found)
7345 int old_size = buf.size(); // Update buffer size so that final discard can be last before return in this code block
7346 __eol_consumed_seq = buf.substr(len, len_with_eol - len); // Save EoL sequence
7347 buf.resize(len); // Truncate string without the EoL sequence
7348 if (old_size == len_with_eol) discard(1); // Discard the character we peeked ahead at if it completed the EoL sequence detection
7349 return buf;
7350 } // -x- if eol_index -x-
7351
7352 // --------------------------------------------------------------------------
7353 // Remove extra character we appended to "buf" earlier because it's not as
7354 // simple if we have to adjust and track the number of bytes to read next.
7355 // --------------------------------------------------------------------------
7356 //buf.resize(buf.size() - 1); // We don't do this in this case because of pre-resizing
7357
7358 } // -x if old_size -x-
7359
7360 // --------------------------------------------------------------------------
7361 // If an internal buffer, which is used by __recv, hasn't been instantiated,
7362 // then instantiate it and save the pointer to it for future use by __recv().
7363 //
7364 // Buffering becomes necessary with TLS connection because SSL_peek fails to
7365 // return data that spans multiple packets. What triggers this behaviour is
7366 // the command "stty -icanon && openssl s_client host_name tcp_port" followed
7367 // by manual interactive typing some text then pressing the "[Enter]" key.
7368 //
7369 // The ::malloc(__buffer_size) won't be leaked because it will be freed in
7370 // in this rsocket's destructor (if it isn't freed elsewhere before this).
7371 // --------------------------------------------------------------------------
7372 if (__buffer == nullptr) __buffer = new rring(__buffer_size);
7373
7374 // --------------------------------------------------------------------------
7375 // Calculate timeout_target for easy comparisons with time(0) in the loop,
7376 // and other internal variables.
7377 // --------------------------------------------------------------------------
7378 ulong timeout_target = timeout == 0
7379 ? (__recvline_timeout == 0 ? ULONG_MAX : __recvline_timeout + time(0))
7380 : timeout + time(0);
7381 int br = 0; // Quantity of Bytes Received
7382 int len = -1; // Length of string before EoL sequence (-1 = no EoL sequence)
7383 int len_with_eol = 0; // Length of string including the EoL sequence (which may be 0 or 1 or 2)
7384
7385 // --------------------------------------------------------------------------
7386 // Take advantage of RAII in C++ to ensure that the non-recvline timeout
7387 // settings will be restored regardless of a normal return or throwing any
7388 // exception.
7389 // --------------------------------------------------------------------------
7390 RAII_timeout raii(*this, timeout);
7391
7392 // --------------------------------------------------------------------------
7393 // Line-reading loop. Unlike a regular recv() call, the recvline() method
7394 // attempts to read an entire line of text, and it's possible that this line
7395 // of text is sent across multiple packets, including half-way between a CRLF
7396 // EoL sequence, so we either stop upon encountering the desired EoL sequence
7397 // (be it one or two characters long), or we read all the way to the end of
7398 // the maximum line length and then throw rex::xEOVERFLOW exception if no
7399 // EoL sequence was encountered, or rex::xETIMEDOUT exception if the allotted
7400 // recvline_timeout() time expired (and this particular timeout can actually
7401 // be longer than the non-line-receiving per-character timeout).
7402 // --------------------------------------------------------------------------
7403 do {
7404
7405 // --------------------------------------------------------------------------
7406 // Use exceptions to handle timeouts and generate randolf::rex::xETIMEDOUT,
7407 // which is helpful because timeout errors vary on different Operating
7408 // Systems, systems configurations, and in different socket implementations.
7409 // --------------------------------------------------------------------------
7410 try {
7411
7412 // --------------------------------------------------------------------------
7413 // Attempt to read data, but without consuming it. The reason for this is
7414 // that we need to scan the data for an EoL sequence, but if there isn't one
7415 // then we need to try again while waiting for more data. (The current data
7416 // will be buffered by __recv so we will actually be waiting for more data to
7417 // to arrive from the endpoint.)
7418 // --------------------------------------------------------------------------
7419//std::cout << "br0=" << br << " old_size=" << old_size << " buf_size=" << buf_size << std::endl; // Debug
7420 br = __recv(buf.data() + old_size, recv_size, posix_flags | MSG_PEEK); // Look-ahead at socket stream (with buffer size that was inflated earlier to include EoL sequence)
7421//std::cout << "br1=" << br << " old_size=" << old_size << " buf_size=" << buf_size << std::endl; // Debug
7422
7423 } catch (const randolf::rex::xEAGAIN& e) { // Socket timeout derived from EAGAIN error
7424 throw randolf::rex::xETIMEDOUT("randolf::rex::xEAGAIN");
7425 } catch (const randolf::rex::xEWOULDBLOCK& e) { // Socket timeout derived from EWOULDBLOCK error
7426 throw randolf::rex::xETIMEDOUT("randolf::rex::xEWOULDBLOCK");
7427 } catch (const randolf::rex::xTLS_SYSCALL& e) { // Socket timeout derived from TLS_SYSCALL error
7428 throw randolf::rex::xETIMEDOUT("randolf::rex::xTLS_SYSCALL");
7429 //} catch (const std::exception& e) { // Debug
7430 // std::cout << "Unknown error " << e.what() << std::endl;
7431 // throw;
7432 }
7433
7434 // --------------------------------------------------------------------------
7435 // If EoL sequence found, save the consumed EoL sequence and return the line.
7436 // --------------------------------------------------------------------------
7437 if ((len = eol_index(buf, &len_with_eol)) >= 0) { // EoL found, so we're done now (-1 == no EoL found)
7438//std::cout << "Found // EoL: br=" << br << " len=" << len << " len_with_eol=" << len_with_eol << " eol.size=" << __eol.size() << " buf_size=" << buf_size << " old_size=" << old_size << " buf=[" << buf.data() << "]" << std::endl;
7439// __eol_consumed_seq = buf.substr(len, len_with_eol - len); // Save EoL sequence (copy it from the string) // TODO: Optimize this
7440 __eol_consumed_seq.assign(buf, len, len_with_eol - len); // Save EoL sequence (copy it from the string); "len" serves as the position here
7441 buf.resize(len); // Truncate string without the EoL sequence
7442 if (!(posix_flags & MSG_PEEK)) discard(len_with_eol - old_size); // Discard all characters that were added to the buffer
7443 return buf;
7444 } // -x- if len -x-
7445//std::cout << "Not found // EoL: br=" << br << " len=" << len << " len_with_eol=" << len_with_eol << " eol.size=" << __eol.size() << " buf_size=" << buf_size << " old_size=" << old_size << " buf=[" << buf.data() << "]" << std::endl;
7446
7447 // --------------------------------------------------------------------------
7448 // If EoS wasn't found and the maximum line length has been reached, then we
7449 // need to end this loop so that we can handle any recvline_flags and/or
7450 // throw an xEOVERFLOW exception.
7451 // --------------------------------------------------------------------------
7452 } while (br != recv_size); // Loop again if buffer isn't full yet
7453
7454 // --------------------------------------------------------------------------
7455 // If a timeout occurred, then throw the randolf::rex::xETIMEDOUT exception.
7456 // --------------------------------------------------------------------------
7457 if (timeout != 0)
7458 if (time(0) > timeout_target)
7459 throw randolf::rex::xETIMEDOUT("recvline timed out"); // We reached the line timeout target
7460
7461 // ==========================================================================
7462 // We've exited the loop, and now we're deciding how to handle a full buffer
7463 // that doesn't have an EoL, and obviously indicates line-overflow situation.
7464 // ==========================================================================
7465
7466 // --------------------------------------------------------------------------
7467 // Caller wants partial data instead of throwing an exception, even if it has
7468 // no EoL sequence.
7469 // --------------------------------------------------------------------------
7470 if (recvline_flags & RECVLINE_PARTIAL) {
7471 __eol_consumed_seq.clear(); // Indicate that there's no EoL sequence
7472 if (!(posix_flags & MSG_PEEK)) discard(br);
7473 buf.resize(br + old_size); // Truncate string to "br" (bytes received) in addition to the original buffer's bytes
7474//std::cout << "No EoL // br=" << br << " buf=[" << buf.data() << "]" << std::endl; // Debug
7475 return buf;
7476 } // -x- if RECVLINE_PARTIAL -x-
7477
7478 // --------------------------------------------------------------------------
7479 // Generate xEOVERFLOW exception.
7480 //
7481 // Discarded data is limited to the maximum length of the line that could be
7482 // received if the RECVLINE_LIMIT_DISCARD flag is set, otherwise the amount
7483 // of data that can be discarded will run beyond the maximum length.
7484 //
7485 // TODO: Throw a different exception for no EoL when the buffer isn't full.
7486 // --------------------------------------------------------------------------
7487 if (!(recvline_flags & RECVLINE_NO_DISCARD_ON_OVERFLOW) && !(posix_flags & MSG_PEEK)) {
7488 if (recvline_flags & RECVLINE_LIMIT_DISCARD) {
7489 discard(br); // Consume only data that was received (which should always just be the maximum line length)
7490 } else {
7491 discard_line(); // Consume remaining data beyond maximum line length (this is the default behaviour)
7492 }
7493 } // -x- if !RECVLINE_NO_DISCARD_ON_OVERFLOW -x-
7494
7495 throw randolf::rex::xEOVERFLOW("recvline overflow (line too long or missing EoL sequence)");
7496 } // -x- std::string& __recvline -x-
7497
7498 public:
7499 /*======================================================================*//**
7500 @brief
7501 Receive a line of data from the endpoint, with the EoL character(s) removed.
7502 While this is meant for ASCII and UTF-8 text, it will also work with binary
7503 data that doesn't include EoL character sequences as non-line-ending data
7504 (when receiving binary data, the @ref recv() and @ref recvz() methods tend to
7505 be better-suited).
7506
7507 @note
7508 Be sure to read the docuemntation for the @c eol and @c eol_adoption methods,
7509 especially if you're using @c recvline or @c recv_rline to implement an
7510 internet daemon, because pre-configuring the EoL sequence is an important
7511 consideration for some protocols.
7512
7513 In the following example, you can use the command `telnet 127.0.0.1 1024` to
7514 connect to the complied code once it's running, and then issue the commands
7515 "exit" or "quit" to end the session, or "down" to terminate the daemon:
7516
7517 @code{.cpp}
7518 #include <iostream> // std::cout, std::cerr, and friends
7519 #include <string> // std::string
7520 #include <randolf/rex>
7521 #include <randolf/rsocket>
7522
7523 int main() {
7524 try {
7525 randolf::rsocket r(AF_INET, SOCK_STREAM, IPPROTO_TCP);
7526 r.bind("127.0.0.1", 1024);
7527 r.listen();
7528
7529 bool running = true;
7530 while (running) {
7531 std::cout << "Waiting for connections on 127.0.0.1:1024" << std::endl;
7532 std::unique_ptr<randolf::rsocket> c = r.accept_up();
7533
7534 c->sendline("+-----------------+", MSG_MORE)
7535 .sendline("| === Welcome === |", MSG_MORE)
7536 .sendline("+-----------------+");
7537
7538 while (running) {
7539 c->send("==> ");
7540 std::string line = c->recvline(); // <-- You are here
7541
7542 if (line == "exit" || line == "quit") break;
7543 else if (line == "down") running = false;
7544 else if (line.empty()) continue;
7545 else c->send("You wrote \"")
7546 .send(line)
7547 .sendline("\"");
7548
7549 } // -x- while true -x-
7550
7551 } // -x- while running -x-
7552 r.close();
7553
7554 } catch (const randolf::rex::xEADDRINUSE& e) {
7555 std::cerr << "Could not bind IP address: " << e.what() << std::endl;
7556 return EXIT_FAILURE;
7557 } catch (const randolf::rex::xALL& e) {
7558 std::cerr << "Socket exception: " << e.what() << std::endl;
7559 return EXIT_FAILURE;
7560 } catch (const std::exception& e) {
7561 std::cerr << "Other exception: " << e.what() << std::endl;
7562 return EXIT_FAILURE;
7563 }
7564
7565 return EXIT_SUCCESS;
7566 } // -x- int main -x-
7567 @endcode
7568
7569 If @c nbytes is 0, then the internal buffer_size will be used as the default,
7570 which can also be changed from its compiled-in default of 1024 by using one
7571 of the buffer_size() methods. The total number of bytes received, including
7572 the EoL sequence, will not exceed the total number of byte specified with the
7573 @c nbytes parameter.
7574 @warning
7575 For @c nbytes the EoL character sequence size should be included, especially
7576 for shorter lines -- if the EoL character sequence is detected automatically
7577 on-the-fly (which is the default), then provisioning 2 characters is needed
7578 since an EoL sequence could be 1 or 2 characters long (line lengths shorter
7579 than 3 characters could be particularly problematic when detecting on-the-fly
7580 EoL character sequences). In such a case, a better approach is to allow for
7581 2 additional characters and then test the length of the returned string to
7582 ensure it doesn't exceed whatever the required maximum is (and throw the same
7583 @ref randolf::rex::xEOVERFLOW exception if the length is exceeded).
7584
7585 For more information about which characters an EoL sequence is comprised of,
7586 and how our specialized support for multiple EoL sequences works, see the
7587 documentation for the various @ref eol methods.
7588
7589 @pre
7590 When setting the recvline timeout (either with the @c timeout parameter with
7591 this method, or by using the @ref timeout_recvline(long) method), you may
7592 also need to set the socket timeout beforehand using the @ref timeout()
7593 method, because @c recvline doesn't do this...
7594 @n
7595 <blockquote>
7596 @ref timeout_recvline sets the overall total timeout for an entire line to
7597 be entered
7598 @n
7599 @ref timeout sets the timeout between individual characters received (such
7600 as keystrokes from a live end-user)
7601 </blockquote>
7602 @n
7603 If your socket timeout is longer than then recvline timeout, this will have
7604 the effect of rendering the recvling timeout as ineffective.
7605
7606 The @c timeout parameter can be used to override the total overall timeout
7607 for receiving a line, which is useful for specific situations where a
7608 specific timeout is desired for particular prompts, during certain data entry
7609 stages, etc.
7610
7611 @post
7612 When a line is not found (@c randolf::rex::xEAGAIN), or a line is too long
7613 because there's too much data (longer than a line) without an EoL sequence
7614 (@c randolf::rex::xOVERFLOW), then that data will not be discarded or
7615 consumed, and will remain ready for receiving (@ref recv) or for discarding
7616 @ref discard).
7617
7618 @par Notes
7619 If you're searching for a readline() method for socket I/O, then this is most
7620 likely what you're wanting/needing.
7621
7622 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
7623 @throws randolf::rex::xEBADF The underlying socket is not open
7624 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7625 connections
7626 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7627 part of the user address space
7628 @throws randolf::rex::xEINTR Interrupted by a signal
7629 @throws randolf::rex::xEINVAL Invalid argument passed
7630 @throws randolf::rex::xENOMEM Insufficient memory
7631 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7632 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7633 doesn't refer to a socket
7634 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
7635 no EoL character sequence is detected after recvline's buffer became
7636 full (whatever data is waiting may still be received using any of the
7637 recv_ methods {with or without the @c MSG_PEEK flag set}, including
7638 @c recvline() with a larger buffer {see the @c nbytes parameter})
7639 @throws randolf::rex::xERANGE if the timeout parameter is below 0
7640 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7641 there's no new data
7642
7643 @returns One line of text as a std::string object.
7644 @see discard
7645 @see eol()
7646 @see eol_adoption()
7647 @see eol_consumed_seq()
7648 @see recv_rline
7649 @see sendline(const std::string, const int)
7650 @see timeout
7651 @see timeout_recvline
7652 @see timeout_recvline(long)
7653 @qualifier TLS
7654 *///=========================================================================
7655 std::string recvline(
7656 /// Maximum number of bytes to receive (including EoL character sequence)@n
7657 /// 0 = use internal @ref buffer_size()
7658 const size_t nbytes = 0,
7659 /// MSG_OOB@n
7660 /// MSG_PEEK@n
7661 /// MSG_WAITALL@n
7662 /// MSG_DONTWAIT@n
7663 /// MSG_CMSG_CLOEXEC
7664 const int posix_flags = 0,
7665 /// Line timeout (in seconds)@n
7666 /// 0 = no timeout (default) or whatever @ref timeout was set to, unless it
7667 /// was configured by way of the @ref timeout_recvline(long) method
7668 long timeout = 0,
7669 /// Configuration parameters
7670 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
7671 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
7672 // + (__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
7673 // Internally, the length of the @ref eol() sequence is added to the buffer size
7674 // so that the maximum line length without EoL characters matches the maximum
7675 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
7676 if (__debug) debug("recvline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7677 + ", " + std::to_string(buf_size)
7678 + ", " + std::to_string(posix_flags)
7679 + ", " + std::to_string(timeout)
7680 + ", " + std::to_string(recvline_flags)
7681 + ");");
7682 std::string str;
7683 __recvline(str, buf_size, posix_flags, timeout, recvline_flags);
7684 return str;
7685 } // -x- std::string recvline -x-
7686
7687 /*======================================================================*//**
7688 @brief
7689 This method is the same as the @ref recvline() method, except that it also
7690 appends data to an existing std::string line. If the line provided is longer
7691 than @c nbytes bytes, then the line will be returned as is without any
7692 attempts made to receive more data nor to search for an EoL sequence
7693
7694 @throws randolf::rex::xEAGAIN If @c timeout occurs before EoL
7695 @throws randolf::rex::xEBADF The underlying socket is not open
7696 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7697 connections
7698 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7699 part of the user address space
7700 @throws randolf::rex::xEINTR Interrupted by a signal
7701 @throws randolf::rex::xEINVAL Invalid argument passed
7702 @throws randolf::rex::xENOMEM Insufficient memory
7703 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7704 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7705 doesn't refer to a socket
7706 @throws randolf::rex::xEOVERFLOW if the line is too long; in other words, if
7707 no EoL character sequence is detected after recvline's buffer became
7708 full (whatever data is waiting may still be received using any of the
7709 recv_ methods {with or without the @c MSG_PEEK flag set}, including
7710 @c recvline() with a larger buffer {see the @c nbytes parameter})
7711 @throws randolf::rex::xERANGE if the timeout parameter is below 0
7712 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7713 there's no new data
7714
7715 @returns One line of text as a std::string object.
7716 @see discard
7717 @see eol()
7718 @see eol_adoption()
7719 @see eol_consumed_seq()
7720 @see recv_rline
7721 @see sendline(const std::string, const int)
7722 @see timeout
7723 @see timeout_recvline
7724 @see timeout_recvline(long)
7725 @qualifier TLS
7726 *///=========================================================================
7727 std::string& recvline_append_to(
7728 /// Where to append the data
7729 std::string& line,
7730 /// Maximum number of bytes to receive (including EoL character sequence)@n
7731 /// 0 = use internal @ref buffer_size()
7732 const size_t nbytes = 0,
7733 /// MSG_OOB@n
7734 /// MSG_PEEK@n
7735 /// MSG_WAITALL@n
7736 /// MSG_DONTWAIT@n
7737 /// MSG_CMSG_CLOEXEC
7738 const int posix_flags = 0,
7739 /// Line timeout (in seconds)@n
7740 /// 0 = no timeout (default) or whatever @ref timeout was set to, unless it
7741 /// was configured by way of the @ref timeout_recvline(long) method
7742 long timeout = 0,
7743 /// Configuration parameters
7744 const int recvline_flags = RECVLINE_FLAGS::RECVLINE_DEFAULT) {
7745 size_t buf_size = (nbytes != 0 ? nbytes : __buffer_size); // If 0, then set this to the internal __buffer_size default
7746 // + (__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
7747 // Internally, the length of the @ref eol() sequence is added to the buffer size
7748 // so that the maximum line length without EoL characters matches the maximum
7749 // that was specified with @ref buffer_size() or with the @c nbytes parameter.
7750 if (__debug) debug("recvline_append_to(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7751 + ", <line>"
7752 + ", " + std::to_string(buf_size)
7753 + ", " + std::to_string(posix_flags)
7754 + ", " + std::to_string(timeout)
7755 + ", " + std::to_string(recvline_flags)
7756 + ");");
7757 if (line.size() < nbytes)
7758 __recvline(line, buf_size, posix_flags, timeout, recvline_flags);
7759 return line;
7760 } // -x- std::string& recvline_append_to -x-
7761
7762 /*======================================================================*//**
7763 @brief
7764 Receive data in the form of a "msghdr" structure.
7765 @warning
7766 This method is not compatible with TLS.
7767
7768 @throws randolf::rex::xEBADF The underlying socket is not open
7769 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7770 connections
7771 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7772 part of the user address space
7773 @throws randolf::rex::xEINTR Interrupted by a signal
7774 @throws randolf::rex::xEINVAL Invalid argument passed
7775 @throws randolf::rex::xENOMEM Insufficient memory
7776 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7777 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7778 doesn't refer to a socket
7779 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7780 there's no new data
7781
7782 @returns pointer to "msghdr" structure
7783 @see sendmsg
7784 @qualifier POSIX
7785 *///=========================================================================
7786 msghdr* recvmsg(
7787 /// Pointer to "msghdr" structure (or nullptr for automatic allocation)
7788 msghdr* msg,
7789 /// MSG_OOB@n
7790 /// MSG_PEEK@n
7791 /// MSG_WAITALL@n
7792 /// MSG_DONTWAIT@n
7793 /// MSG_CMSG_CLOEXEC
7794 const int posix_flags = 0) {
7795 if (__debug) debug("recvmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7796 + ", <msghdr>"
7797 + ", " + std::to_string(posix_flags)
7798 + ");");
7799 if (msg == nullptr) msg = new msghdr{0};
7800 __track_bytes_rx(__rc_check(::recvmsg(__socket_fd, msg, posix_flags)));
7801 return msg;
7802 } // -x- msghdr* recvmsg -x-
7803
7804 /*======================================================================*//**
7805 @brief
7806 Receive data in the form of an "mmsghdr" structure.
7807 @warning
7808 This method is not compatible with TLS.
7809
7810 @throws randolf::rex::xEBADF The underlying socket is not open
7811 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7812 connections
7813 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7814 part of the user address space
7815 @throws randolf::rex::xEINTR Interrupted by a signal
7816 @throws randolf::rex::xEINVAL Invalid argument passed
7817 @throws randolf::rex::xENOMEM Insufficient memory
7818 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7819 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7820 doesn't refer to a socket
7821 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7822 there's no new data
7823
7824 @returns pointer to "mmsghdr" structure
7825 @see sendmmsg
7826 @qualifier POSIX
7827 *///=========================================================================
7828 mmsghdr* recvmmsg(
7829 /// Pointer to "mmsghdr" structure (or nullptr for automatic allocation)
7830 struct mmsghdr* mmsg,
7831 /// Size of target endpoint structure
7832 const unsigned int vlen = sizeof(mmsghdr),
7833 /// MSG_DONTROUTE@n
7834 /// MSG_DONTWAIT@n
7835 /// MSG_EOR@n
7836 /// MSG_NOSIGNAL@n
7837 /// MSG_OOB
7838 const int posix_flags = 0,
7839 /// Timeout
7840 struct timespec* timeout = {0}) {
7841 if (__debug) debug("recvmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7842 + ", <mmsghdr>"
7843 + ", " + std::to_string(vlen)
7844 + ", " + std::to_string(posix_flags)
7845 + ", timeout{tv_sec=" + std::to_string(timeout->tv_sec)
7846 + ", tv_nsec=" + std::to_string(timeout->tv_nsec) + "}"
7847 + ");");
7848 __track_bytes_rx(__rc_check(::recvmmsg(__socket_fd, mmsg, vlen, posix_flags, timeout)));
7849 return mmsg;
7850 } // -x- mmsghdr* recvmmsg -x-
7851
7852 /*======================================================================*//**
7853 @brief
7854 Receive data from the endpoint, and add a 0 (null) onto the end. This is
7855 useful when using the resulting std::vector<char> as an ASCIIZ string.
7856
7857 If nbytes is 0, then the internal buffer_size will be used as the default,
7858 which can also be changed from its compiled-in default of 1024 by using one
7859 of the buffer_size() methods.
7860
7861 @post
7862 The resulting std::vector size is always inflated by 1. This means that
7863 relying on a comparison against 0 will result in an infinite loop:
7864 @code{.cpp}
7865 for (std::vector<char> v = r.recvz(); v.size > 0; v = recvz()) { ... }
7866 @endcode
7867 So, you'll need to compare against 1 instead of 0 to compensate fot the
7868 inflated size due to the addition of null character 0:
7869 @code{.cpp}
7870 for (std::vector<char> v = r.recvz(); v.size > 1; v = recvz()) { ... }
7871 @endcode
7872
7873 @warning
7874 The resulting size of std::vector<char> will be <tt>nbytes + 1</tt> which
7875 ensures that any string processing functions or presentation libraries - such
7876 as @c printf() - expecting an ASCIIZ string buffer will stop at null (0), and
7877 will reliably output no more than the maximum size specified.
7878 @n@n
7879 The way to think of this is that @c nbytes specifies the maximum number of
7880 bytes (a.k.a., characters) to recieve over the underlying socket, and the
7881 final 0 (null) being added is not included in the maximum specified by the
7882 @c nbytes parameter.
7883
7884 @par Notes
7885 The @c MSG_WAITALL flag is particularly useful for preventing premature data
7886 reception, but it's important to note that it does implement temporary
7887 blocking while waiting for data.
7888
7889 @throws randolf::rex::xEBADF The underlying socket is not open
7890 @throws randolf::rex::xECONNREFUSED Remote address is not listening for new
7891 connections
7892 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7893 part of the user address space
7894 @throws randolf::rex::xEINTR Interrupted by a signal
7895 @throws randolf::rex::xEINVAL Invalid argument passed
7896 @throws randolf::rex::xENOMEM Insufficient memory
7897 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7898 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7899 doesn't refer to a socket
7900 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7901 there's no new data
7902
7903 @returns appropriately-sized vector of characters + 1 (includes additional
7904 null terminator character 0)
7905 @see recv(const size_t, const int)
7906 @see recv(std::vector<char>, const int)
7907 @see sendz
7908 @qualifier TLS
7909 *///=========================================================================
7910 std::vector<char>* recvz(
7911 /// Maximum number of bytes to receive
7912 const size_t nbytes = 0,
7913 /// MSG_OOB@n
7914 /// MSG_PEEK@n
7915 /// MSG_WAITALL@n
7916 /// MSG_DONTWAIT@n
7917 /// MSG_CMSG_CLOEXEC
7918 const int posix_flags = 0) {
7919 size_t buf_size = nbytes != 0 ? nbytes : __buffer_size;
7920 if (__debug) debug("recvz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
7921 + ", <buf>"
7922 + ", " + std::to_string(buf_size)
7923 + ", " + std::to_string(posix_flags)
7924 + ");");
7925 std::vector<char>* v = new std::vector<char>(buf_size);
7926 v->resize(__recv(v->data(), v->size(), posix_flags) + 1); // Add 1 to include room for final null character 0
7927 v->at(v->size() - 1) = (char)0; // Set final element to 0 (null)
7928 return v;
7929 } // -x- std::vector<char>* recvz -x-
7930
7931 private:
7932 /*======================================================================*//**
7933 @brief
7934 This is an internal function that:
7935 1. sends data to the endpoint:
7936 - via underlying socket when TLS is not enabled
7937 - via the OpenSSL socket API when TLS is enabled
7938 2. checks for a socket I/O error (and throws an exception)
7939 3. tracks number of bytes transmitted (if there were no errors)
7940
7941 @par Threads
7942 This method is threadsafe because the underlying POSIX send() and OpenSSL's
7943 SSL_write() functions are threadsafe.
7944
7945 @throws randolf::rex::xEBADF The underlying socket is not open
7946 @throws randolf::rex::xECONNRESET Connect reset by peer
7947 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
7948 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
7949 part of the user address space
7950 @throws randolf::rex::xEINTR Interrupted by a signal
7951 @throws randolf::rex::xEINVAL Invalid argument passed
7952 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
7953 occur, but the POSIX sockets documentation lists it as one of the
7954 errors that can be returned, perhaps because some incorrectly
7955 implemented TCP/IP stacks return this error?)
7956 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
7957 sent atomically (the maximum size is typically 65,507 bytes for IPv4
7958 and 65,527 bytes for IPv6)
7959 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
7960 network congestion (or, less commonly, insufficient memory)
7961 @throws randolf::rex::xENOMEM Insufficient memory
7962 @throws randolf::rex::xENOTCONN Underlying socket is not connected
7963 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
7964 doesn't refer to a socket
7965 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
7966 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
7967 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
7968 isn't set)
7969 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
7970 there's no new data
7971 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
7972 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
7973 but it really isn't)
7974
7975 @returns Number of bytes that were successfully transmitted
7976 @see __recv
7977 @qualifier TLS
7978 *///=========================================================================
7979 int __send(
7980 /// Pointer to data
7981 const void* data,
7982 /// Length of message data
7983 const size_t len,
7984 /// Flags (ignored with encrypted streams)
7985 const int posix_flags = 0) {
7986 if (__tls) return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data, len)));
7987 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data, len, posix_flags)));
7988 } // -x- int __send -x-
7989
7990 /*======================================================================*//**
7991 @brief
7992 This is an internal function that:
7993 1. sends data to the endpoint:
7994 - via underlying socket when TLS is not enabled
7995 - via the OpenSSL socket API when TLS is enabled
7996 2. checks for a socket I/O error (and throws an exception)
7997 3. tracks number of bytes transmitted (if there were no errors)
7998
7999 @par Threads
8000 This method is threadsafe because the underlying POSIX send() and OpenSSL's
8001 SSL_write() functions are threadsafe.
8002
8003 @throws randolf::rex::xEBADF The underlying socket is not open
8004 @throws randolf::rex::xECONNRESET Connect reset by peer
8005 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8006 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8007 part of the user address space
8008 @throws randolf::rex::xEINTR Interrupted by a signal
8009 @throws randolf::rex::xEINVAL Invalid argument passed
8010 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8011 occur, but the POSIX sockets documentation lists it as one of the
8012 errors that can be returned, perhaps because some incorrectly
8013 implemented TCP/IP stacks return this error?)
8014 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8015 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8016 and 65,527 bytes for IPv6)
8017 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8018 network congestion (or, less commonly, insufficient memory)
8019 @throws randolf::rex::xENOMEM Insufficient memory
8020 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8021 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8022 doesn't refer to a socket
8023 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8024 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8025 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8026 isn't set)
8027 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8028 there's no new data
8029 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8030 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8031 but it really isn't)
8032
8033 @returns Number of bytes that were successfully transmitted
8034 @see __recvline
8035 @qualifier TLS
8036 *///=========================================================================
8037 int __sendline(
8038 /// Pointer to data
8039 const void* data,
8040 /// Number of bytes to send
8041 const size_t len,
8042 /// Flags (ignored with encrypted streams)
8043 const int posix_flags = 0) {
8044 if (__tls) { // Encrypted
8045 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, data , len )))
8046 + __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.data(), __eol_out.length())));
8047 } else { // Not encrypted
8048 return __track_bytes_tx(__rc_check( ::send(__socket_fd, data , len, posix_flags | MSG_MORE))) // MSG_MORE prevents extra packets from being sent
8049 + __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.data(), __eol_out.length(), posix_flags )));
8050 } // -x- if __tls -x-
8051 } // -x- int __sendline -x-
8052
8053 /*======================================================================*//**
8054 @brief
8055 This is an internal function that:
8056 1. sends data to the endpoint:
8057 - via underlying socket when TLS is not enabled
8058 - via the OpenSSL socket API when TLS is enabled
8059 2. checks for a socket I/O error (and throws an exception)
8060 3. tracks number of bytes transmitted (if there were no errors)
8061
8062 @par Threads
8063 This method is threadsafe because the underlying POSIX send() and OpenSSL's
8064 SSL_write() functions are threadsafe.
8065
8066 @throws randolf::rex::xEBADF The underlying socket is not open
8067 @throws randolf::rex::xECONNRESET Connect reset by peer
8068 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8069 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8070 part of the user address space
8071 @throws randolf::rex::xEINTR Interrupted by a signal
8072 @throws randolf::rex::xEINVAL Invalid argument passed
8073 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8074 occur, but the POSIX sockets documentation lists it as one of the
8075 errors that can be returned, perhaps because some incorrectly
8076 implemented TCP/IP stacks return this error?)
8077 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8078 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8079 and 65,527 bytes for IPv6)
8080 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8081 network congestion (or, less commonly, insufficient memory)
8082 @throws randolf::rex::xENOMEM Insufficient memory
8083 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8084 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8085 doesn't refer to a socket
8086 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8087 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8088 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8089 isn't set)
8090 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8091 there's no new data
8092 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8093 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8094 but it really isn't)
8095
8096 @returns Number of bytes that were successfully transmitted
8097 @qualifier TLS
8098 *///=========================================================================
8099 int __send_eol(
8100 /// Flags (ignored with encrypted streams)
8101 const int posix_flags = 0) {
8102 if (__tls) { // Encrypted
8103 return __track_crypt_tx(__rc_check_tls(SSL_write(__tls_fd, __eol_out.data(), __eol_out.length())));
8104 } else { // Not encrypted
8105 return __track_bytes_tx(__rc_check( ::send(__socket_fd, __eol_out.data(), __eol_out.length(), posix_flags)));
8106 } // -x- if __tls -x-
8107 } // -x- int __send_eol -x-
8108 // TODO: Create a recv_eol() method that some people will probably consider to be evil (ha ha!)
8109 // recv_eol() will need to be able to auto-detect the CRLF sequence on-the-fly (if necessary) just like recvline() does
8110
8111 public:
8112 /*======================================================================*//**
8113 @brief
8114 Send data in the form of a std::string to the endpoint.
8115 @par Threads
8116 This method is threadsafe.
8117
8118 @throws randolf::rex::xEBADF The underlying socket is not open
8119 @throws randolf::rex::xECONNRESET Connect reset by peer
8120 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8121 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8122 part of the user address space
8123 @throws randolf::rex::xEINTR Interrupted by a signal
8124 @throws randolf::rex::xEINVAL Invalid argument passed
8125 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8126 occur, but the POSIX sockets documentation lists it as one of the
8127 errors that can be returned, perhaps because some incorrectly
8128 implemented TCP/IP stacks return this error?)
8129 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8130 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8131 and 65,527 bytes for IPv6)
8132 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8133 network congestion (or, less commonly, insufficient memory)
8134 @throws randolf::rex::xENOMEM Insufficient memory
8135 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8136 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8137 doesn't refer to a socket
8138 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8139 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8140 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8141 isn't set)
8142 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8143 there's no new data
8144 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8145 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8146 but it really isn't)
8147
8148 @returns The same rsocket object so as to facilitate stacking
8149 @see recv
8150 @qualifier TLS
8151 *///=========================================================================
8152 rsocket& send(
8153 /// Data to send
8154 const std::string& msg,
8155 /// MSG_DONTROUTE@n
8156 /// MSG_DONTWAIT@n
8157 /// MSG_EOR@n
8158 /// MSG_MORE@n
8159 /// MSG_NOSIGNAL@n
8160 /// MSG_OOB
8161 const int posix_flags = 0) {
8162 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8163 + ", <std::string>"
8164 + ", " + std::to_string(msg.length())
8165 + ", " + std::to_string(posix_flags)
8166 + ");");
8167 __send(msg.data(), msg.length(), posix_flags);
8168 return *this;
8169 } // -x- rsocket& send -x-
8170
8171 /*======================================================================*//**
8172 @brief
8173 Send data in the form of a std::vector<char> to the endpoint.
8174 @par Threads
8175 This method is threadsafe.
8176
8177 @throws randolf::rex::xEBADF The underlying socket is not open
8178 @throws randolf::rex::xECONNRESET Connect reset by peer
8179 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8180 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8181 part of the user address space
8182 @throws randolf::rex::xEINTR Interrupted by a signal
8183 @throws randolf::rex::xEINVAL Invalid argument passed
8184 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8185 occur, but the POSIX sockets documentation lists it as one of the
8186 errors that can be returned, perhaps because some incorrectly
8187 implemented TCP/IP stacks return this error?)
8188 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8189 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8190 and 65,527 bytes for IPv6)
8191 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8192 network congestion (or, less commonly, insufficient memory)
8193 @throws randolf::rex::xENOMEM Insufficient memory
8194 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8195 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8196 doesn't refer to a socket
8197 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8198 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8199 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8200 isn't set)
8201 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8202 there's no new data
8203 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8204 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8205 but it really isn't)
8206
8207 @returns The same rsocket object so as to facilitate stacking
8208 @see recv(std::vector<char>, const int)
8209 @qualifier TLS
8210 *///=========================================================================
8211 rsocket& send(
8212 /// Data to send
8213 const std::vector<char>& msg,
8214 /// MSG_DONTROUTE@n
8215 /// MSG_DONTWAIT@n
8216 /// MSG_EOR@n
8217 /// MSG_MORE@n
8218 /// MSG_NOSIGNAL@n
8219 /// MSG_OOB
8220 const int posix_flags = 0) {
8221 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8222 + ", <std::vector>"
8223 + ", " + std::to_string(msg.size())
8224 + ", " + std::to_string(posix_flags)
8225 + ");");
8226 __send(msg.data(), msg.size(), posix_flags);
8227 return *this;
8228 } // -x- rsocket& send -x-
8229
8230 /*======================================================================*//**
8231 @brief
8232 Send data in the form of a C-string to the endpoint.
8233 @par Threads
8234 This method is threadsafe.
8235
8236 @throws randolf::rex::xEBADF The underlying socket is not open
8237 @throws randolf::rex::xECONNRESET Connect reset by peer
8238 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8239 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8240 part of the user address space
8241 @throws randolf::rex::xEINTR Interrupted by a signal
8242 @throws randolf::rex::xEINVAL Invalid argument passed
8243 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8244 occur, but the POSIX sockets documentation lists it as one of the
8245 errors that can be returned, perhaps because some incorrectly
8246 implemented TCP/IP stacks return this error?)
8247 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8248 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8249 and 65,527 bytes for IPv6)
8250 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8251 network congestion (or, less commonly, insufficient memory)
8252 @throws randolf::rex::xENOMEM Insufficient memory
8253 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8254 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8255 doesn't refer to a socket
8256 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8257 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8258 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8259 isn't set)
8260 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8261 there's no new data
8262 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8263 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8264 but it really isn't)
8265
8266 @returns The same rsocket object so as to facilitate stacking
8267 @see recv(const size_t, const int)
8268 @qualifier POSIX
8269 @qualifier TLS
8270 *///=========================================================================
8271 rsocket& send(
8272 /// Pointer to data to send
8273 const char* msg,
8274 /// Number of bytes to send, or 0 to auto-detect length if message is an ASCIIZ string
8275 size_t len = 0,
8276 /// MSG_DONTROUTE@n
8277 /// MSG_DONTWAIT@n
8278 /// MSG_EOR@n
8279 /// MSG_MORE@n
8280 /// MSG_NOSIGNAL@n
8281 /// MSG_OOB
8282 const int posix_flags = 0) {
8283 if (__debug) debug("send(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8284 + ", <msg>"
8285 + ", " + std::to_string(len)
8286 + ", " + std::to_string(posix_flags)
8287 + ");");
8288
8289 // --------------------------------------------------------------------------
8290 // Measure size of format string if an ASCIIZ string was indicated.
8291 // --------------------------------------------------------------------------
8292 if (len == 0) len = std::strlen(msg);
8293
8294 // --------------------------------------------------------------------------
8295 // Send string.
8296 // --------------------------------------------------------------------------
8297 __send(msg, len, posix_flags);
8298 return *this;
8299 } // -x- rsocket& send -x-
8300
8301 /*======================================================================*//**
8302 @brief
8303 Send data in the form of an ASCIIZ string to the endpoint, including the
8304 terminating NULL character.
8305 @par Threads
8306 This method is threadsafe.
8307
8308 @throws randolf::rex::xEBADF The underlying socket is not open
8309 @throws randolf::rex::xECONNRESET Connect reset by peer
8310 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8311 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8312 part of the user address space
8313 @throws randolf::rex::xEINTR Interrupted by a signal
8314 @throws randolf::rex::xEINVAL Invalid argument passed
8315 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8316 occur, but the POSIX sockets documentation lists it as one of the
8317 errors that can be returned, perhaps because some incorrectly
8318 implemented TCP/IP stacks return this error?)
8319 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8320 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8321 and 65,527 bytes for IPv6)
8322 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8323 network congestion (or, less commonly, insufficient memory)
8324 @throws randolf::rex::xENOMEM Insufficient memory
8325 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8326 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8327 doesn't refer to a socket
8328 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8329 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8330 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8331 isn't set)
8332 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8333 there's no new data
8334 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8335 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8336 but it really isn't)
8337
8338 @returns The same rsocket object so as to facilitate stacking
8339 @see recv_asciiz(const size_t, const int)
8340 @see sendz(const char*, const int) which doesn't transmit the terminating
8341 NULL character
8342 @qualifier TLS
8343 *///=========================================================================
8344 rsocket& send_asciiz(
8345 /// Pointer to data to send
8346 const char* msg,
8347 /// MSG_DONTROUTE@n
8348 /// MSG_DONTWAIT@n
8349 /// MSG_EOR@n
8350 /// MSG_MORE@n
8351 /// MSG_NOSIGNAL@n
8352 /// MSG_OOB
8353 const int posix_flags = 0) {
8354 if (__debug) debug("send_asciiz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8355 + ", <msg>"
8356 + ", " + std::to_string(std::strlen(msg) + 1)
8357 + ", " + std::to_string(posix_flags)
8358 + ");");
8359 __send(msg, std::strlen(msg) + 1, posix_flags);
8360 return *this;
8361 } // -x- rsocket& send_asciiz -x-
8362
8363 /*======================================================================*//**
8364 @brief
8365 Send one 8-bit byte (one unsigned character) of data to the endpoint.
8366 @par Threads
8367 This method is threadsafe.
8368
8369 @throws randolf::rex::xEBADF The underlying socket is not open
8370 @throws randolf::rex::xECONNRESET Connect reset by peer
8371 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8372 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8373 part of the user address space
8374 @throws randolf::rex::xEINTR Interrupted by a signal
8375 @throws randolf::rex::xEINVAL Invalid argument passed
8376 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8377 occur, but the POSIX sockets documentation lists it as one of the
8378 errors that can be returned, perhaps because some incorrectly
8379 implemented TCP/IP stacks return this error?)
8380 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8381 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8382 and 65,527 bytes for IPv6)
8383 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8384 network congestion (or, less commonly, insufficient memory)
8385 @throws randolf::rex::xENOMEM Insufficient memory
8386 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8387 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8388 doesn't refer to a socket
8389 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8390 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8391 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8392 isn't set)
8393 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8394 there's no new data
8395 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8396 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8397 but it really isn't)
8398
8399 @returns The same rsocket object so as to facilitate stacking
8400 @see recv_byte
8401 @see recv_char
8402 @see send_char
8403 @qualifier TLS
8404 *///=========================================================================
8405 rsocket& send_byte(
8406 /// Data to send
8407 const u_char value,
8408 /// MSG_DONTROUTE@n
8409 /// MSG_DONTWAIT@n
8410 /// MSG_EOR@n
8411 /// MSG_MORE@n
8412 /// MSG_NOSIGNAL@n
8413 /// MSG_OOB
8414 const int posix_flags = 0) {
8415 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8416 + ", <value>"
8417 + ", " + std::to_string(sizeof(value))
8418 + ", " + std::to_string(posix_flags)
8419 + ");");
8420 __send(&value, sizeof(value), posix_flags);
8421 return *this;
8422 } // -x- rsocket& send_byte -x-
8423
8424 /*======================================================================*//**
8425 @brief
8426 Send one signed character (one 8-bit byte) of data to the endpoint.
8427 @copydoc send_byte
8428 @returns The same rsocket object so as to facilitate stacking
8429 @see recv_byte
8430 @see recv_char
8431 @see send_byte
8432 @qualifier TLS
8433 *///=========================================================================
8434 rsocket& send_char(
8435 /// Data to send
8436 const char value,
8437 /// MSG_DONTROUTE@n
8438 /// MSG_DONTWAIT@n
8439 /// MSG_EOR@n
8440 /// MSG_MORE@n
8441 /// MSG_NOSIGNAL@n
8442 /// MSG_OOB
8443 const int posix_flags = 0) {
8444 if (__debug) debug("send_byte(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8445 + ", <value>"
8446 + ", " + std::to_string(sizeof(value))
8447 + ", " + std::to_string(posix_flags)
8448 + ");");
8449 __send(&value, sizeof(value), posix_flags);
8450 return *this;
8451 } // -x- rsocket& send_char -x-
8452
8453 /*======================================================================*//**
8454 @brief
8455 Send the EoL sequence to the endpoint.
8456 @par Threads
8457 This method is threadsafe.
8458
8459 @throws randolf::rex::xEBADF The underlying socket is not open
8460 @throws randolf::rex::xECONNRESET Connect reset by peer
8461 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8462 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8463 part of the user address space
8464 @throws randolf::rex::xEINTR Interrupted by a signal
8465 @throws randolf::rex::xEINVAL Invalid argument passed
8466 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8467 occur, but the POSIX sockets documentation lists it as one of the
8468 errors that can be returned, perhaps because some incorrectly
8469 implemented TCP/IP stacks return this error?)
8470 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8471 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8472 and 65,527 bytes for IPv6)
8473 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8474 network congestion (or, less commonly, insufficient memory)
8475 @throws randolf::rex::xENOMEM Insufficient memory
8476 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8477 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8478 doesn't refer to a socket
8479 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8480 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8481 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8482 isn't set)
8483 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8484 there's no new data
8485 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8486 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8487 but it really isn't)
8488
8489 @returns The same rsocket object so as to facilitate stacking
8490 @see recvline
8491 @see recv_rline
8492 @see sendline
8493 @see send_rline
8494 @qualifier TLS
8495 *///=========================================================================
8496 rsocket& send_eol(
8497 /// MSG_DONTROUTE@n
8498 /// MSG_DONTWAIT@n
8499 /// MSG_EOR@n
8500 /// MSG_MORE@n
8501 /// MSG_NOSIGNAL@n
8502 /// MSG_OOB
8503 const int posix_flags = 0) {
8504 if (__debug) debug("send_eol(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8505 + ", " + std::to_string(posix_flags)
8506 + ");");
8507 __send_eol(posix_flags);
8508 return *this;
8509 } // -x- rsocket& send_eol -x-
8510
8511 /*======================================================================*//**
8512 @brief
8513 Send data in the form of a @ref randolf::rline to the endpoint, with an EoL
8514 sequence appended.
8515 @par Threads
8516 This method is threadsafe.
8517
8518 @throws randolf::rex::xEBADF The underlying socket is not open
8519 @throws randolf::rex::xECONNRESET Connect reset by peer
8520 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8521 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8522 part of the user address space
8523 @throws randolf::rex::xEINTR Interrupted by a signal
8524 @throws randolf::rex::xEINVAL Invalid argument passed
8525 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8526 occur, but the POSIX sockets documentation lists it as one of the
8527 errors that can be returned, perhaps because some incorrectly
8528 implemented TCP/IP stacks return this error?)
8529 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8530 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8531 and 65,527 bytes for IPv6)
8532 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8533 network congestion (or, less commonly, insufficient memory)
8534 @throws randolf::rex::xENOMEM Insufficient memory
8535 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8536 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8537 doesn't refer to a socket
8538 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8539 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8540 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8541 isn't set)
8542 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8543 there's no new data
8544 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8545 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8546 but it really isn't)
8547
8548 @returns The same rsocket object so as to facilitate stacking
8549 @see eol
8550 @see printfline
8551 @see recv_rline
8552 @see recvline
8553 @see sendline
8554 @see vprintfline
8555 @qualifier TLS
8556 *///=========================================================================
8557 rsocket& send_rline(
8558 /// Data to send
8559 randolf::rline& line,
8560 /// MSG_DONTROUTE@n
8561 /// MSG_DONTWAIT@n
8562 /// MSG_EOR@n
8563 /// MSG_MORE@n
8564 /// MSG_NOSIGNAL@n
8565 /// MSG_OOB
8566 const int posix_flags = 0) {
8567 if (__debug) debug("sendline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8568 + ", <randolf::rline>"
8569 + ", " + std::to_string(line.length())
8570 + ", " + std::to_string(posix_flags)
8571 + ");");
8572 __sendline(line.data(), line.length(), posix_flags);
8573 return *this;
8574 } // -x- rsocket& send_rline -x-
8575
8576 /*======================================================================*//**
8577 @brief
8578 Send a data structure to the endpoint.
8579 @pre
8580 MSB/LSB considerations are important for any integers within your structure,
8581 so be sure to sanitize them with htons(), ntohs(), and related methods prior
8582 to sending, and then after receiving. This way, your data will be protected
8583 against corruption resulting from byte order differences when communicating
8584 between hardware architectures that differ in MSB and LSB byte ordering.
8585 @par Threads
8586 This method is threadsafe.
8587
8588 @throws randolf::rex::xEBADF The underlying socket is not open
8589 @throws randolf::rex::xECONNRESET Connect reset by peer
8590 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8591 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8592 part of the user address space
8593 @throws randolf::rex::xEINTR Interrupted by a signal
8594 @throws randolf::rex::xEINVAL Invalid argument passed
8595 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8596 occur, but the POSIX sockets documentation lists it as one of the
8597 errors that can be returned, perhaps because some incorrectly
8598 implemented TCP/IP stacks return this error?)
8599 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8600 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8601 and 65,527 bytes for IPv6)
8602 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8603 network congestion (or, less commonly, insufficient memory)
8604 @throws randolf::rex::xENOMEM Insufficient memory
8605 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8606 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8607 doesn't refer to a socket
8608 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8609 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8610 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8611 isn't set)
8612 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8613 there's no new data
8614 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8615 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8616 but it really isn't)
8617
8618 @returns The same rsocket object so as to facilitate stacking
8619 @see recv_struct
8620 @qualifier TLS
8621 *///=========================================================================
8622 template <typename T> rsocket& send_struct(
8623 /// Data to send
8624 const T& value, // TODO: Test this to make sure __send(value ...) works; or does it need __send(&value ...)?
8625 /// MSG_DONTROUTE@n
8626 /// MSG_DONTWAIT@n
8627 /// MSG_EOR@n
8628 /// MSG_MORE@n
8629 /// MSG_NOSIGNAL@n
8630 /// MSG_OOB
8631 const int posix_flags = 0) {
8632 if (__debug) debug("send_struct(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8633 + ", <value>"
8634 + ", " + std::to_string(sizeof(value))
8635 + ", " + std::to_string(posix_flags)
8636 + ");");
8637 __send(value, sizeof(value), posix_flags);
8638 return *this;
8639 } // -x- rsocket& send_struct -x-
8640
8641 /*======================================================================*//**
8642 @brief
8643 Send one 16-bit unsigned integer of data in LSB (little endian) order to the
8644 endpoint.
8645 @par Threads
8646 This method is threadsafe.
8647
8648 @throws randolf::rex::xEBADF The underlying socket is not open
8649 @throws randolf::rex::xECONNRESET Connect reset by peer
8650 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8651 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8652 part of the user address space
8653 @throws randolf::rex::xEINTR Interrupted by a signal
8654 @throws randolf::rex::xEINVAL Invalid argument passed
8655 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8656 occur, but the POSIX sockets documentation lists it as one of the
8657 errors that can be returned, perhaps because some incorrectly
8658 implemented TCP/IP stacks return this error?)
8659 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8660 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8661 and 65,527 bytes for IPv6)
8662 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8663 network congestion (or, less commonly, insufficient memory)
8664 @throws randolf::rex::xENOMEM Insufficient memory
8665 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8666 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8667 doesn't refer to a socket
8668 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8669 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8670 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8671 isn't set)
8672 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8673 there's no new data
8674 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8675 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8676 but it really isn't)
8677
8678 @returns The same rsocket object so as to facilitate stacking
8679 @see recv_uint16_lsb
8680 @see send_uint16_msb
8681 @qualifier TLS
8682 *///=========================================================================
8683 rsocket& send_uint16_lsb(
8684 /// Data to send
8685 const uint16_t value,
8686 /// MSG_DONTROUTE@n
8687 /// MSG_DONTWAIT@n
8688 /// MSG_EOR@n
8689 /// MSG_MORE@n
8690 /// MSG_NOSIGNAL@n
8691 /// MSG_OOB
8692 const int posix_flags = 0) {
8693 uint16_t buf = !__endian_is_msb ? value : ntohs(value);
8694 if (__debug) debug("send_uint16_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8695 + ", " + std::to_string(value)
8696 + ", " + std::to_string(sizeof(buf))
8697 + ", " + std::to_string(posix_flags)
8698 + ");");
8699 __send(&buf, sizeof(buf), posix_flags);
8700 return *this;
8701 } // -x- rsocket& send_uint16_lsb -x-
8702
8703 /*======================================================================*//**
8704 @brief
8705 Send one 16-bit integer of data in MSB (big endian) order to the endpoint.
8706 @par Threads
8707 This method is threadsafe.
8708
8709 @throws randolf::rex::xEBADF The underlying socket is not open
8710 @throws randolf::rex::xECONNRESET Connect reset by peer
8711 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8712 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8713 part of the user address space
8714 @throws randolf::rex::xEINTR Interrupted by a signal
8715 @throws randolf::rex::xEINVAL Invalid argument passed
8716 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8717 occur, but the POSIX sockets documentation lists it as one of the
8718 errors that can be returned, perhaps because some incorrectly
8719 implemented TCP/IP stacks return this error?)
8720 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8721 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8722 and 65,527 bytes for IPv6)
8723 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8724 network congestion (or, less commonly, insufficient memory)
8725 @throws randolf::rex::xENOMEM Insufficient memory
8726 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8727 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8728 doesn't refer to a socket
8729 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8730 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8731 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8732 isn't set)
8733 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8734 there's no new data
8735 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8736 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8737 but it really isn't)
8738
8739 @returns The same rsocket object so as to facilitate stacking
8740 @see recv_uint16_msb
8741 @see send_uint16_lsb
8742 @qualifier TLS
8743 *///=========================================================================
8744 rsocket& send_uint16_msb(
8745 /// Data to send
8746 const uint16_t value,
8747 /// MSG_DONTROUTE@n
8748 /// MSG_DONTWAIT@n
8749 /// MSG_EOR@n
8750 /// MSG_MORE@n
8751 /// MSG_NOSIGNAL@n
8752 /// MSG_OOB
8753 const int posix_flags = 0) {
8754 int16_t buf = __endian_is_msb ? value : htons(value);
8755 if (__debug) debug("send_uint16_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8756 + ", " + std::to_string(value)
8757 + ", " + std::to_string(sizeof(buf))
8758 + ", " + std::to_string(posix_flags)
8759 + ");");
8760 __send(&buf, sizeof(buf), posix_flags);
8761 return *this;
8762 } // -x- rsocket& send_int16_msb -x-
8763
8764 /*======================================================================*//**
8765 @brief
8766 Send one 32-bit unsigned integer of data in LSB (little endian) order to the
8767 endpoint.
8768 @par Threads
8769 This method is threadsafe.
8770
8771 @throws randolf::rex::xEBADF The underlying socket is not open
8772 @throws randolf::rex::xECONNRESET Connect reset by peer
8773 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8774 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8775 part of the user address space
8776 @throws randolf::rex::xEINTR Interrupted by a signal
8777 @throws randolf::rex::xEINVAL Invalid argument passed
8778 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8779 occur, but the POSIX sockets documentation lists it as one of the
8780 errors that can be returned, perhaps because some incorrectly
8781 implemented TCP/IP stacks return this error?)
8782 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8783 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8784 and 65,527 bytes for IPv6)
8785 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8786 network congestion (or, less commonly, insufficient memory)
8787 @throws randolf::rex::xENOMEM Insufficient memory
8788 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8789 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8790 doesn't refer to a socket
8791 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8792 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8793 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8794 isn't set)
8795 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8796 there's no new data
8797 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8798 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8799 but it really isn't)
8800
8801 @returns The same rsocket object so as to facilitate stacking
8802 @see recv_uint32_lsb
8803 @see send_uint32_msb
8804 @qualifier TLS
8805 *///=========================================================================
8806 rsocket& send_uint32_lsb(
8807 /// Data to send
8808 const uint32_t value,
8809 /// MSG_DONTROUTE@n
8810 /// MSG_DONTWAIT@n
8811 /// MSG_EOR@n
8812 /// MSG_MORE@n
8813 /// MSG_NOSIGNAL@n
8814 /// MSG_OOB
8815 const int posix_flags = 0) {
8816 uint32_t buf = !__endian_is_msb ? value : ntohl(value);
8817 if (__debug) debug("send_uint32_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8818 + ", " + std::to_string(value)
8819 + ", " + std::to_string(sizeof(buf))
8820 + ", " + std::to_string(posix_flags)
8821 + ");");
8822 __send(&buf, sizeof(buf), posix_flags);
8823 return *this;
8824 } // -x- rsocket& send_uint32_lsb -x-
8825
8826 /*======================================================================*//**
8827 @brief
8828 Send one 32-bit unsigned integer of data in MSB (big endian) order to the
8829 endpoint.
8830 @par Threads
8831 This method is threadsafe.
8832
8833 @throws randolf::rex::xEBADF The underlying socket is not open
8834 @throws randolf::rex::xECONNRESET Connect reset by peer
8835 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8836 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8837 part of the user address space
8838 @throws randolf::rex::xEINTR Interrupted by a signal
8839 @throws randolf::rex::xEINVAL Invalid argument passed
8840 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8841 occur, but the POSIX sockets documentation lists it as one of the
8842 errors that can be returned, perhaps because some incorrectly
8843 implemented TCP/IP stacks return this error?)
8844 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8845 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8846 and 65,527 bytes for IPv6)
8847 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8848 network congestion (or, less commonly, insufficient memory)
8849 @throws randolf::rex::xENOMEM Insufficient memory
8850 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8851 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8852 doesn't refer to a socket
8853 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8854 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8855 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8856 isn't set)
8857 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8858 there's no new data
8859 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8860 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8861 but it really isn't)
8862
8863 @returns The same rsocket object so as to facilitate stacking
8864 @see recv_uint32_msb
8865 @see send_uint32_lsb
8866 @qualifier TLS
8867 *///=========================================================================
8868 rsocket& send_uint32_msb(
8869 /// Data to send
8870 const uint32_t value,
8871 /// MSG_DONTROUTE@n
8872 /// MSG_DONTWAIT@n
8873 /// MSG_EOR@n
8874 /// MSG_MORE@n
8875 /// MSG_NOSIGNAL@n
8876 /// MSG_OOB
8877 const int posix_flags = 0) {
8878 uint32_t buf = __endian_is_msb ? value : htonl(value);
8879 if (__debug) debug("send_uint32_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8880 + ", " + std::to_string(value)
8881 + ", " + std::to_string(sizeof(buf))
8882 + ", " + std::to_string(posix_flags)
8883 + ");");
8884 __send(&buf, sizeof(buf), posix_flags);
8885 return *this;
8886 } // -x- rsocket& send_uint32_msb -x-
8887
8888 /*======================================================================*//**
8889 @brief
8890 Send one 64-bit unsigned integer of data in LSB (little endian) order to the
8891 endpoint.
8892 @par Threads
8893 This method is threadsafe.
8894
8895 @throws randolf::rex::xEBADF The underlying socket is not open
8896 @throws randolf::rex::xECONNRESET Connect reset by peer
8897 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8898 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8899 part of the user address space
8900 @throws randolf::rex::xEINTR Interrupted by a signal
8901 @throws randolf::rex::xEINVAL Invalid argument passed
8902 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8903 occur, but the POSIX sockets documentation lists it as one of the
8904 errors that can be returned, perhaps because some incorrectly
8905 implemented TCP/IP stacks return this error?)
8906 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8907 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8908 and 65,527 bytes for IPv6)
8909 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8910 network congestion (or, less commonly, insufficient memory)
8911 @throws randolf::rex::xENOMEM Insufficient memory
8912 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8913 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8914 doesn't refer to a socket
8915 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8916 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8917 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8918 isn't set)
8919 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8920 there's no new data
8921 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8922 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8923 but it really isn't)
8924
8925 @returns The same rsocket object so as to facilitate stacking
8926 @see recv_uint64_lsb
8927 @see send_uint64_msb
8928 @qualifier TLS
8929 *///=========================================================================
8930 rsocket& send_uint64_lsb(
8931 /// Data to send
8932 const uint64_t value,
8933 /// MSG_DONTROUTE@n
8934 /// MSG_DONTWAIT@n
8935 /// MSG_EOR@n
8936 /// MSG_MORE@n
8937 /// MSG_NOSIGNAL@n
8938 /// MSG_OOB
8939 const int posix_flags = 0) {
8940 uint64_t buf = !__endian_is_msb ? value : ((((uint64_t)ntohl((value) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((value) >> 32)));
8941 if (__debug) debug("send_uint64_lsb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
8942 + ", " + std::to_string(value)
8943 + ", " + std::to_string(sizeof(buf))
8944 + ", " + std::to_string(posix_flags)
8945 + ");");
8946 __send(&buf, sizeof(buf), posix_flags);
8947 return *this;
8948 } // -x- rsocket& send_uint64_lsb -x-
8949
8950 /*======================================================================*//**
8951 @brief
8952 Send one 64-bit unsigned integer of data in MSB (big endian) order to the
8953 endpoint.
8954 @par Threads
8955 This method is threadsafe.
8956
8957 @throws randolf::rex::xEBADF The underlying socket is not open
8958 @throws randolf::rex::xECONNRESET Connect reset by peer
8959 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
8960 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
8961 part of the user address space
8962 @throws randolf::rex::xEINTR Interrupted by a signal
8963 @throws randolf::rex::xEINVAL Invalid argument passed
8964 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
8965 occur, but the POSIX sockets documentation lists it as one of the
8966 errors that can be returned, perhaps because some incorrectly
8967 implemented TCP/IP stacks return this error?)
8968 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
8969 sent atomically (the maximum size is typically 65,507 bytes for IPv4
8970 and 65,527 bytes for IPv6)
8971 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
8972 network congestion (or, less commonly, insufficient memory)
8973 @throws randolf::rex::xENOMEM Insufficient memory
8974 @throws randolf::rex::xENOTCONN Underlying socket is not connected
8975 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
8976 doesn't refer to a socket
8977 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
8978 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
8979 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
8980 isn't set)
8981 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
8982 there's no new data
8983 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
8984 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
8985 but it really isn't)
8986
8987 @returns The same rsocket object so as to facilitate stacking
8988 @see recv_uint64_msb
8989 @see send_uint64_lsb
8990 @qualifier TLS
8991 *///=========================================================================
8992 rsocket& send_uint64_msb(
8993 /// Data to send
8994 const uint64_t value,
8995 /// MSG_DONTROUTE@n
8996 /// MSG_DONTWAIT@n
8997 /// MSG_EOR@n
8998 /// MSG_MORE@n
8999 /// MSG_NOSIGNAL@n
9000 /// MSG_OOB
9001 const int posix_flags = 0) {
9002 uint64_t buf = __endian_is_msb ? value : ((((uint64_t)htonl((value) & 0xffffffffUL)) << 32) | htonl((uint32_t)((value) >> 32)));
9003 if (__debug) debug("send_uint64_msb(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9004 + ", " + std::to_string(value)
9005 + ", " + std::to_string(sizeof(buf))
9006 + ", " + std::to_string(posix_flags)
9007 + ");");
9008 __send(&buf, sizeof(buf), posix_flags);
9009 return *this;
9010 } // -x- rsocket& send_uint64_msb -x-
9011
9012 /*======================================================================*//**
9013 @brief
9014 Send data in the form of a std::string to the endpoint, with an EoL sequence
9015 appended.
9016 @par Threads
9017 This method is threadsafe.
9018
9019 @throws randolf::rex::xEBADF The underlying socket is not open
9020 @throws randolf::rex::xECONNRESET Connect reset by peer
9021 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
9022 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9023 part of the user address space
9024 @throws randolf::rex::xEINTR Interrupted by a signal
9025 @throws randolf::rex::xEINVAL Invalid argument passed
9026 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
9027 occur, but the POSIX sockets documentation lists it as one of the
9028 errors that can be returned, perhaps because some incorrectly
9029 implemented TCP/IP stacks return this error?)
9030 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
9031 sent atomically (the maximum size is typically 65,507 bytes for IPv4
9032 and 65,527 bytes for IPv6)
9033 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
9034 network congestion (or, less commonly, insufficient memory)
9035 @throws randolf::rex::xENOMEM Insufficient memory
9036 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9037 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9038 doesn't refer to a socket
9039 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
9040 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
9041 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
9042 isn't set)
9043 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
9044 there's no new data
9045 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
9046 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
9047 but it really isn't)
9048
9049 @returns The same rsocket object so as to facilitate stacking
9050 @see eol
9051 @see printfline
9052 @see recvline
9053 @see send_rline
9054 @see vprintfline
9055 @qualifier TLS
9056 *///=========================================================================
9057 rsocket& sendline(
9058 /// Data to send
9059 const std::string& msg,
9060 /// MSG_DONTROUTE@n
9061 /// MSG_DONTWAIT@n
9062 /// MSG_EOR@n
9063 /// MSG_MORE@n
9064 /// MSG_NOSIGNAL@n
9065 /// MSG_OOB
9066 const int posix_flags = 0) {
9067 if (__debug) debug("sendline(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9068 + ", <std::string>"
9069 + ", " + std::to_string(msg.length())
9070 + "+" + std::to_string(__eol_out.length())
9071 + ", " + std::to_string(posix_flags)
9072 + ");");
9073 __sendline(msg.data(), msg.length(), posix_flags);
9074 return *this;
9075 } // -x- rsocket& sendline -x-
9076
9077 /*======================================================================*//**
9078 @brief
9079 Send data in the form of a "msghdr" structure to a specific endpoint.
9080 @warning
9081 This method is not compatible with TLS.
9082 @par Threads
9083 This method is threadsafe.
9084
9085 @throws randolf::rex::xEBADF The underlying socket is not open
9086 @throws randolf::rex::xECONNRESET Connect reset by peer
9087 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
9088 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9089 part of the user address space
9090 @throws randolf::rex::xEINTR Interrupted by a signal
9091 @throws randolf::rex::xEINVAL Invalid argument passed
9092 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
9093 occur, but the POSIX sockets documentation lists it as one of the
9094 errors that can be returned, perhaps because some incorrectly
9095 implemented TCP/IP stacks return this error?)
9096 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
9097 sent atomically (the maximum size is typically 65,507 bytes for IPv4
9098 and 65,527 bytes for IPv6)
9099 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
9100 network congestion (or, less commonly, insufficient memory)
9101 @throws randolf::rex::xENOMEM Insufficient memory
9102 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9103 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9104 doesn't refer to a socket
9105 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
9106 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
9107 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
9108 isn't set)
9109 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
9110 there's no new data
9111 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
9112 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
9113 but it really isn't)
9114
9115 @returns The same rsocket object so as to facilitate stacking
9116 @see recvmsg
9117 @qualifier POSIX
9118 *///=========================================================================
9119 rsocket& sendmsg(
9120 /// Pointer to data to send
9121 const struct msghdr* msg,
9122 /// MSG_DONTROUTE@n
9123 /// MSG_DONTWAIT@n
9124 /// MSG_EOR@n
9125 /// MSG_MORE@n
9126 /// MSG_NOSIGNAL@n
9127 /// MSG_OOB
9128 const int posix_flags = 0) {
9129 if (__debug) debug("sendmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9130 + ", <msghdr>"
9131 + ", " + std::to_string(posix_flags)
9132 + ");");
9133 __track_bytes_tx(__rc_check(::sendmsg(__socket_fd, msg, posix_flags)));
9134 return *this;
9135 } // -x- rsocket& sendmsg -x-
9136
9137 /*======================================================================*//**
9138 @brief
9139 Send data in the form of a "mmsghdr" structure to a specific endpoint.
9140 @warning
9141 This method is not compatible with TLS.
9142 @par Threads
9143 This method is threadsafe.
9144
9145 @throws randolf::rex::xEBADF The underlying socket is not open
9146 @throws randolf::rex::xECONNRESET Connect reset by peer
9147 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
9148 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9149 part of the user address space
9150 @throws randolf::rex::xEINTR Interrupted by a signal
9151 @throws randolf::rex::xEINVAL Invalid argument passed
9152 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
9153 occur, but the POSIX sockets documentation lists it as one of the
9154 errors that can be returned, perhaps because some incorrectly
9155 implemented TCP/IP stacks return this error?)
9156 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
9157 sent atomically (the maximum size is typically 65,507 bytes for IPv4
9158 and 65,527 bytes for IPv6)
9159 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
9160 network congestion (or, less commonly, insufficient memory)
9161 @throws randolf::rex::xENOMEM Insufficient memory
9162 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9163 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9164 doesn't refer to a socket
9165 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
9166 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
9167 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
9168 isn't set)
9169 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
9170 there's no new data
9171 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
9172 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
9173 but it really isn't)
9174
9175 @returns The same rsocket object so as to facilitate stacking
9176 @see recvmmsg
9177 @qualifier POSIX
9178 *///=========================================================================
9179 rsocket& sendmmsg(
9180 /// Pointer to data to send
9181 struct mmsghdr* mmsg,
9182 /// Size of target endpoint structure
9183 const unsigned int vlen = sizeof(mmsghdr),
9184 /// MSG_DONTROUTE@n
9185 /// MSG_DONTWAIT@n
9186 /// MSG_EOR@n
9187 /// MSG_MORE@n
9188 /// MSG_NOSIGNAL@n
9189 /// MSG_OOB
9190 const int posix_flags = 0) {
9191 if (__debug) debug("sendmmsg(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9192 + ", <mmsghdr>"
9193 + ", " + std::to_string(vlen)
9194 + ", " + std::to_string(posix_flags)
9195 + ");");
9196 __track_bytes_tx(__rc_check(::sendmmsg(__socket_fd, mmsg, vlen, posix_flags)));
9197 return *this;
9198 } // -x- rsocket& sendmsg -x-
9199
9200 /*======================================================================*//**
9201 @brief
9202 Send data in the form of a std::string to a specific endpoint.
9203 @warning
9204 This method is not compatible with TLS.
9205 @par Threads
9206 This method is threadsafe.
9207
9208 @throws randolf::rex::xEBADF The underlying socket is not open
9209 @throws randolf::rex::xECONNRESET Connect reset by peer
9210 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
9211 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9212 part of the user address space
9213 @throws randolf::rex::xEINTR Interrupted by a signal
9214 @throws randolf::rex::xEINVAL Invalid argument passed
9215 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
9216 occur, but the POSIX sockets documentation lists it as one of the
9217 errors that can be returned, perhaps because some incorrectly
9218 implemented TCP/IP stacks return this error?)
9219 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
9220 sent atomically (the maximum size is typically 65,507 bytes for IPv4
9221 and 65,527 bytes for IPv6)
9222 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
9223 network congestion (or, less commonly, insufficient memory)
9224 @throws randolf::rex::xENOMEM Insufficient memory
9225 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9226 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9227 doesn't refer to a socket
9228 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
9229 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
9230 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
9231 isn't set)
9232 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
9233 there's no new data
9234 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
9235 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
9236 but it really isn't)
9237
9238 @returns The same rsocket object so as to facilitate stacking
9239 @see recvfrom
9240 @qualifier POSIX
9241 *///=========================================================================
9242 rsocket& sendto(
9243 /// Data to send
9244 const std::string& msg,
9245 /// MSG_DONTROUTE@n
9246 /// MSG_DONTWAIT@n
9247 /// MSG_EOR@n
9248 /// MSG_MORE@n
9249 /// MSG_NOSIGNAL@n
9250 /// MSG_OOB
9251 const int posix_flags,
9252 /// Target endpoint address structure
9253 const struct sockaddr *to,
9254 /// Size of target endpoint structure
9255 socklen_t tolen = sizeof(sockaddr)) {
9256 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9257 + ", <std::string>"
9258 + ", " + std::to_string(posix_flags)
9259 + ", <sockaddr>"
9260 + ", " + std::to_string(tolen)
9261 + ");");
9262 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg.data(), msg.length(), posix_flags, to, tolen)));
9263 return *this;
9264 } // -x- rsocket& sendto -x- // TODO: Create easier-to-use variants
9265
9266 /*======================================================================*//**
9267 @brief
9268 Send data in the form of a C-string to a specific endpoint.
9269 @warning
9270 This method is not compatible with TLS.
9271 @par Threads
9272 This method is threadsafe.
9273
9274 @throws randolf::rex::xEBADF The underlying socket is not open
9275 @throws randolf::rex::xECONNRESET Connect reset by peer
9276 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
9277 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9278 part of the user address space
9279 @throws randolf::rex::xEINTR Interrupted by a signal
9280 @throws randolf::rex::xEINVAL Invalid argument passed
9281 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
9282 occur, but the POSIX sockets documentation lists it as one of the
9283 errors that can be returned, perhaps because some incorrectly
9284 implemented TCP/IP stacks return this error?)
9285 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
9286 sent atomically (the maximum size is typically 65,507 bytes for IPv4
9287 and 65,527 bytes for IPv6)
9288 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
9289 network congestion (or, less commonly, insufficient memory)
9290 @throws randolf::rex::xENOMEM Insufficient memory
9291 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9292 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9293 doesn't refer to a socket
9294 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
9295 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
9296 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
9297 isn't set)
9298 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
9299 there's no new data
9300 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
9301 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
9302 but it really isn't)
9303
9304 @returns The same rsocket object so as to facilitate stacking
9305 @see recvfrom
9306 @qualifier POSIX
9307 *///=========================================================================
9308 rsocket& sendto(
9309 /// Pointer to data to send
9310 const char* msg,
9311 /// Number of bytes to send
9312 const size_t len,
9313 /// MSG_DONTROUTE@n
9314 /// MSG_DONTWAIT@n
9315 /// MSG_EOR@n
9316 /// MSG_MORE@n
9317 /// MSG_NOSIGNAL@n
9318 /// MSG_OOB
9319 const int posix_flags,
9320 /// Target endpoint address structure
9321 const struct sockaddr *to,
9322 /// Size of target endpoint structure
9323 socklen_t tolen = sizeof(sockaddr)) {
9324 if (__debug) debug("sendto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9325 + ", <msg>"
9326 + ", " + std::to_string(std::strlen(msg))
9327 + ", " + std::to_string(posix_flags)
9328 + ", <sockaddr>"
9329 + ", " + std::to_string(tolen)
9330 + ");");
9331 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, len, posix_flags, to, tolen)));
9332 return *this;
9333 } // -x- rsocket& sendto -x-
9334
9335 /*======================================================================*//**
9336 @brief
9337 Send data in the form of an ASCIIZ string to the endpoint. The terminating
9338 NULL character won't be transmitted.
9339 @par Threads
9340 This method is threadsafe.
9341
9342 @throws randolf::rex::xEBADF The underlying socket is not open
9343 @throws randolf::rex::xECONNRESET Connect reset by peer
9344 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
9345 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9346 part of the user address space
9347 @throws randolf::rex::xEINTR Interrupted by a signal
9348 @throws randolf::rex::xEINVAL Invalid argument passed
9349 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
9350 occur, but the POSIX sockets documentation lists it as one of the
9351 errors that can be returned, perhaps because some incorrectly
9352 implemented TCP/IP stacks return this error?)
9353 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
9354 sent atomically (the maximum size is typically 65,507 bytes for IPv4
9355 and 65,527 bytes for IPv6)
9356 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
9357 network congestion (or, less commonly, insufficient memory)
9358 @throws randolf::rex::xENOMEM Insufficient memory
9359 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9360 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9361 doesn't refer to a socket
9362 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
9363 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
9364 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
9365 isn't set)
9366 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
9367 there's no new data
9368 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
9369 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
9370 but it really isn't)
9371
9372 @returns The same rsocket object so as to facilitate stacking
9373 @see recvz(const size_t, const int)
9374 @see send_asciiz(const char*, const int) which also transmits the terminating
9375 NULL character
9376 @qualifier TLS
9377 *///=========================================================================
9378 rsocket& sendz(
9379 /// Pointer to data to send
9380 const char* msg,
9381 /// MSG_DONTROUTE@n
9382 /// MSG_DONTWAIT@n
9383 /// MSG_EOR@n
9384 /// MSG_MORE@n
9385 /// MSG_NOSIGNAL@n
9386 /// MSG_OOB
9387 const int posix_flags = 0) {
9388 if (__debug) debug("sendz(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9389 + ", <msg>"
9390 + ", " + std::to_string(std::strlen(msg))
9391 + ", " + std::to_string(posix_flags)
9392 + ");");
9393 __send(msg, std::strlen(msg), posix_flags);
9394 return *this;
9395 } // -x- rsocket& sendz -x-
9396
9397 /*======================================================================*//**
9398 @brief
9399 Send data in the form of an ASCIIZ string to a specific endpoint. The
9400 terminating NULL character won't be transmitted.
9401 @warning
9402 This method is not compatible with TLS.
9403 @par Threads
9404 This method is threadsafe.
9405
9406 @throws randolf::rex::xEBADF The underlying socket is not open
9407 @throws randolf::rex::xECONNRESET Connect reset by peer
9408 @throws randolf::rex::xEDESTADDRREQ Socket is not connected to an endpoint
9409 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9410 part of the user address space
9411 @throws randolf::rex::xEINTR Interrupted by a signal
9412 @throws randolf::rex::xEINVAL Invalid argument passed
9413 @throws randolf::rex::xEISCONN Socket is already connected (this shouldn't
9414 occur, but the POSIX sockets documentation lists it as one of the
9415 errors that can be returned, perhaps because some incorrectly
9416 implemented TCP/IP stacks return this error?)
9417 @throws randolf::rex::xEMSGSIZE Message data exceeds the maximum size to be
9418 sent atomically (the maximum size is typically 65,507 bytes for IPv4
9419 and 65,527 bytes for IPv6)
9420 @throws randolf::rex::xENOBUFS Output queue is full, mostly likely due to
9421 network congestion (or, less commonly, insufficient memory)
9422 @throws randolf::rex::xENOMEM Insufficient memory
9423 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9424 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9425 doesn't refer to a socket
9426 @throws randolf::rex::xEOPNOTSUPP Underlying socket is no of type SOCK_STREAM
9427 @throws randolf::rex::xEPIPE Connection-oriented socket was shut down on the
9428 local end (and @c SIGPIPE will also be received if @c MSG_NOSIGNAL
9429 isn't set)
9430 @throws randolf::rex::xEWOULDBLOCK The underlying socket is non-blocking and
9431 there's no new data
9432 @throws randolf::rex::xEWOULDBLOCK No ephemeral ports are available for
9433 assignment to unbound socket (should be randolf::rex::xEADDRNOTAVAIL,
9434 but it really isn't)
9435
9436 @see recvfrom
9437 @see sendto
9438 @see sendz
9439 @returns The same rsocket object so as to facilitate stacking
9440 *///=========================================================================
9441 rsocket& sendzto(
9442 /// Pointer to data to send
9443 const char* msg,
9444 /// MSG_DONTROUTE@n
9445 /// MSG_DONTWAIT@n
9446 /// MSG_EOR@n
9447 /// MSG_MORE@n
9448 /// MSG_NOSIGNAL@n
9449 /// MSG_OOB
9450 const int posix_flags,
9451 /// Target endpoint address structure
9452 const struct sockaddr* to,
9453 /// Size of target endpoint structure
9454 socklen_t tolen = sizeof(sockaddr)) {
9455 if (__debug) debug("sendzto(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9456 + ", <msg>"
9457 + ", " + std::to_string(std::strlen(msg))
9458 + ", " + std::to_string(posix_flags)
9459 + ", <sockaddr>"
9460 + ", " + std::to_string(tolen)
9461 + ");");
9462 __track_bytes_tx(__rc_check(::sendto(__socket_fd, msg, std::strlen(msg), posix_flags, to, tolen)));
9463 return *this;
9464 } // -x- rsocket& sendzto -x-
9465
9466 /*======================================================================*//**
9467 @brief
9468 Set socket option with a zero-length value, which is a special case usage
9469 that will probably never be needed, but is included here for future use where
9470 the call to @c setsockopt() would include specifying @c option_value's length
9471 in the final parameter @c option_len as zero.
9472
9473 (Internally, we still populate the pointer for @c option_value to that of an
9474 internal `static const int` {which does have the value of 0} so as to avoid
9475 triggering an unexpected error where this parameter can't be @c nullptr).
9476
9477 @par Notes
9478 These setsockopt() methods take an integer or character value directly, or a
9479 pointer to a structure, and then rsocket handles the remaining tedious
9480 technical details behind-the-scenes for you when calling the underlying
9481 socket's setsockopt() function.
9482
9483 @throws randolf::rex::xEBADF The underlying socket is not open
9484 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9485 part of the user address space
9486 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
9487 valid for this socket's family (a.k.a., communication domain)
9488 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
9489 is not supported
9490 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9491 doesn't refer to a socket
9492
9493 @returns The same rsocket object so as to facilitate stacking
9494 @see getsockopt_int
9495 @qualifier POSIX
9496 @qualifier TLS
9497 *///=========================================================================
9498 rsocket& setsockopt(
9499 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9500 /// also be @c IPPROTO_TCP, etc.
9501 const int level,
9502 /// The name of the option, such as @c TCP_CORK, etc.
9503 const int option) {
9504 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option);
9505 static const int value = 0;
9506 __rc_check(::setsockopt(__socket_fd, level, option, &value, 0));
9507 return *this;
9508 } // -x- rsocket& setsockopt -x-
9509
9510 /*======================================================================*//**
9511 @brief
9512 Set socket option to the specific `integer`.
9513
9514 @par Notes
9515 These setsockopt() methods take an integer or character value directly, or a
9516 pointer to a structure, and then rsocket handles the remaining tedious
9517 technical details behind-the-scenes for you when calling the underlying
9518 socket's setsockopt() function.
9519
9520 @throws randolf::rex::xEBADF The underlying socket is not open
9521 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9522 part of the user address space
9523 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
9524 valid for this socket's family (a.k.a., communication domain)
9525 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
9526 is not supported
9527 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9528 doesn't refer to a socket
9529
9530 @returns The same rsocket object so as to facilitate stacking
9531 @see getsockopt_int(const int, const int)
9532 @qualifier POSIX
9533 @qualifier TLS
9534 *///=========================================================================
9535 rsocket& setsockopt(
9536 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9537 /// also be @c IPPROTO_TCP, etc.
9538 const int level,
9539 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9540 const int option,
9541 /// The value that this socket option will be set to
9542 const int value) {
9543 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
9544 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9545 return *this;
9546 } // -x- rsocket& setsockopt -x-
9547
9548 /*======================================================================*//**
9549 @brief
9550 Set socket option to the specific `unsigned integer`.
9551 @copydetails setsockopt(const int, const int, const int)
9552
9553 @pre
9554 For any values that require a u_int, you'll need to explicitly cast this type
9555 when specifying the value directly; for example: (u_int)32768
9556
9557 @throws randolf::rex::xEBADF The underlying socket is not open
9558 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
9559 part of the user address space
9560 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
9561 valid for this socket's family (a.k.a., communication domain)
9562 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
9563 is not supported
9564 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9565 doesn't refer to a socket
9566
9567 @returns The same rsocket object so as to facilitate stacking
9568 @see getsockopt_u_int(const int, const int)
9569 @qualifier TLS
9570 *///=========================================================================
9571 rsocket& setsockopt(
9572 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9573 /// also be @c IPPROTO_TCP, etc.
9574 const int level,
9575 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9576 const int option,
9577 /// The value that this socket option will be set to
9578 const u_int value) {
9579 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
9580 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9581 return *this;
9582 } // -x- rsocket& setsockopt -x-
9583
9584 /*======================================================================*//**
9585 @brief
9586 Set socket option to the specific `unsigned character`.
9587 @copydetails setsockopt(const int, const int, const int)
9588 @see getsockopt_u_char(const int, const int)
9589 @qualifier TLS
9590 *///=========================================================================
9591 rsocket& setsockopt(
9592 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9593 /// also be @c IPPROTO_TCP, etc.
9594 const int level,
9595 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9596 const int option,
9597 /// The value that this socket option will be set to
9598 const u_char value) {
9599 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, value);
9600 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9601 return *this;
9602 } // -x- rsocket& setsockopt -x-
9603
9604 /*======================================================================*//**
9605 @brief
9606 Set socket option to the specific `linger` structure.
9607 @copydetails setsockopt(const int, const int, const int)
9608 @see getsockopt_linger(const int, const int)
9609 @qualifier TLS
9610 *///=========================================================================
9611 rsocket& setsockopt(
9612 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9613 /// also be @c IPPROTO_TCP, etc.
9614 const int level,
9615 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9616 const int option,
9617 /// The structure that this socket option will be set to
9618 const linger& value) {
9619 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9620 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9621 return *this;
9622 } // -x- rsocket& setsockopt -x-
9623
9624 /*======================================================================*//**
9625 @brief
9626 Set socket option to the specific `timeval` structure.
9627 @copydetails setsockopt(const int, const int, const linger&)
9628 @see getsockopt_timeval(const int, const int)
9629 @qualifier TLS
9630 *///=========================================================================
9631 rsocket& setsockopt(
9632 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9633 /// also be @c IPPROTO_TCP, etc.
9634 const int level,
9635 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9636 const int option,
9637 /// The structure that this socket option will be set to
9638 const timeval& value) {
9639 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9640 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9641 return *this;
9642 } // -x- rsocket& setsockopt -x-
9643
9644 /*======================================================================*//**
9645 @brief
9646 Set socket option to the specific `in_addr` structure.
9647 @copydetails setsockopt(const int, const int, const linger&)
9648 @see getsockopt_in_addr(const int, const int)
9649 @qualifier TLS
9650 *///=========================================================================
9651 rsocket& setsockopt(
9652 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9653 /// also be @c IPPROTO_TCP, etc.
9654 const int level,
9655 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9656 const int option,
9657 /// The structure that this socket option will be set to
9658 const in_addr& value) {
9659 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9660 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9661 return *this;
9662 } // -x- rsocket& setsockopt -x-
9663
9664 /*======================================================================*//**
9665 @brief
9666 Set socket option to the specific `ip_mreq` structure.
9667 @copydetails setsockopt(const int, const int, const linger&)
9668 @see getsockopt_ip_mreq(const int, const int)
9669 @qualifier TLS
9670 *///=========================================================================
9671 rsocket& setsockopt(
9672 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9673 /// also be @c IPPROTO_TCP, etc.
9674 const int level,
9675 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9676 const int option,
9677 /// The structure that this socket option will be set to
9678 const ip_mreq& value) {
9679 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9680 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9681 return *this;
9682 } // -x- rsocket& setsockopt -x-
9683
9684 /*======================================================================*//**
9685 @brief
9686 Set socket option to the specific `ip_mreq_source` structure.
9687 @copydetails setsockopt(const int, const int, const linger&)
9688 @see getsockopt_ip_mreq_source(const int, const int)
9689 @qualifier TLS
9690 *///=========================================================================
9691 rsocket& setsockopt(
9692 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9693 /// also be @c IPPROTO_TCP, etc.
9694 const int level,
9695 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9696 const int option,
9697 /// The structure that this socket option will be set to
9698 const ip_mreq_source& value) {
9699 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9700 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9701 return *this;
9702 } // -x- rsocket& setsockopt -x-
9703
9704 /*======================================================================*//**
9705 @brief
9706 Set socket option to the specific `ip_mreqn` structure.
9707 @copydetails setsockopt(const int, const int, const linger&)
9708 @see getsockopt_ip_mreqn(const int, const int)
9709 @qualifier TLS
9710 *///=========================================================================
9711 rsocket& setsockopt(
9712 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9713 /// also be @c IPPROTO_TCP, etc.
9714 const int level,
9715 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9716 const int option,
9717 /// The structure that this socket option will be set to
9718 const ip_mreqn& value) {
9719 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9720 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9721 return *this;
9722 } // -x- rsocket& setsockopt -x-
9723
9724 /*======================================================================*//**
9725 @brief
9726 Set socket option to the specific `icmp6_filter` structure.
9727 @copydetails setsockopt(const int, const int, const linger&)
9728 @see getsockopt_icmp6_filter(const int, const int)
9729 @qualifier TLS
9730 *///=========================================================================
9731 rsocket& setsockopt(
9732 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9733 /// also be @c IPPROTO_TCP, etc.
9734 const int level,
9735 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9736 const int option,
9737 /// The structure that this socket option will be set to
9738 const icmp6_filter& value) {
9739 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9740 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9741 return *this;
9742 } // -x- rsocket& setsockopt -x-
9743
9744 /*======================================================================*//**
9745 @brief
9746 Set socket option to the specific `sockaddr_in6` structure.
9747 @copydetails setsockopt(const int, const int, const linger&)
9748 @see getsockopt_sockaddr_in6(const int, const int)
9749 @qualifier TLS
9750 *///=========================================================================
9751 rsocket& setsockopt(
9752 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9753 /// also be @c IPPROTO_TCP, etc.
9754 const int level,
9755 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9756 const int option,
9757 /// The structure that this socket option will be set to
9758 const sockaddr_in6& value) {
9759 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9760 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9761 return *this;
9762 } // -x- rsocket& setsockopt -x-
9763
9764 /*======================================================================*//**
9765 @brief
9766 Set socket option to the specific `ip6_mtuinfo` structure.
9767 @copydetails setsockopt(const int, const int, const linger&)
9768 @see getsockopt_ip6_mtuinfo(const int, const int)
9769 @qualifier TLS
9770 *///=========================================================================
9771 rsocket& setsockopt(
9772 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9773 /// also be @c IPPROTO_TCP, etc.
9774 const int level,
9775 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9776 const int option,
9777 /// The structure that this socket option will be set to
9778 const ip6_mtuinfo& value) {
9779 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9780 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9781 return *this;
9782 } // -x- rsocket& setsockopt -x-
9783
9784 /*======================================================================*//**
9785 @brief
9786 Set socket option to the specific `ipv6_mreq` structure.
9787 @copydetails setsockopt(const int, const int, const linger&)
9788 @see getsockopt_ipv6_mreq(const int, const int)
9789 @qualifier TLS
9790 *///=========================================================================
9791 rsocket& setsockopt(
9792 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9793 /// also be @c IPPROTO_TCP, etc.
9794 const int level,
9795 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9796 const int option,
9797 /// The structure that this socket option will be set to
9798 const ipv6_mreq& value) {
9799 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9800 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9801 return *this;
9802 } // -x- rsocket& setsockopt -x-
9803
9804 /*======================================================================*//**
9805 @brief
9806 Set socket option to the specific `group_req` structure.
9807 @copydetails setsockopt(const int, const int, const linger&)
9808 @see getsockopt_group_req(const int, const int)
9809 @qualifier TLS
9810 *///=========================================================================
9811 rsocket& setsockopt(
9812 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9813 /// also be @c IPPROTO_TCP, etc.
9814 const int level,
9815 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9816 const int option,
9817 /// The structure that this socket option will be set to
9818 const group_req& value) {
9819 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9820 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9821 return *this;
9822 } // -x- rsocket& setsockopt -x-
9823
9824 /*======================================================================*//**
9825 @brief
9826 Set socket option to the specific `group_source_req` structure.
9827 @copydetails setsockopt(const int, const int, const linger&)
9828 @see getsockopt_group_source_req(const int, const int)
9829 @qualifier TLS
9830 *///=========================================================================
9831 rsocket& setsockopt(
9832 /// The level at which the option resides; typically @c SOL_SOCKET, or could
9833 /// also be @c IPPROTO_TCP, etc.
9834 const int level,
9835 /// The name of the option, such as @c SO_REUSEADDR, @c SO_LINGER, etc.
9836 const int option,
9837 /// The structure that this socket option will be set to
9838 const group_source_req& value) {
9839 if (__debug) __debug_sockopt("setsockopt(socket{0x", level, option, &value);
9840 __rc_check(::setsockopt(__socket_fd, level, option, &value, sizeof(value)));
9841 return *this;
9842 } // -x- rsocket& setsockopt -x-
9843
9844 /*======================================================================*//**
9845 @brief
9846 Shut down the underlying socket, partially or fully.
9847
9848 <div style=padding-left:32px;>
9849 <table>
9850 <tr>
9851 <td valign=top>SHUT_RD:</td>
9852 <td>Further receives will be disallowed.</td>
9853 </tr>
9854 <tr>
9855 <td valign=top>SHUT_WR:</td>
9856 <td>Further sends will be disallowed (this may cause actions specific
9857 to the protocol family of the socket to occur).</td>
9858 </tr>
9859 <tr>
9860 <td valign=top>SHUT_RDWR:</td>
9861 <td>Further sends and receives will be disallowed (default).</td>
9862 </tr>
9863 </table>
9864 </div>
9865
9866 @throws randolf::rex::xEBADF The underlying socket is not open
9867 @throws randolf::rex::xEINVAL Invalid argument passed
9868 @throws randolf::rex::xENOTCONN Underlying socket is not connected
9869 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
9870 doesn't refer to a socket
9871
9872 @returns The same rsocket object so as to facilitate stacking
9873 @see close
9874 @see close_passive
9875 @see connect
9876 @qualifier POSIX
9877 @qualifier TLS
9878 *///=========================================================================
9879 rsocket& shutdown(
9880 /// SHUT_RD@n
9881 /// SHUT_RW@n
9882 /// SHUT_RDWR (default)
9883 const int how = SHUT_RDWR,
9884 /// Shutdown the TLS connection too (but only if it's alrady enabled), which
9885 /// is the default (disabling this will usually result in errors for the
9886 /// endpoint upon raw socket shutdown)
9887 const bool tls_shutdown = true,
9888 /// Whether to track for the destructor whether this method was called; by
9889 /// default, the destructor will automatically call `shutdown(SHUT_RDRW)` on
9890 /// an rsocket that wasn't closed before closing it unless this @c shutdown()
9891 /// was called at least once.
9892 const bool track_shutdown_for_destructor = true) {
9893 if (__debug) debug("shutdown(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
9894 + ", " + std::to_string(how)
9895 + ");");
9896
9897 // --------------------------------------------------------------------------
9898 // Track whether this method was called. If it was, then the destructor will
9899 // avoid calling it again because the developer may need to control the way
9900 // the socket is shutdown before closing.
9901 // --------------------------------------------------------------------------
9902 if (track_shutdown_for_destructor) __socket_got_shutdown = true;
9903
9904 // --------------------------------------------------------------------------
9905 // Shut down TLS connections first, if TLS is enabled.
9906 //
9907 // If SSL_shutdown returns a 0, then it means a "bidirectional shutdown" is
9908 // needed which entails calling SSL_shutdown a second time. The error codes
9909 // returned by SSL_shutdown are as follows:
9910 //
9911 // 1 = Shutdown completed normally
9912 //
9913 // 0 = Shutdown is not yet finished, and a second SSL_shutdown() is needed
9914 //
9915 // -1 = Critical error, probably due to a connection problem, or because
9916 // the endpoint already initiated a shutdown, etc.
9917 //
9918 // We don't really care what the second SSL_shutdown's return code is because
9919 // there's nothing we can do about it whatever it is. This situation is rare
9920 // though, and what follows next closes the raw socket, which is OpenSSL's
9921 // lifeline to communicating with the endpoint.
9922 // --------------------------------------------------------------------------
9923 if (__tls && tls_shutdown) {
9924 if (SSL_shutdown(__tls_fd) == 0) {
9925 SSL_shutdown(__tls_fd);
9926 } // -x- if SSL_shutdown -x-
9927 } // -x- if __tls -x-
9928
9929 // --------------------------------------------------------------------------
9930 // Shut down the raw socket.
9931 // --------------------------------------------------------------------------
9932 __rc_check(::shutdown(__socket_fd, how));
9933 //__socket_connected = false; // TODO: Figure out when to change this to false
9934
9935 return *this;
9936 } // -x- rsocket& shutdown -x-
9937
9938 /*======================================================================*//**
9939 @brief
9940 Complete the configuration of an rsocket that was previously initialized
9941 without any parameters (a.k.a., an "empty rsocket").
9942 @copydetails rsocket()
9943 @throws randolf::rex::xEACCES Elevated access is needed to open this socket
9944 @throws randolf::rex::xEAFNOSUPPORT Address family not implemented/supported
9945 @throws randolf::rex::xEALREADY If this socket() method was already used, or
9946 it was used after rsocket() initialized with at least one parameter
9947 @throws randolf::rex::xEINVAL Protocal family invalid or not available
9948 @throws randolf::rex::xEINVAL Invalid flags in type
9949 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
9950 @throws randolf::rex::xENFILE System-wide maximum open files limit reached
9951 @throws randolf::rex::xENOBUFS Insufficient memory
9952 @throws randolf::rex::xENOMEM Insufficient memory
9953 @throws randolf::rex::xEPROTONOSUPPORT Specified `type` or `protocol` is not
9954 supported within the specified family (a.k.a., communication domain)
9955 @returns The same rsocket object so as to facilitate stacking
9956 @see get_socket_family()
9957 @see get_socket_fd()
9958 @see get_socket_protocol()
9959 @see get_socket_type()
9960 @see port()
9961 @see rsocket()
9962 @see socket_fd()
9963 @qualifier POSIX
9964 @qualifier TLS
9965 *///=========================================================================
9966 rsocket& socket(
9967 /// Communication domain; usually one of:@n
9968 /// AF_INET (IPv4)@n
9969 /// AF_INET6 (IPv6)@n
9970 /// AF_UNIX (UNIX domain sockets)
9971 const int family,
9972 /// Communication semantics; usually one of:@n
9973 /// SOCK_STREAM (common for TCP)@n
9974 /// SOCK_DGRAM (common for UDP)
9975 const int type = SOCK_STREAM,
9976 /// Network protocol; usually one of:@n
9977 /// IPPROTO_TCP / IPPROTO_MPTCP@n
9978 /// IPPROTO_UDP@n
9979 /// IPPROTO_IP@n
9980 /// PF_UNSPEC (auto-detect)
9981 const int protocol = PF_UNSPEC) {
9982 __socket(family, type, protocol);
9983 return *this;
9984 } // -x- rsocket& socket -x-
9985
9986 /*======================================================================*//**
9987 @brief
9988 Set underlying socket descriptor/handle (to one that is presumed to be open).
9989 @note
9990 This method is only available while an underlying socket has not been created
9991 or previously assigned, such as after an empty @ref rsocket instantiation.
9992 @throws randolf::rex::xEALREADY If this socket_fd() method was already used,
9993 or it was used after socket() initialized it, or if rsocket() had
9994 initialized with at least one parameter that resulted in the creation
9995 of an underlying socket
9996 @returns The same rsocket object so as to facilitate stacking
9997 @see socket()
9998 @see get_socket_family()
9999 @see get_socket_fd()
10000 @see get_socket_protocol()
10001 @see get_socket_type()
10002 @qualifier TLS
10003 *///=========================================================================
10004 rsocket& socket_fd(
10005 /// New socket descriptor/handle
10006 const int new_socket_fd) {
10007 if (__debug) debug("socket_fd(socket{0x" + randolf::rtools::to_hex(new_socket_fd) + "}"
10008 + ");");
10009 if (__socket_fd != 0) throw randolf::rex::xEALREADY("EALREADY");
10010 __socket_fd = new_socket_fd;
10011 __socket_addr->ss_family = getsockopt_int(SOL_SOCKET, SO_DOMAIN);
10012 __socket_type = getsockopt_int(SOL_SOCKET, SO_TYPE);
10013 __socket_protocol = getsockopt_int(SOL_SOCKET, SO_PROTOCOL);
10014 __socket_open = true;
10015 return *this;
10016 } // -x- rsocket& socket_fd -x-
10017
10018 /*======================================================================*//**
10019 @brief
10020 Find out whether the underlying socket is at the out-of-band (OOB) mark.
10021
10022 @throws randolf::rex::xEBADF The underlying socket is not open
10023 @throws randolf::rex::xEINVAL The underlying socket file descriptor is not a
10024 type to which @ref sockatmark() can be applied
10025
10026 @returns TRUE = at OOB mark
10027 @returns FALSE = not at OOB mark
10028 @qualifier POSIX
10029 @qualifier TLS
10030 *///=========================================================================
10031 bool sockatmark() {
10032 return __rc_check(::sockatmark(__socket_fd)) ? true : false;
10033 } // -x- bool sockatmark -x-
10034
10035 /*======================================================================*//**
10036 @brief
10037 Configure the receive (read) or send (write/transmit) timeout on the
10038 underlying socket.
10039
10040 Since setting the read timeout is such a common operation, this specialized
10041 method was created to ease software development efforts; internally we're
10042 just calling setsockopt(SOL_SOCKET, SO_RCVTIMEO, timeval).
10043 @attention
10044 Although a timeout of 100,000 microseconds (1/10 of one second) may suffice
10045 in healthy and efficient networks, a more conservative setting of 1 second
10046 tends to minimally yield more reliable results. Many end-user applications
10047 use defaults of 3 to 5 seconds to cover a wider variety of unpredictable
10048 network connections (such as over shared wireless connections that are slow),
10049 and this setting should ultimately be configurable by users/administrators.
10050
10051 @note
10052 The default timeout for new sockets is normally 0 (no timeout).
10053
10054 @throws randolf::rex::xEBADF The underlying socket is not open
10055 @throws randolf::rex::xEFAULT Address structure/memory is not in a writable
10056 part of the user address space
10057 @throws randolf::rex::xEINVAL Address length is incorrect, or address is not
10058 valid for this socket's family (a.k.a., communication domain)
10059 @throws randolf::rex::xENOPROTOOPT Either the level or the specified optname
10060 is not supported
10061 @throws randolf::rex::xENOTDIR If the @c direction parameter is supplied with
10062 an incorrect value (not @c SO_RCVTIMEO or @c SO_SNDTIMEO); this
10063 exception is normally a file-system related error, so we're using it
10064 here instead of EINVAL to make detecting this problem simpler for
10065 software developers (plus, "DIR" relates well to "direction"); this
10066 exception shouldn't need to be caught in the vast majority of uses,
10067 although one use where it should be caught is if the end-user has
10068 free reign to set the @c direction parameter to any value (e.g., a
10069 customizable software debugging interface)
10070 @throws randolf::rex::xENOTSOCK Underlying socket file descriptor (handle)
10071 doesn't refer to a socket
10072
10073 @returns The same rsocket object so as to facilitate stacking
10074 @see get_tieout
10075 @see recvline
10076 @see recv_rline
10077 @see timeout_recvline
10078 @qualifier TLS
10079 *///=========================================================================
10080 rsocket& timeout(
10081 /// timeval structure
10082 const struct timeval tv,
10083 /// Direction:@n
10084 /// @c SO_RCVTIMEO (default) @n
10085 /// @c SO_SNDTIMEO
10086 const int direction = SO_RCVTIMEO) {
10087 // TODO: Debug output
10088
10089 // --------------------------------------------------------------------------
10090 // Set timeout for the specified direction.
10091 // --------------------------------------------------------------------------
10092 if (direction == SO_RCVTIMEO || direction == SO_SNDTIMEO) {
10093// setsockopt(SOL_SOCKET, direction, tv);
10094 __rc_check(::setsockopt(__socket_fd, SOL_SOCKET, direction, &tv, sizeof(tv)));
10095 return *this;
10096 } // -x- if direction -x-
10097
10098 // --------------------------------------------------------------------------
10099 // Throw exception because direction is invalid.
10100 // --------------------------------------------------------------------------
10101 throw randolf::rex::xENOTDIR("invalid timeout direction (must be SO_RCVTIMEO or SO_SNDTIMEO)");
10102
10103 } // -x- rsocket& timeout -x-
10104
10105 /*======================================================================*//**
10106 @copydoc timeout(const struct timeval, const int)
10107 @see get_timeout
10108 @see recvline
10109 @see recv_rline
10110 @see timeout_recvline
10111 @qualifier TLS
10112 *///=========================================================================
10113 rsocket& timeout(
10114 /// Timeout in seconds
10115 const long tv_sec,
10116 /// Timeout in microseconds
10117 const long tv_usec = 0,
10118 /// Direction:@n
10119 /// @c SO_RCVTIMEO (default) @n
10120 /// @c SO_SNDTIMEO
10121 const int direction = SO_RCVTIMEO) {
10122 // TODO: Debug output
10123
10124 // --------------------------------------------------------------------------
10125 // Set timeout for the specified direction.
10126 // --------------------------------------------------------------------------
10127 if (direction == SO_RCVTIMEO || direction == SO_SNDTIMEO) {
10128 struct timeval tv{tv_sec, tv_usec};
10129// setsockopt(SOL_SOCKET, direction, tv);
10130 __rc_check(::setsockopt(__socket_fd, SOL_SOCKET, direction, &tv, sizeof(tv)));
10131 return *this;
10132 } // -x- if direction -x-
10133
10134 // --------------------------------------------------------------------------
10135 // Throw exception because direction is invalid.
10136 // --------------------------------------------------------------------------
10137 throw randolf::rex::xENOTDIR("invalid timeout direction (must be SO_RCVTIMEO or SO_SNDTIMEO)");
10138
10139 } // -x- rsocket& timeout -x-
10140
10141 /*======================================================================*//**
10142 @brief
10143 Set the read timeout for the @ref recvline() method (the @ref recvline()
10144 method's @c timeout parameter can override this setting).
10145
10146 @note
10147 The default timeout for this recvline_timeout setting is 0 (no timeout).
10148
10149 @throws randolf::rex::xERANGE if the timeout parameter is below 0
10150
10151 @returns The same rsocket object so as to facilitate stacking
10152 @see get_timeout_recvline
10153 @see recvline
10154 @see recv_rline
10155 @see timeout
10156 @see timeout_recvline
10157 @qualifier TLS
10158 *///=========================================================================
10159 rsocket& timeout_recvline(
10160 /// timeval structure
10161 const long timeout) {
10162 if (timeout < 0) throw randolf::rex::xERANGE("timeout parameter of " + std::to_string(timeout) + " is below 0");
10163 __recvline_timeout = timeout;
10164 return *this;
10165 } // -x- rsocket& timeout_recvline -x-
10166
10167 /*======================================================================*//**
10168 @brief
10169 Enable or disable encrypted communications (from the OpenSSL library).
10170
10171 @warning
10172 TLS cannot be enabled before a non-encrypted rsocket is open (an rsocket is
10173 typically opened with the @ref socket() method, the @ref connect() method, or
10174 one of the @ref accept() methods). If calling @c tls(true) on an rsocket
10175 that isn't open, an exception will be thrown.
10176
10177 If needed, a new TLS context will be instantiated and TLS will be initialized
10178 (if this hasn't already been done). TLS instantiation can be done first by
10179 calling the @ref tls_ctx() method (regardless of whether encryption is being
10180 enabled or disabled). If the default @ref TLS_FLAGS aren't sufficient for
10181 the needs of your application, then the @ref tls_ctx() method facilitates
10182 this regardless of wehther rsocket is open.
10183
10184 @note
10185 The reason a TLS context is instantiated and TLS is initialized even when the
10186 status is being set to FALSE is to facilitate TLS ingress from an unencrypted
10187 connection later in the session (see the @ref TLS_NO_INGRESS flag for more
10188 information).
10189
10190 @post
10191 If a client attempts to upgrade a TLS connection to TLS (e.g., by using a
10192 command such as @c STARTTLS, which is commonly transmitted in unencrypted
10193 form, like @c telnet-ssl does -- using `telnet-ssl -z ssl` prevents this
10194 condition), the following error that's difficult to track down may be
10195 triggered when calling any of the @c recv methods (I hope that including this
10196 information here in this documentation will be helpful):
10197 @verbatim
10198 SSL_ERROR_UNKNOWN: error:0A00010B:SSL routines::wrong version number
10199 @endverbatim
10200 This is most likely not a programming error, but rather a problem with how
10201 users may attempt to mis-use a connection based on a misunderstanding of the
10202 communications requirements (e.g., connecting unencrypted and attempting to
10203 upgrade to TLS over a connection that's expecting TLS encrypted data from the
10204 very beginning, without involving any ingress).
10205
10206 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10207 OpenSSL library doesn't document which errors may be returned)
10208
10209 @returns The same rsocket object so as to facilitate stacking
10210 @see is_tls
10211 @see tls_ctx
10212 @qualifier TLS
10213 *///=========================================================================
10214 rsocket& tls(
10215 /// TRUE = Enable encrypted communications@n FALSE = Disable encrypted communications
10216 const bool status = true,
10217 /// Configuration parameters
10218 const int flags = TLS_FLAGS::TLS_DEFAULT) { // | TLS_FLAGS::TLS_CLIENT
10219 if (__debug) debug("tls(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
10220 + ", " + (status ? "true" : "false")
10221 + ");");
10222
10223 // --------------------------------------------------------------------------
10224 // Create default context (with "flags" passthrough), unless it was already
10225 // created (usually by one of the tls_ctx() methods).
10226 // --------------------------------------------------------------------------
10227 if (__tls_ctx == nullptr) tls_ctx((SSL_CTX*)nullptr, flags);
10228
10229 // --------------------------------------------------------------------------
10230 // Make sure tls_fd (ssl_fd) is allocated. If not, then it needs to be
10231 // allocated and configured.
10232 // --------------------------------------------------------------------------
10233 if (status == true && __tls_fd == nullptr) {
10234 __tls_fd = SSL_new(__tls_ctx);
10235 if (__debug) debug("SSL_set_fd(<tls_fd>, socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
10236 + ");");
10237
10238 // --------------------------------------------------------------------------
10239 // Associate OpenSSL file descriptor with underlying socket file descriptor.
10240 // --------------------------------------------------------------------------
10241 __rc_check_tls(SSL_set_fd(__tls_fd, __socket_fd));
10242
10243 // --------------------------------------------------------------------------
10244 // Enable read-ahead so that SSL_peek will work more efficiently.
10245 // --------------------------------------------------------------------------
10246 if (!(flags & TLS_NO_READ_AHEAD)) {
10247 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
10248 SSL_set_read_ahead(__tls_fd, 1); // 0=disable / 1=enable
10249 } // -x- if !TLS_NO_READ_AHEAD -x-
10250
10251// SSL_CTX_set_max_pipelines(__tls_ctx, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
10252// SSL_set_max_pipelines(__tls_fd, SSL_MAX_PIPELINES); // SSL_MAX_PIPELINES = 32
10253
10254// SSL_CTX_set_mode(__tls_ctx, SSL_MODE_AUTO_RETRY);
10255// SSL_set_mode(__tls_fd, SSL_MODE_AUTO_RETRY);
10256
10257 } // -x- if !__tls_fd -x-
10258 __tls = status;
10259 return *this;
10260 } // -x- rsocket& tls -x-
10261
10262 /*======================================================================*//**
10263 @brief
10264 Return the current TLS context (multiple TLS contexts are supported, although
10265 typically needed to support SNI with inbound connections).
10266 @returns Pointer to OpenSSL's context (normally labelled @c SSL_CTX* in the
10267 documentation for OpenSSL), or nullptr if this context was never
10268 assigned to (or created by) this rsocket
10269 @see tls_ctx
10270 @qualifier TLS
10271 *///=========================================================================
10272 SSL_CTX* tls_ctx() noexcept {
10273 return __tls_ctx;
10274 } // -x- SSL_CTX* tls_ctx -x-
10275
10276 /*======================================================================*//**
10277 @brief
10278 Copy the source rsocket's TLS context map and add it to this rsocket's
10279 collection; or, if the source doesn't have any TLS contexts and this rsocket
10280 doesn't have any TLS contexts in its collection, then initialize TLS and
10281 instantiate a new TLS context. In either scenario, the source rsocket will
10282 be treated as a template as all TLS flags duplicated to enable encrypted
10283 socket I/O for use in this rsocket().
10284
10285 @note
10286 At least one TLS context is needed to enable encrypted socket I/O for use in
10287 this rsocket().
10288
10289 @post
10290 Encrypted socket I/O is only possible after a TLS context has been
10291 initialized (this is not a global setting as it has per-rsocket specificity).
10292
10293 @note
10294 The only @ref TLS_FLAGS flag that doesn't get transferred is @ref TLS_SERVER
10295 when no flags are specified. Specifying any flag(s) will cause this method
10296 to ignore the source rsocket's TLS flags so as to defer to this override.
10297
10298 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10299 OpenSSL library doesn't document which errors may be returned)
10300
10301 @returns The same rsocket object so as to facilitate stacking
10302 @see tls_sni
10303 @qualifier TLS
10304 *///=========================================================================
10305 rsocket& tls_ctx(
10306 /// An @c rsocket object from which to duplicate OpenSSL's TLS context, plus
10307 /// any @c TLS_FLAGS that were set differently from @c rsocket defaults
10308 rsocket& rtemplate,
10309 /// Configuration parameters
10310 const int flags = TLS_FLAGS::TLS_DEFAULT) {
10311 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
10312 + ", <rsocket>"
10313 + ", " + std::to_string(flags)
10314 + ");");
10315
10316 // --------------------------------------------------------------------------
10317 // TLS-related variables (OpenSSL).
10318 //
10319 // Note: The __tls_fd variable cannot be allocated until after __socket_fd
10320 // has been, hence the "post" note in the documentation.
10321 // --------------------------------------------------------------------------
10322 __tls = (bool)rtemplate.__tls; // Preserve TLS mode setting
10323 if (rtemplate.__tls_ctx != nullptr) { // If TLS context is defined, then do these important things:
10324 __tls_ctx = rtemplate.__tls_ctx; // 1. copy the pointer to SSL_CTX
10325 SSL_CTX_up_ref(__tls_ctx); // 2. increment SSL_CTX's internal reference count
10326 } // -x- if __tlx_ctx -x-
10327
10328 // --------------------------------------------------------------------------
10329 // Copy or override TLS flags.
10330 // --------------------------------------------------------------------------
10331 if (flags == TLS_FLAGS::TLS_DEFAULT) {
10332 __tls_exclusive = rtemplate.__tls_exclusive; // TLS policy
10333 __tls_egress = rtemplate.__tls_egress; // TLS policy
10334 __tls_ingress = rtemplate.__tls_ingress; // TLS policy
10335 __tls_no_read_ahead = rtemplate.__tls_ingress; // TLS policy
10336 } else { // Save flags
10337 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
10338 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
10339 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
10340 __tls_server_mode = flags & TLS_FLAGS::TLS_SERVER;
10341 __tls_no_read_ahead = flags & TLS_FLAGS::TLS_NO_READ_AHEAD;
10342 } // -x- if flags -x-
10343
10344 return *this;
10345 } // -x- rsocket& tls_ctx -x-
10346
10347 /*======================================================================*//**
10348 @brief
10349 Initialize TLS and instantiate a TLS context, and add it to this rsocket's
10350 current collection of TLS contexts, and set it as the currently active TLS
10351 context (so that a certificate chain and private key may be added to it).
10352 @note
10353 At least one TLS context is needed to enable encrypted socket I/O for use in
10354 this rsocket().
10355 @post
10356 Encrypted socket I/O is only possible after a TLS context has been
10357 initialized (this is not a global setting as it has per-rsocket specificity).
10358 @note
10359 This is the default TLS context for this @c rsocket, which will also be used
10360 for non-SNI handshakes.
10361
10362 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10363 OpenSSL library doesn't document which errors may be returned)
10364
10365 @returns The same rsocket object so as to facilitate stacking
10366 @see tls_sni
10367 @qualifier TLS
10368 *///=========================================================================
10369 rsocket& tls_ctx(
10370 /// OpenSSL's TLS context to use (if not provided because @c nullptr was
10371 /// specified, a new TLS context will be created automatically that uses
10372 /// OpenSSL's defaults, which should be fine for most scenarios)
10373 SSL_CTX* ctx,
10374 /// Configuration parameters
10375 const int flags = TLS_FLAGS::TLS_DEFAULT) {
10376 if (__debug) debug("tls_ctx(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
10377 + ", <ctx>"
10378 + ", " + std::to_string(flags)
10379 + ");");
10380
10381 // --------------------------------------------------------------------------
10382 // Ignore repeated calls to this method.
10383 // --------------------------------------------------------------------------
10384 if (__tls_ctx != nullptr) return *this;
10385
10386 // --------------------------------------------------------------------------
10387 // Fire up OpenSSL's algorithms and pre-load its error strings.
10388 //
10389 // These two functions have been deprecated since OpenSSL v1.1.0, so we don't
10390 // need to call them. If someone needs them, then they can always call them
10391 // in their own code.
10392 // --------------------------------------------------------------------------
10393 //OpenSSL_add_all_algorithms(); // Load and register cryptography algorithms
10394 //SSL_load_error_strings(); // Load all error messages into memory
10395
10396 // --------------------------------------------------------------------------
10397 // Save (ctx != nullptr) or create (ctx == nullptr) OpenSSL context.
10398 // --------------------------------------------------------------------------
10399 __tls_ctx = ctx == nullptr ? SSL_CTX_new(flags & TLS_FLAGS::TLS_SERVER ? TLS_server_method() : TLS_client_method()) // Create anew (default)
10400 : ctx; // Use OpenSSL context that was provided
10401 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);
10402
10403 // --------------------------------------------------------------------------
10404 // Enable read-ahead so that SSL_peek will work properly.
10405 // --------------------------------------------------------------------------
10406 if (!(flags & TLS_NO_READ_AHEAD)) {
10407 SSL_CTX_set_read_ahead(__tls_ctx, 1); // 0=disable / 1=enable
10408 } // -x- if !TLS_NO_READ_AHEAD -x-
10409
10410 // --------------------------------------------------------------------------
10411 // Save flags.
10412 // --------------------------------------------------------------------------
10413 __tls_exclusive = flags & TLS_FLAGS::TLS_EXCLUSIVE;
10414 __tls_egress = !(flags & TLS_FLAGS::TLS_NO_EGRESS);
10415 __tls_ingress = !(flags & TLS_FLAGS::TLS_NO_INGRESS);
10416 __tls_server_mode = flags & TLS_FLAGS::TLS_SERVER;
10417 __tls_no_read_ahead = flags & TLS_FLAGS::TLS_NO_READ_AHEAD;
10418
10419 return *this;
10420 } // -x- rsocket& tls_ctx -x-
10421
10422 /*======================================================================*//**
10423 @brief
10424 Check the private key it to ensure it's consistent with the corresponding TLS
10425 certificate chain.
10426
10427 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10428 OpenSSL library doesn't document which errors may be returned)
10429
10430 @returns The same rsocket object so as to facilitate stacking
10431 @see tls_ctx_use_privatekey_file
10432 @see tls_ctx_use_privatekey_pem
10433 @qualifier TLS
10434 *///=========================================================================
10435 rsocket& tls_ctx_check_privatekey() {
10436 if (__debug) debug("tls_ctx_check_privatekey();");
10437 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);
10438 return *this;
10439 } // -x- rsocket& tls_ctx_check_privatekey -x-
10440
10441 /*======================================================================*//**
10442 @brief
10443 Load a TLS certificate chain and private key in PEM format from text files
10444 and use them in the TLS context.
10445
10446 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10447 OpenSSL library doesn't document which errors may be returned)
10448
10449 @returns The same rsocket object so as to facilitate stacking
10450 @see tls_ctx_use_certificate_chain_and_privatekey_pems
10451 @see tls_ctx_use_certificate_chain_file
10452 @see tls_ctx_use_certificate_chain_pem
10453 @see tls_ctx_use_privatekey_file
10454 @see tls_ctx_use_privatekey_pem
10455 @see tls_ctx_check_privatekey
10456 @qualifier TLS
10457 *///=========================================================================
10458 rsocket& tls_ctx_use_certificate_chain_and_privatekey_files(
10459 /// Pointer to ASCIIZ path and filename to certificate chain file (@c nullptr
10460 /// will simply be ignored)
10461 const char* chain_file,
10462 /// Pointer to ASCIIZ path and filename to private key file (@c nullptr will
10463 /// simply be ignored)
10464 const char* key_file) {
10465 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
10466 + std::string(chain_file)
10467 + ", " + std::string( key_file)
10468 + ");");
10469 if (chain_file != nullptr) tls_ctx_use_certificate_chain_file(chain_file);
10470 if ( key_file != nullptr) tls_ctx_use_privatekey_file( key_file);
10471 return *this;
10472 } // -x- rsocket& tls_ctx_use_certificate_chain_and_privatekey_files -x-
10473
10474 /*======================================================================*//**
10475 @brief
10476 Load a TLS certificate chain and private key in PEM format from text files
10477 and use them in the TLS context.
10478
10479 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10480 OpenSSL library doesn't document which errors may be returned)
10481
10482 @returns The same rsocket object so as to facilitate stacking
10483 @see tls_ctx_use_certificate_chain_and_privatekey_pems
10484 @see tls_ctx_use_certificate_chain_file
10485 @see tls_ctx_use_certificate_chain_pem
10486 @see tls_ctx_use_privatekey_file
10487 @see tls_ctx_use_privatekey_pem
10488 @see tls_ctx_check_privatekey
10489 @qualifier TLS
10490 *///=========================================================================
10491 rsocket& tls_ctx_use_certificate_chain_and_privatekey_files(
10492 /// Pointer to ASCIIZ path and filename to certificate chain file (an empty
10493 /// string will simply be ignored)
10494 const std::string chain_file,
10495 /// Pointer to ASCIIZ path and filename to private key file (an empty string
10496 /// will simply be ignored)
10497 const std::string key_file) {
10498 if (__debug) debug("tls_ctx_use_certificate_chain_and_privatekey_files("
10499 + chain_file
10500 + ", " + key_file
10501 + ");");
10502 if (!chain_file.empty()) tls_ctx_use_certificate_chain_file(chain_file);
10503 if ( !key_file.empty()) tls_ctx_use_privatekey_file( key_file);
10504 return *this;
10505 } // -x- rsocket& tls_ctx_use_certificate_chain_and_privatekey_files -x-
10506
10507 /*======================================================================*//**
10508 @brief
10509 Load a TLS certificate chain and a TLS private key in PEM format from memory
10510 and use them in the TLS context.
10511
10512 Although this functionality doesn't exist in OpenSSL (at the time of writing
10513 this method), it's provided here in a manner that has exactly the same effect
10514 as the @ref tls_ctx_use_certificate_chain_and_privatekey_files() methods, but
10515 without needing the PEM-formatted certificate chain stored in files
10516 beforehand.
10517
10518 @note
10519 The @c cert_pem_data and key_pem_data parameters are pointers to the memory
10520 locations that holds the PEM formatted certificate chain data and private key
10521 data, respectively. If the corresponding lengths of each of these data aren't
10522 specified or are set to zero (default), then they will be treated as multiline
10523 ASCIIZ strings.
10524
10525 Behind the scenes, we're just writing the cert_pem_data and key_pem_data
10526 memory to temporary files with severely-limited permissions (), then
10527 optionally overwriting those temporary files with random data prior to
10528 deleting them (this is the default, since better security practices should be
10529 the default, but on a secured system it may not be necessary and so this
10530 option can also be disabled to save CPU cycles and reduce overall disk-write
10531 I/O operations).
10532
10533 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10534 OpenSSL library doesn't document which errors may be returned)
10535
10536 @returns The same rsocket object so as to facilitate stacking
10537 @see tls_ctx_use_certificate_chain_and_privatekey_files
10538 @see tls_ctx_use_certificate_chain_file
10539 @see tls_ctx_use_certificate_chain_pem
10540 @see tls_ctx_use_privatekey_file
10541 @see tls_ctx_use_privatekey_pem
10542 @see tls_ctx_check_privatekey
10543 @qualifier TLS
10544 *///=========================================================================
10545 rsocket& tls_ctx_use_certificate_chain_and_privatekey_pems(
10546 /// Pointer to certificate chain data in PEM format
10547 const char* cert_pem_data,
10548 /// Pointer to private key data in PEM format
10549 const char* key_pem_data,
10550 /// Length of cert_pem_data (in bytes), or 0 to auto-detect length if cert_pem_data is an ASCIIZ string
10551 size_t cert_len = 0,
10552 /// Length of key_pem_data (in bytes), or 0 to auto-detect length if key_pem_data is an ASCIIZ string
10553 size_t key_len = 0,
10554 /// Whether to overwrite the temporary files with random data before deleting them
10555 const bool random_fill = true) {
10556 tls_ctx_use_certificate_chain_pem(cert_pem_data, cert_len, random_fill);
10557 tls_ctx_use_privatekey_pem( key_pem_data, key_len, random_fill);
10558 return *this;
10559 } // -x- rsocket& tls_ctx_use_certificate_chain_and_privatekey_pems -x-
10560
10561 /*======================================================================*//**
10562 @brief
10563 Load a TLS certificate chain in PEM format from a text file and use it in the
10564 TLS context.
10565
10566 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10567 OpenSSL library doesn't document which errors may be returned)
10568
10569 @returns The same rsocket object so as to facilitate stacking
10570 @see tls_ctx_use_certificate_chain_file
10571 @see tls_ctx_use_certificate_chain_pem
10572 @see tls_ctx_check_privatekey
10573 @qualifier TLS
10574 *///=========================================================================
10575 rsocket& tls_ctx_use_certificate_chain_file(
10576 /// Pointer to ASCIIZ path and filename to certificate chain file
10577 const char* file) {
10578 if (__debug) debug("tls_ctx_use_certificate_chain_file("
10579 + std::string(file)
10580 + ");");
10581 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);
10582 return *this;
10583 } // -x- rsocket& tls_ctx_use_certificate_chain_file -x-
10584
10585 /*======================================================================*//**
10586 @brief
10587 Load a TLS certificate chain in PEM format from a text file and use it in the
10588 TLS context.
10589
10590 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10591 OpenSSL library doesn't document which errors may be returned)
10592
10593 @returns The same rsocket object so as to facilitate stacking
10594 @see tls_ctx_use_certificate_chain_file
10595 @see tls_ctx_use_certificate_chain_pem
10596 @see tls_ctx_check_privatekey
10597 @qualifier TLS
10598 *///=========================================================================
10599 rsocket& tls_ctx_use_certificate_chain_file(
10600 /// Path and filename to certificate chain file
10601 const std::string& file) {
10602 if (__debug) debug("tls_ctx_use_certificate_chain_file("
10603 + file
10604 + ");");
10605 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);
10606 return *this;
10607 } // -x- rsocket& tls_ctx_use_certificate_chain_file -x-
10608
10609 /*======================================================================*//**
10610 @brief
10611 Load a TLS certificate chain in PEM format from memory and use it in the TLS
10612 context.
10613
10614 Although this functionality doesn't exist in OpenSSL (at the time of writing
10615 this method), it's provided here in a manner that has exactly the same effect
10616 as the @ref tls_ctx_use_certificate_chain_file() methods, but without needing
10617 the PEM-formatted certificate chain stored in a file beforehand.
10618
10619 @note
10620 The @c pem_data parameter is a pointer to the memory location that holds
10621 the PEM formatted certificate chain data. If the length of this data isn't
10622 specified or is set to zero (default), then it will be treated as a multiline
10623 ASCIIZ string.
10624
10625 Behind the scenes, we're just writing the pem_data memory to a temporary
10626 file with severely-limited permissions (), then optionally overwriting that
10627 temporary file with random data prior to deleting it (this is the default,
10628 since better security practices should be the default, but on a secured
10629 system it may not be necessary and so this option can also be disabled to
10630 save CPU cycles and reduce overall disk-write I/O operations).
10631
10632 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10633 OpenSSL library doesn't document which errors may be returned)
10634
10635 @returns The same rsocket object so as to facilitate stacking
10636 @see tls_ctx_use_certificate_chain_file
10637 @see tls_ctx_check_privatekey
10638 @qualifier TLS
10639 *///=========================================================================
10640 rsocket& tls_ctx_use_certificate_chain_pem(
10641 /// Pointer to certificate chain data in PEM format
10642 const char* pem_data,
10643 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
10644 size_t len = 0,
10645 /// Whether to overwrite the temporary file with random data before deleting it
10646 const bool random_fill = true) {
10647 if (__debug) debug("tls_ctx_use_certificate_chain_pem(<buf>, " + std::to_string(len)
10648 + ", " + (random_fill ? "true" : "false")
10649 + ");");
10650
10651 // --------------------------------------------------------------------------
10652 // Measure size of certificate chain if an ASCIIZ string was indicated.
10653 // --------------------------------------------------------------------------
10654 if (len == 0) len = std::strlen(pem_data);
10655
10656 // --------------------------------------------------------------------------
10657 // Generate filename for temporary use.
10658 // --------------------------------------------------------------------------
10659 std::string file = std::filesystem::temp_directory_path();
10660 file.append("/rsocket.")
10661 .append(std::to_string(::getpid()))
10662 .append(".")
10663 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
10664
10665 // --------------------------------------------------------------------------
10666 // Open temporary file.
10667 // --------------------------------------------------------------------------
10668 FILE* fp = fopen(file.data(), "w+");
10669 if (fp == nullptr) randolf::rex::mk_exception("Cannot open temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
10670 { int attr;
10671 ioctl(fileno(fp), FS_IOC_GETFLAGS, &attr);
10672 attr |= FS_NOATIME_FL // Don't update access time attribute
10673 | FS_NODUMP_FL // Don't include in filesystem backup dumps
10674 | FS_SECRM_FL; // Mark file for secure deletion (where supported)
10675 ioctl(fileno(fp), FS_IOC_SETFLAGS, &attr);
10676 }
10677 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
10678 if (fputs(pem_data, fp) == EOF) {
10679 fclose(fp);
10680 randolf::rex::mk_exception("Cannot write to temporary file (to use certificate chain) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
10681 } // -x- if !fputs -x-
10682 fflush(fp);
10683
10684 // --------------------------------------------------------------------------
10685 // Attempt to load certificate chain file, but save the error code for later
10686 // because we need to clean up the temporary file before possibly throwing an
10687 // exception.
10688 // --------------------------------------------------------------------------
10689 int rc = SSL_CTX_use_certificate_chain_file(__tls_ctx, file.data());
10690
10691 // --------------------------------------------------------------------------
10692 // Overwrite the contenst of the temporary file before deleting it so as to
10693 // sabotage a simple attempt to undelete the file and access the certificate.
10694 //
10695 // We're also re-using the "len" local variable because it's not needed once
10696 // we get the loop started, and it's more optimal to not allocate yet another
10697 // local variable while "len" goes to waste. :D
10698 // --------------------------------------------------------------------------
10699 if (random_fill) { // This option is configurable
10700 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
10701 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
10702 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
10703 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
10704 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
10705 } // -x- for i -x-
10706 } // -x- if randfill -x-
10707 fchmod(fileno(fp), 0); // Remove all permissions
10708 fclose(fp); // Close file handle
10709 unlink(file.data()); // Delete temporary file
10710
10711 // --------------------------------------------------------------------------
10712 // Error check ... was delayed here until after temporary file cleanup.
10713 // --------------------------------------------------------------------------
10714 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);
10715
10716 return *this;
10717 } // -x- rsocket& tls_ctx_use_certificate_chain_pem -x-
10718
10719 /*======================================================================*//**
10720 @brief
10721 Load a TLS private key in PEM format from a text file and use it in the TLS
10722 context.
10723
10724 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10725 OpenSSL library doesn't document which errors may be returned)
10726
10727 @returns The same rsocket object so as to facilitate stacking
10728 @see tls_ctx_use_privatekey_file
10729 @see tls_ctx_use_privatekey_pem
10730 @see tls_ctx_check_privatekey
10731 @qualifier TLS
10732 *///=========================================================================
10733 rsocket& tls_ctx_use_privatekey_file(
10734 /// Pointer to ASCIIZ path-and-filename of private key file
10735 const char* file) {
10736 if (__debug) debug("tls_ctx_use_privatekey_file("
10737 + std::string(file)
10738 + ");");
10739 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);
10740
10741 return *this;
10742 } // -x- rsocket& tls_ctx_use_privatekey_file -x-
10743
10744 /*======================================================================*//**
10745 @brief
10746 Load a TLS private key in PEM format from a text file and use it in the TLS
10747 context.
10748
10749 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10750 OpenSSL library doesn't document which errors may be returned)
10751
10752 @returns The same rsocket object so as to facilitate stacking
10753 @see tls_ctx_use_privatekey_file
10754 @see tls_ctx_use_privatekey_pem
10755 @see tls_ctx_check_privatekey
10756 @qualifier TLS
10757 *///=========================================================================
10758 rsocket& tls_ctx_use_privatekey_file(
10759 /// Path and filename to private key file
10760 const std::string& file) {
10761 if (__debug) debug("tls_ctx_use_privatekey_file("
10762 + file
10763 + ");");
10764 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);
10765
10766 return *this;
10767 } // -x- rsocket& tls_ctx_use_privatekey_file -x-
10768
10769 /*======================================================================*//**
10770 @brief
10771 Load a TLS private key in PEM format from memory and use it in the TLS
10772 context.
10773
10774 Although this functionality doesn't exist in OpenSSL (at the time of writing
10775 this method), it's provided here in a manner that has exactly the same effect
10776 as the @ref tls_ctx_use_privatekey_file() methods, but without needing the
10777 PEM-formatted private key stored in a file beforehand.
10778
10779 @note
10780 The @c pem_data parameter is a pointer to the memory location that holds the
10781 PEM formatted private key data. If the length of this data isn't specified
10782 or is set to zero (default), then it will be treated as a multiline ASCIIZ
10783 string.
10784
10785 Behind the scenes, we're just writing the pem_data memory to a temporary
10786 file (with severely-limited permissions), then optionally overwriting that
10787 temporary file with random data prior to deleting it (this is the default,
10788 since better security practices should be the default, but on a secured
10789 system it may not be necessary and so this option can also be disabled to
10790 save CPU cycles and reduce overall disk-write I/O operations).
10791
10792 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10793 OpenSSL library doesn't document which errors may be returned)
10794
10795 @returns The same rsocket object so as to facilitate stacking
10796 @see tls_ctx_use_privatekey_file
10797 @see tls_ctx_check_privatekey
10798 @qualifier TLS
10799 *///=========================================================================
10800 rsocket& tls_ctx_use_privatekey_pem(
10801 /// Pointer to private key data in PEM format
10802 const char* pem_data,
10803 /// Length of pem_data (in bytes), or 0 to auto-detect length if pem_data is an ASCIIZ string
10804 size_t len = 0,
10805 /// Whether to overwrite the temporary file with random data before deleting it
10806 const bool random_fill = true) {
10807 if (__debug) debug("tls_ctx_use_privatekey_pem(<buf>, " + std::to_string(len)
10808 + ", " + (random_fill ? "true" : "false")
10809 + ");");
10810
10811 // --------------------------------------------------------------------------
10812 // Measure size of private key if an ASCIIZ string was indicated.
10813 // --------------------------------------------------------------------------
10814 if (len == 0) len = std::strlen(pem_data);
10815
10816 // --------------------------------------------------------------------------
10817 // Generate filename for temporary use.
10818 // --------------------------------------------------------------------------
10819 std::string file = std::filesystem::temp_directory_path();
10820 file.append("/rsocket.")
10821 .append(std::to_string(::getpid()))
10822 .append(".")
10823 .append(std::to_string(__fn_counter.fetch_add(1, std::memory_order_relaxed)));
10824
10825 // --------------------------------------------------------------------------
10826 // Open temporary file.
10827 // --------------------------------------------------------------------------
10828 FILE* fp = fopen(file.data(), "w+");
10829 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);
10830 fchmod(fileno(fp), S_IRUSR | S_IWUSR);
10831 if (fputs(pem_data, fp) == EOF) {
10832 fclose(fp);
10833 randolf::rex::mk_exception("Cannot cannot write to temporary file (to use private key) " + file, 0, rex::rex::REX_FLAGS::REX_FIND_ERRNO);
10834 } // -x- if !fputs -x-
10835 fflush(fp);
10836
10837 // --------------------------------------------------------------------------
10838 // Attempt to load private key file, but save the error code for later
10839 // because we need to clean up the temporary file before possibly throwing an
10840 // exception.
10841 // --------------------------------------------------------------------------
10842 int rc = SSL_CTX_use_PrivateKey_file(__tls_ctx, file.data(), SSL_FILETYPE_PEM);
10843
10844 // --------------------------------------------------------------------------
10845 // Overwrite the contenst of the temporary file before deleting it so as to
10846 // sabotage a simple attempt to undelete the file and access the certificate.
10847 //
10848 // We're also re-using the "len" local variable because it's not needed once
10849 // we get the loop started, and it's more optimal to not allocate yet another
10850 // local variable while "len" goes to waste. :D
10851 // --------------------------------------------------------------------------
10852 if (random_fill) { // This option is configurable
10853 fseek(fp, 0, SEEK_SET); // File file position pointer to the beginning
10854 ::srand(::time(0) + __fn_counter); // Current time + atomic __fn_counter value helps add variety
10855 for (int i = len / sizeof(int) + (::rand() & 127); i >= 0; i--) { // Fill contents of file with random data
10856 len = ::rand() + (len >> (sizeof(int) >> 2)); // Bias random data toward containing more set bits (more 1's)
10857 fwrite(&len, sizeof(int), 1, fp); // Write one integer at a time
10858 } // -x- for i -x-
10859 } // -x- if randfill -x-
10860 fchmod(fileno(fp), 0); // Remove all permissions
10861 fclose(fp); // Close file handle
10862 unlink(file.data()); // Delete temporary file
10863
10864 // --------------------------------------------------------------------------
10865 // Error check ... was delayed here until after temporary file cleanup.
10866 // --------------------------------------------------------------------------
10867 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);
10868
10869 return *this;
10870 } // -x- rsocket& tls_ctx_use_privatekey_pem -x-
10871
10872 /*======================================================================*//**
10873 @brief
10874 Initiate the TLS handshake with the endpoint (which is presumed to be a
10875 server).
10876 This method makes it easier to support application-level commands such as @c
10877 STARTTLS (which are implemented in protocols like SMTP, POP3, IMAP4, MEOW,
10878 FTP, NNTP, LDAP, XMPP, etc.).
10879
10880 @throws randolf::rex::xALL Catch this exception for now (at this time, the
10881 OpenSSL library doesn't document which errors may be returned)
10882
10883 @returns The same rsocket object so as to facilitate stacking
10884 @see connect()
10885 @see connect(std::string, int)
10886 @see TLS_NO_INGRESS
10887 @qualifier TLS
10888 *///=========================================================================
10889 rsocket& tls_do_handshake() {
10890 if (__debug) debug("tls_handshake(socket{0x" + randolf::rtools::to_hex(__socket_fd) + "}"
10891 + ");");
10892 __rc_check_tls(SSL_do_handshake(__tls_fd));
10893 return *this;
10894 } // -x- rsocket& tls_do_handshake -x-
10895
10896 /*======================================================================*//**
10897 @brief
10898 Get OpenSSL's TLS structure.
10899 @returns TLS structure
10900 @returns nullptr = TLS structure not yet allocated
10901 @qualifier TLS
10902 *///=========================================================================
10903 const SSL* tls_fd() noexcept {
10904 return __tls_fd;
10905 } // -x- int tls_fd -x-
10906
10907 /*======================================================================*//**
10908 @brief
10909 Return the current @ref rsocket_sni object that this @c rsocket will use when
10910 accepting incoming encrypted connections.
10911 @returns Pointer to @c rsocket_sni object
10912 @returns nullptr = SNI is not assigned
10913 @see name_sni
10914 @see tls_sni(rsocket_sni*)
10915 @qualifier TLS
10916 *///=========================================================================
10917 rsocket_sni* tls_sni() noexcept {
10918 return __tls_sni;
10919 } // -x- rsocket_sni* tls_sni -x-
10920
10921 /*======================================================================*//**
10922 @brief
10923 Set the current @ref rsocket_sni object that this @c rsocket will use when
10924 accepting incoming encrypted connections.
10925
10926 Use the @ref name() method to find out which server name was supplied by the
10927 endpoint that triggered the SNI callback, regardless of whether it matches
10928 any of the TLS certificates used with this rsocket object or the rsocket_sni
10929 object that's associated with this rsocket object. If an SNI callback wasn't
10930 triggered, or if the endpoint didn't provide a server name, then it will
10931 remain unaffected (and the default {empty string} will remain unchanged).
10932 @returns The same rsocket object so as to facilitate stacking
10933 @see name_sni
10934 @see tls_sni
10935 @see is_tls_sni_match
10936 @qualifier TLS
10937 *///=========================================================================
10938 rsocket& tls_sni(
10939 /// Pointer to the @ref rsocket_sni object to use, or specify @c nullptr to
10940 /// remove SNI support from this rsocket object
10941 rsocket_sni* sni) noexcept {
10942
10943 // --------------------------------------------------------------------------
10944 // Remove SNI support.
10945 // --------------------------------------------------------------------------
10946 if (sni == nullptr) {
10947
10948 // --------------------------------------------------------------------------
10949 // Only remove SNI support if it's configured.
10950 // --------------------------------------------------------------------------
10951 if (__tls_sni != nullptr) SSL_CTX_set_client_hello_cb(__tls_ctx, nullptr, this);
10952
10953 // --------------------------------------------------------------------------
10954 // Add or set SNI support.
10955 // --------------------------------------------------------------------------
10956 } else {
10957
10958 // --------------------------------------------------------------------------
10959 // Configure SNI callbacks for TLS. This callback will occur in the early
10960 // stages, which is triggered by the SSL_accept() method.
10961 // --------------------------------------------------------------------------
10962 SSL_CTX_set_client_hello_cb(__tls_ctx, tls_sni_callback, this);
10963
10964 } // -x- if !sni -x-
10965
10966 // --------------------------------------------------------------------------
10967 // Update internal pointer to the SNI map, or nullptr if it's being removed.
10968 // --------------------------------------------------------------------------
10969 __tls_sni = sni; // TODO: Do we need to free/delete the old __tls_sni as memory leak prevention?
10970
10971 return *this;
10972 } // -x- rsocket_sni& tls_sni -x-
10973
10974private:
10975 /*======================================================================*//**
10976 @brief
10977 Get OpenSSL's TLS structure.
10978 @returns TLS structure
10979 @returns nullptr = TLS structure not yet allocated
10980 @qualifier TLS
10981 *///=========================================================================
10982 int static tls_sni_callback(
10983 /// OpenSSL's socket descriptor/handle
10984 SSL* tls_fd,
10985 /// Where to store the @c alert value
10986 int* al,
10987 /// Context-specific argument
10988 void* arg) noexcept {
10989
10990 // --------------------------------------------------------------------------
10991 // Internal variables.
10992 // --------------------------------------------------------------------------
10993 rsocket* r = (rsocket*)arg; // We may be updating the TLS context (this is normally the new client's rsocket object)
10994 const unsigned char* out = nullptr;
10995 size_t out_size = 0;
10996
10997 // --------------------------------------------------------------------------
10998 // Obtain a pointer to newly-allocated ClientHello fields data. If *out is
10999 // nullptr, it means that no TLSEXT_TYPE_server_name was received from the
11000 // client, and so the default TLS context will suffice. If out_size is less
11001 // than or equal to 2, then it also won't have what we need.
11002 // --------------------------------------------------------------------------
11003 if (!SSL_client_hello_get0_ext(tls_fd, TLSEXT_TYPE_server_name, &out, &out_size)
11004 || out == nullptr
11005 || out_size <= 2) {
11006 //std::cout << "no server_name / out_size=" << out_size << std::endl;
11007 r->__tls_sni_has_name = false; // No SNI name
11008 r->__tls_sni_match = false; // No SNI match
11009 return SSL_CLIENT_HELLO_SUCCESS; // 1
11010 }
11011// if (!SSL_client_hello_get0_ext(tls_fd, TLSEXT_TYPE_server_name, &out, &out_size)
11012// || out == nullptr || out_size <= 2) return SSL_CLIENT_HELLO_SUCCESS; // 1
11013
11014 // --------------------------------------------------------------------------
11015 // Scan for TLSEXT_NAMETYPE_host_name, but if we can't find it then we'll
11016 // just leave the default TLS context as is.
11017 // --------------------------------------------------------------------------
11018 unsigned char* pos = (unsigned char*)out;
11019 size_t len = (*(pos++) << 8); // Extract first MSB
11020 len += *(pos++); // Add first LSB
11021//std::cout << "scan for host_name / out_size=" << out_size << " len=" << len << std::endl;
11022 if (len + 2 != out_size) return SSL_CLIENT_HELLO_SUCCESS; // 1 // goto finish;
11023 out_size = len;
11024
11025 // --------------------------------------------------------------------------
11026 // We're taking a shortcut by examining only the first element in the list,
11027 // but in the future we need to make this more robust in case other types of
11028 // elements precede what we're looking for.
11029 //
11030 // Unfortunately, there's no documentation that properly-explains the format
11031 // of the list, so some deeper research into OpenSSL's source code will be
11032 // needed (a cursory look so far has not yielded the necessary insight).
11033 //
11034 // TODO: Turn this into a loop that supports future clients that provide
11035 // multiple SNI server names in their requests. (Although this isn't
11036 // occuring at present with common end-user tools such as web browsers
11037 // and eMail software, it may happen in the future as client/server
11038 // software becomes more sophisticated to support users, some of whom
11039 // are gradually becoming more savvy.)
11040 // --------------------------------------------------------------------------
11041 if (out_size <= 3 || *pos++ != TLSEXT_NAMETYPE_host_name) return SSL_CLIENT_HELLO_SUCCESS; // 1 // goto finish;
11042 out_size--; // Avoid a buffer overrun caused by corrupt or prematurely-truncated data
11043
11044//std::cout << "out_size=" << out_size << std::endl;
11045
11046 // --------------------------------------------------------------------------
11047 // Extract and use the hostname (SNI server name) that was supplied by the
11048 // endpoint so that the correct TLS certificate can be selected and assigned.
11049 // --------------------------------------------------------------------------
11050 len = (*(pos++) << 8); // Extract second MSB
11051 len += *(pos++); // Add second LSB
11052 if (!(len + 2 > out_size)) { // Only process SNI server name string if it's at least 2 bytes long
11053 // Debug: std::cout << "Server name: " << (const char*)p << std::endl; // Debug
11054
11055//std::cout << "out_size=" << out_size << " len=" << len << std::endl;
11056
11057 // --------------------------------------------------------------------------
11058 // Obtain the correct TLS context (wildcards supported) that is associated
11059 // with the hostname (SNI server name) that was supplied by the endpoint.
11060 // --------------------------------------------------------------------------
11061 const char* sni_name = (const char*)pos;
11062 r->__name_sni.assign(sni_name, len);
11063 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
11064 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
11065 r->__tls_sni_match = new_ctx != nullptr; // Track whether sni_name matches any of the certificates that are stored in rsocket_sni
11066
11067 // --------------------------------------------------------------------------
11068 // Change TLS context so that encryption with the endpoint uses the correct
11069 // TLS certificate (otherwise the endpoint will indicate security risk errors
11070 // to end users, log files, etc., and may {should} reject the connection).
11071 // --------------------------------------------------------------------------
11072 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
11073 r->tls_ctx(new_ctx); // If nullptr, then tls_ctx() will get OpenSSL to create a new SSL_CTX
11074
11075 } // -x- if len -x-
11076
11077 finish:
11078 // --------------------------------------------------------------------------
11079 // Free the ClientHello fields data, then return SSL_CLIENT_HELLO_SUCCESS.
11080 // Even if we encountered a problem with the ClientHello fields data, we
11081 // still return SSL_CLIENT_HELLO_SUCCESS so that the TLS context will be
11082 // accepted as valid.
11083 //
11084 // If we return SSL_CLIENT_HELLO_ERROR the connection will fail unnecessarily
11085 // for valid TLS certificates. If the ClientHello fields data is malformed
11086 // to a degree that doesn't satisfy OpenSSL, then OpenSSL will reject it, so
11087 // there's really no point in duplicating what OpenSSL already does properly,
11088 // which OpenSSL will pass through the standard error channels with normal
11089 // error details-and-diagnostics anyway.
11090 //
11091 // OpenSSL documentation says to do the following...
11092 // OPENSSL_free((char*)out);
11093 // ...but it's not necessary here because we'd be freeing a local variable,
11094 // which will fail and cause a crash.
11095 // --------------------------------------------------------------------------
11096 //std::cout << "out=" << randolf::rtools::to_hex(out[0], 8) << std::endl; // Debug
11097 //OPENSSL_free((unsigned char*)out);
11098
11099 return SSL_CLIENT_HELLO_SUCCESS; // 1
11100 } // -x- int tls_sni_callback -x-
11101
11102public:
11103 /*======================================================================*//**
11104 @brief
11105 Convert a 48-bit (6-byte) integer to a machine address in the form of @c
11106 xx:xx:xx:xx:xx:xx where every instance of @c xx is a hexadecimal
11107 representation of each respective 8-bit byte portion.
11108
11109 This method is needed because we don't want to bring in the heavy fmt::format
11110 class as a dependency.
11111 @returns Mac address as 17-character in the typical format expected by system
11112 administrators
11113 @see to_node
11114 @qualifier TLS
11115 *///=========================================================================
11116 static std::unique_ptr<char[]> to_mac(
11117 /// Pointer to 48-bit integer
11118 const void* addr) noexcept {
11119 std::unique_ptr<char[]> h = std::make_unique_for_overwrite<char[]>(18); // 48-bit mac address needs 6 hexadecimal pairs of nybbles delimited by colons plus a NULL terminator
11120 snprintf(h.get(),
11121 18,
11122 "%02x:%02x:%02x:%02x:%02x:%02x",
11123 ((u_char*)addr)[0],
11124 ((u_char*)addr)[1],
11125 ((u_char*)addr)[2],
11126 ((u_char*)addr)[3],
11127 ((u_char*)addr)[4],
11128 ((u_char*)addr)[5]); // Convert, and truncate NULL terminator
11129 return h;
11130 } // -x- std::unique_ptr<char[]> to_mac -x-
11131
11132 /*======================================================================*//**
11133 @brief
11134 Convert a 48-bit (6 byte) integer to a node address in the form of @c
11135 xxxx:xxxx:xxxx where every instance of @c xxxx is a hexadecimal
11136 representation of each respective 16-bit word portion.
11137
11138 This method is needed because we don't want to bring in the heavy fmt::format
11139 class as a dependency.
11140 @returns Node address as 14-character in the typical format expected by
11141 network administrators
11142 @see to_mac
11143 @qualifier TLS
11144 *///=========================================================================
11145 static std::unique_ptr<char[]> to_node(
11146 /// Pointer to 48-bit integer
11147 const void* addr) noexcept {
11148 std::unique_ptr<char[]> h = std::make_unique_for_overwrite<char[]>(15); // 48-bit node address needs 3 hexadecimal sets of words delimited by colons plus a NULL terminator
11149 snprintf(h.get(),
11150 15,
11151 "%04x:%04x:%04x",
11152 ((u_int16_t*)addr)[0],
11153 ((u_int16_t*)addr)[1],
11154 ((u_int16_t*)addr)[2]); // Convert, and truncate NULL terminator
11155 return h;
11156 } // -x- std::unique_ptr<char[]> to_node -x-
11157
11158 /*======================================================================*//**
11159 @brief
11160 Send a formatted string to the @ref rsocket endpoint.
11161
11162 The @c format is described in the documentation for the POSIX or Standard C
11163 Library @c printf() function.
11164 @throws randolf::rex::xEBADF The underlying socket is not open
11165 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
11166 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
11167 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
11168 @throws randolf::rex::xENOMEM Insufficient memory
11169 @returns The same rsocket object so as to facilitate stacking
11170 @see eol_fix_printf
11171 @see is_eol_fix_printf
11172 @see net_io
11173 @see printf
11174 @see printfline
11175 @see vprintfline
11176 @qualifier POSIX
11177 @qualifier TLS
11178 *///=========================================================================
11179 rsocket& vprintf(
11180 /// Format string to use
11181 const char* format,
11182 /// Variadic arguments in @c va_list format
11183 va_list args) {
11184 char* buf;
11185 int rc = ::vasprintf(&buf, format, args);
11186 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
11187 if (__eol_fix_printf && !__eol.empty()) { // We need to edit the string and then send it
11188 std::string str = std::regex_replace(buf, std::regex("\n"), __eol);
11189 ::free(buf);
11190 __send(str.data(), str.length());
11191 } else { // No need to edit the string, so just send it as is
11192 try {
11193 __send(buf, rc);
11194 ::free(buf);
11195 } catch (std::exception& e) { // Free buf then re-throw the exception
11196 ::free(buf); // Prevent memory leak when an exception is thrown
11197 throw;
11198 }
11199 } // -x- if __eol_fix_printf -x-
11200 return *this;
11201 } // -x- rsocket& vprintf -x-
11202
11203 /*======================================================================*//**
11204 @brief
11205 Send a formatted string to the @ref rsocket endpoint, and append an EoL
11206 sequence.
11207
11208 The @c format is described in the documentation for the POSIX or Standard C
11209 Library @c printf() function.
11210 @throws randolf::rex::xEBADF The underlying socket is not open
11211 @throws randolf::rex::xEILSEQ An invalid wide-character code was detected
11212 @throws randolf::rex::xEOVERFLOW The value returned is greater than {INT_MAX}
11213 @throws randolf::rex::xEMFILE Per-process maximum open files limit reached
11214 @throws randolf::rex::xENOMEM Insufficient memory
11215 @returns The same rsocket object so as to facilitate stacking
11216 @see eol
11217 @see eol_fix_printf
11218 @see is_eol_fix_printf
11219 @see net_io
11220 @see printf
11221 @see printfline
11222 @see sendline
11223 @see vprintf
11224 @qualifier TLS
11225 *///=========================================================================
11226 rsocket& vprintfline(
11227 /// Format string to use
11228 const char* format,
11229 /// Variadic arguments in @c va_list format
11230 va_list args) {
11231 char* buf;
11232 int rc = ::vasprintf(&buf, format, args);
11233 __rc_check(rc); // Check for error, and throw exception (buf was not allocated, so does not need to be freed)
11234 if (__eol_fix_printf && !__eol.empty()) {
11235 std::string str = std::regex_replace(buf, std::regex("\n"), __eol)
11236 .append(__eol);
11237 ::free(buf);
11238 __send(str.data(), str.length());
11239 } else {
11240 try {
11241 __sendline(buf, rc);
11242 ::free(buf);
11243 } catch (std::exception& e) { // Free buf then re-throw the exception
11244 ::free(buf); // Prevent memory leak when an exception is thrown
11245 throw;
11246 }
11247 } // -x- if __eol_fix_printf -x-
11248 return *this;
11249 } // -x- rsocket& vprintfline -x-
11250
11251 private:
11252 /*======================================================================*//**
11253 Track unencrypted bytes received. When the number of bytes is negative or
11254 zero, it isn't recorded.
11255 This is an internal function.
11256 @returns same value provided in @ref n
11257 @qualifier TLS
11258 *///=========================================================================
11259 int __track_bytes_rx(
11260 /// Number of bytes transferred
11261 const int n) {
11262 if (n > 0) __bytes_rx.fetch_add(n, std::memory_order_relaxed);
11263 return n;
11264 } // -x- int __track_bytes_rx -x-
11265
11266 /*======================================================================*//**
11267 Track unencrypted bytes transmitted. When the number of bytes is negative or
11268 zero, it isn't recorded.
11269 This is an internal function.
11270 @returns same value provided in @ref n
11271 @qualifier TLS
11272 *///=========================================================================
11273 int __track_bytes_tx(
11274 /// Number of bytes transferred
11275 const int n) {
11276 if (n > 0) __bytes_tx.fetch_add(n, std::memory_order_relaxed);
11277 return n;
11278 } // -x- int __track_bytes_tx -x-
11279
11280 /*======================================================================*//**
11281 Track encrypted bytes received. When the number of bytes is negative or
11282 zero, it isn't recorded.
11283 This is an internal function.
11284 @returns same value provided in @ref n
11285 @qualifier TLS
11286 *///=========================================================================
11287 int __track_crypt_rx(
11288 /// Number of bytes transferred
11289 const int n) {
11290 if (n > 0) __crypt_rx.fetch_add(n, std::memory_order_relaxed);
11291 return n;
11292 } // -x- int __track_crypt_rx -x-
11293
11294 /*======================================================================*//**
11295 Track encrypted bytes transmitted. When the number of bytes is negative or
11296 zero, it isn't recorded.
11297 This is an internal function.
11298 @returns same value provided in @ref n
11299 @qualifier TLS
11300 *///=========================================================================
11301 int __track_crypt_tx(
11302 /// Number of bytes transferred
11303 const int n) {
11304 if (n > 0) __crypt_tx.fetch_add(n, std::memory_order_relaxed);
11305 return n;
11306 } // -x- int __track_crypt_tx -x-
11307
11308 /*======================================================================*//**
11309 Specialized private timeout-handling class that sets a new timeout on the
11310 specified socket, then relies on RAII to activate the destructor to restore
11311 the original timeout (which was acquired in the constructor).
11312 *///=========================================================================
11313 class RAII_timeout {
11314
11315 private:
11316 // --------------------------------------------------------------------------
11317 // Prepare timeout structures so that we can override it (if timeout != 0)
11318 // and also restore it to what it used to be once we're finished. We need to
11319 // set up the "timeout_target" variable and few timeval structures, backup
11320 // the current non-recvline timeout settings, and set the recvline timeout.
11321 // --------------------------------------------------------------------------
11322 randolf::rsocket* r; // TODO: Add support for debug() method
11323 struct timeval old_tv{0, 0}; // {tv_sec, tv_usec};
11324 struct timeval new_tv{0, 0}; // {tv_sec, tv_usec};
11325
11326 public:
11327 /*======================================================================*//**
11328 @brief
11329 Change the specified rsocket's @c timeout, after first saving the current
11330 timeout settings for later restoration.
11331
11332 If both @c sec or @c usec are set to 0, then this class will not make any
11333 changes to the underlying socket's timeout.
11334 *///=========================================================================
11335 RAII_timeout(
11336 /// The rsocket class to which to apply the timeout
11337 randolf::rsocket& r,
11338 /// Timeout in seconds
11339 const int sec,
11340 /// Timeout in microseconds
11341 const int usec = 0) {
11342 if (sec == 0 && usec == 0) return; // Do nothing if timeout is 0
11343 this->r = &r;
11344 if (r.__debug) r.debug("RAII_timeout(socket{0x" + randolf::rtools::to_hex(r.__socket_fd) + "}"
11345 + ", " + std::to_string(sec)
11346 + ", " + std::to_string(usec)
11347 + ");");
11348 socklen_t otv{sizeof(old_tv)};
11349 r.__rc_check(::getsockopt(r.__socket_fd, SOL_SOCKET, SO_RCVTIMEO, &old_tv, &otv)); // getsockopt_timeval(SOL_SOCKET, SO_RCVTIMEO);
11350 new_tv.tv_sec = sec;
11351 new_tv.tv_usec = usec;
11352 r.__rc_check(::setsockopt(r.__socket_fd, SOL_SOCKET, SO_RCVTIMEO, &new_tv, sizeof(new_tv))); // setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
11353//std::cout << "Set timeout to " << sec << std::endl; // Debug
11354 } // -x- consturctor __temporary_timeout -x-
11355
11356 /*======================================================================*//**
11357 @brief
11358 Restore the underlying socket's original @c timeout setting.
11359 *///=========================================================================
11360 ~RAII_timeout() noexcept {
11361 if (new_tv.tv_sec == 0 && new_tv.tv_usec == 0) return; // Do nothing if timeout is 0
11362 if (r->__debug) r->debug("~RAII_timeout(socket{0x" + randolf::rtools::to_hex(r->__socket_fd) + "}"
11363 + ", " + std::to_string(old_tv.tv_sec)
11364 + ", " + std::to_string(old_tv.tv_usec)
11365 + ");");
11366 // This shouldn't fail, but if it does it won't matter since it can only mean that the socket is screwed
11367 ::setsockopt(r->__socket_fd, SOL_SOCKET, SO_RCVTIMEO, &new_tv, sizeof(new_tv)); // setsockopt(SOL_SOCKET, SO_RCVTIMEO, tv);
11368//std::cout << "Restore timeout" << std::endl; // Debug
11369 } // -x- destructor __temporary_timeout -x-
11370
11371 }; // -x- class __RAII_timeout -x-
11372
11373 }; // -x- class rsocket -x-
11374
11375}; // -x- namespace randolf -x-
11376
11377// Save this for a future sendlines() methods.
11378// const void* msg_ptr = msg.data(); // Prevent repeated calls to data() method
11379// const int len = msg.length(); // Prevent repeated calls to length() method