Mstdlib-1.24.0
m_io_net.h
1/* The MIT License (MIT)
2 *
3 * Copyright (c) 2017 Monetra Technologies, LLC.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 * THE SOFTWARE.
22 */
23
24#ifndef __M_IO_NET_H__
25#define __M_IO_NET_H__
26
27#include <mstdlib/base/m_defs.h>
28#include <mstdlib/base/m_types.h>
29#include <mstdlib/io/m_io.h>
30#include <mstdlib/io/m_event.h>
31
32__BEGIN_DECLS
33
34/*! \addtogroup m_io_net Network I/O
35 * \ingroup m_eventio_base
36 *
37 * Network I/O functions
38 *
39 * Capable of functioning as a network server and client.
40 *
41 * Examples
42 * ========
43 *
44 * Client
45 * ------
46 *
47 * Example network client which downloads google.com's home page. This uses
48 * a network client connection wrapped in TLS. A trace layer is provided and
49 * is commented out.
50 *
51 * \code{.c}
52 * #include <mstdlib/mstdlib.h>
53 * #include <mstdlib/mstdlib_io.h>
54 * #include <mstdlib/mstdlib_tls.h>
55 *
56 * static void do_trace(void *cb_arg, M_io_trace_type_t type, M_event_type_t event_type, const unsigned char *data, size_t data_len)
57 * {
58 * char *out;
59 *
60 * switch (type) {
61 * case M_IO_TRACE_TYPE_READ:
62 * M_printf("READ:\n");
63 * break;
64 * case M_IO_TRACE_TYPE_WRITE:
65 * M_printf("WRITE:\n");
66 * break;
67 * default:
68 * return;
69 * }
70 * out = M_str_hexdump(M_STR_HEXDUMP_HEADER, 0, "\t", data, data_len);
71 * M_printf("%s\n", out);
72 * M_free(out);
73 * }
74 *
75 * static void run_cb(M_event_t *el, M_event_type_t etype, M_io_t *io, void *thunk)
76 * {
77 * M_buf_t *connected_buf = thunk;
78 * char buf[128] = { 0 };
79 * size_t len_written = 0;
80 *
81 * switch (etype) {
82 * case M_EVENT_TYPE_CONNECTED:
83 * M_printf("CONNECTED: %s%s%s:%d (%s: %s - session%sreused)\n",
84 * M_io_net_get_type(io)==M_IO_NET_IPV6?"[":"",
85 * M_io_net_get_ipaddr(io),
86 * M_io_net_get_type(io)==M_IO_NET_IPV6?"]":"",
87 * M_io_net_get_port(io),
88 * M_tls_protocols_to_str(M_tls_get_protocol(io, M_IO_LAYER_FIND_FIRST_ID)),
89 * M_tls_get_cipher(io, M_IO_LAYER_FIND_FIRST_ID),
90 * M_tls_get_sessionreused(io, M_IO_LAYER_FIND_FIRST_ID)?" ":" not ");
91 *
92 * M_io_write_from_buf(io, connected_buf);
93 * break;
94 * case M_EVENT_TYPE_READ:
95 * M_io_read(io, (unsigned char *)buf, sizeof(buf), &len_written);
96 * if (len_written > 0) {
97 * M_printf("%.*s", (int)len_written, buf);
98 * }
99 * break;
100 * case M_EVENT_TYPE_WRITE:
101 * M_io_write_from_buf(io, connected_buf);
102 * break;
103 * case M_EVENT_TYPE_DISCONNECTED:
104 * case M_EVENT_TYPE_ERROR:
105 * M_event_done_with_disconnect(el, 0, 1000);
106 * break;
107 * case M_EVENT_TYPE_ACCEPT:
108 * case M_EVENT_TYPE_OTHER:
109 * M_event_done(el);
110 * break;
111 * }
112 * }
113 *
114 * int main(int argc, char **argv)
115 * {
116 * M_event_t *el;
117 * M_dns_t *dns;
118 * M_buf_t *buf;
119 * M_io_t *io;
120 * M_tls_clientctx_t *ctx;
121 * size_t layer_id;
122 *
123 *
124 * dns = M_dns_create();
125 * el = M_event_create(M_EVENT_FLAG_NONE);
126 * buf = M_buf_create();
127 * M_buf_add_str(buf, "GET / HTTP/1.1\r\n");
128 * M_buf_add_str(buf, "Host: www.google.com\r\n");
129 * M_buf_add_str(buf, "Connection: close\r\n");
130 * M_buf_add_str(buf, "\r\n");
131 *
132 * M_io_net_client_create(&io, dns, "google.com", 443, M_IO_NET_ANY);
133 * ctx = M_tls_clientctx_create();
134 * M_tls_clientctx_set_default_trust(ctx);
135 * M_io_tls_client_add(io, ctx, NULL, &layer_id);
136 * M_tls_clientctx_destroy(ctx);
137 * //M_io_add_trace(io, &layer_id, do_trace, NULL, NULL, NULL);
138 *
139 * M_event_add(el, io, run_cb, buf);
140 * M_event_loop(el, M_TIMEOUT_INF);
141 *
142 * M_io_destroy(io);
143 * M_buf_cancel(buf);
144 * M_event_destroy(el);
145 * M_dns_destroy(dns);
146 * return 0;
147 * }
148 * \endcode
149 *
150 * Server
151 * ------
152 *
153 * Example network sever. This is an echo server which uses a state machine
154 * to determine what operation it should perform.
155 *
156 * \code{.c}
157 * #include <mstdlib/mstdlib.h>
158 * #include <mstdlib/mstdlib_io.h>
159 *
160 * typedef enum {
161 * STATE_CHECK = 1,
162 * STATE_ECHO,
163 * STATE_EXIT
164 * } states_t;
165 *
166 * typedef struct {
167 * M_buf_t *write_buf;
168 * M_parser_t *read_parser;
169 * M_io_t *io;
170 * M_event_t *el;
171 * M_state_machine_t *sm;
172 * } ldata_t;
173 *
174 * static M_state_machine_status_t state_check(void *data, M_uint64 *next)
175 * {
176 * ldata_t *ldata = data;
177 *
178 * (void)next;
179 *
180 * M_parser_mark(ldata->read_parser);
181 * if (M_parser_len(ldata->read_parser) == 0 || M_parser_consume_until(ldata->read_parser, (const unsigned char *)"\n", 1, M_TRUE) == 0) {
182 * M_parser_mark_rewind(ldata->read_parser);
183 * return M_STATE_MACHINE_STATUS_WAIT;
184 * }
185 *
186 * return M_STATE_MACHINE_STATUS_NEXT;
187 * }
188 *
189 * static M_state_machine_status_t state_echo(void *data, M_uint64 *next)
190 * {
191 * ldata_t *ldata = data;
192 * char *out;
193 *
194 * out = M_parser_read_strdup_mark(ldata->read_parser);
195 * M_buf_add_str(ldata->write_buf, out);
196 * M_io_write_from_buf(ldata->io, ldata->write_buf);
197 *
198 * if (!M_str_eq(out, "EXIT\r\n") && !M_str_eq(out, "EXIT\n"))
199 * *next = STATE_CHECK;
200 *
201 * M_free(out);
202 * return M_STATE_MACHINE_STATUS_NEXT;
203 * }
204 *
205 * static M_state_machine_status_t state_exit(void *data, M_uint64 *next)
206 * {
207 * ldata_t *ldata = data;
208 * (void)next;
209 * M_event_done_with_disconnect(ldata->el, 0, 1000);
210 * }
211 *
212 * static void connection_cb(M_event_t *el, M_event_type_t etype, M_io_t *io, void *thunk)
213 * {
214 * ldata_t *ldata = thunk;
215 * char error[256] = { 0 };
216 * M_bool clean = M_FALSE;
217 * M_state_machine_status_t status;
218 *
219 * switch (etype) {
220 * case M_EVENT_TYPE_CONNECTED:
221 * M_printf("CLIENT CONNECTED: %s%s%s:%d\n",
222 * M_io_net_get_type(io)==M_IO_NET_IPV6?"[":"",
223 * M_io_net_get_ipaddr(io),
224 * M_io_net_get_type(io)==M_IO_NET_IPV6?"]":"",
225 * M_io_net_get_port(io));
226 * break;
227 * case M_EVENT_TYPE_READ:
228 * if (M_io_read_into_parser(io, ldata->read_parser) == M_IO_ERROR_SUCCESS) {
229 * status = M_state_machine_run(ldata->sm, ldata);
230 * if (status != M_STATE_MACHINE_STATUS_WAIT) {
231 * M_io_disconnect(io);
232 * }
233 * }
234 * break;
235 * case M_EVENT_TYPE_WRITE:
236 * if (M_buf_len(ldata->write_buf) > 0) {
237 * M_io_write_from_buf(io, ldata->write_buf);
238 * }
239 * break;
240 * case M_EVENT_TYPE_DISCONNECTED:
241 * clean = M_TRUE;
242 * case M_EVENT_TYPE_ACCEPT:
243 * case M_EVENT_TYPE_ERROR:
244 * case M_EVENT_TYPE_OTHER:
245 * if (!clean) {
246 * M_io_get_error_string(io, error, sizeof(error));
247 * }
248 * M_printf("CLIENT DISCONNECTED: %s%s%s:%d (%s%s%s)\n",
249 * M_io_net_get_type(io)==M_IO_NET_IPV6?"[":"",
250 * M_io_net_get_ipaddr(io),
251 * M_io_net_get_type(io)==M_IO_NET_IPV6?"]":"",
252 * M_io_net_get_port(io),
253 * clean?"clean":"unclean", clean?"":" - ", clean?"":error);
254 *
255 * M_io_destroy(io);
256 * M_state_machine_destroy(ldata->sm);
257 * M_buf_cancel(ldata->write_buf);
258 * M_parser_destroy(ldata->read_parser);
259 * M_free(ldata);
260 * break;
261 * }
262 * }
263 *
264 * static void listen_cb(M_event_t *el, M_event_type_t etype, M_io_t *io, void *thunk)
265 * {
266 * M_io_t *io_out = NULL;
267 * ldata_t *ldata;
268 * M_io_error_t ioerr;
269 * char error[256] = { 0 };
270 *
271 * (void)thunk;
272 *
273 * switch (etype) {
274 * case M_EVENT_TYPE_ACCEPT:
275 * ioerr = M_io_accept(&io_out, io);
276 * if (ioerr == M_IO_ERROR_WOULDBLOCK) {
277 * return;
278 * } else if (ioerr != M_IO_ERROR_SUCCESS || io_out == NULL) {
279 * M_io_get_error_string(io, error, sizeof(error));
280 * M_printf("ACCEPT FAILURE: %s\n", error);
281 * M_io_destroy(io_out);
282 * }
283 *
284 * ldata = M_malloc_zero(sizeof(*ldata));
285 * ldata->el = el;
286 * ldata->write_buf = M_buf_create();
287 * ldata->read_parser = M_parser_create(M_PARSER_FLAG_NONE);
288 * ldata->io = io_out;
289 * ldata->sm = M_state_machine_create(0, NULL, M_STATE_MACHINE_NONE);
290 * M_state_machine_insert_state(ldata->sm, STATE_CHECK, 0, NULL, state_check, NULL, NULL);
291 * M_state_machine_insert_state(ldata->sm, STATE_ECHO, 0, NULL, state_echo, NULL, NULL);
292 * M_state_machine_insert_state(ldata->sm, STATE_EXIT, 0, NULL, state_exit, NULL, NULL);
293 *
294 * M_event_add(el, io_out, connection_cb, ldata);
295 * break;
296 * case M_EVENT_TYPE_CONNECTED:
297 * case M_EVENT_TYPE_READ:
298 * case M_EVENT_TYPE_WRITE:
299 * break;
300 * case M_EVENT_TYPE_DISCONNECTED:
301 * case M_EVENT_TYPE_ERROR:
302 * case M_EVENT_TYPE_OTHER:
303 * M_io_destroy(io);
304 * break;
305 * }
306 * }
307 *
308 * int main(int argc, char *argv)
309 * {
310 * M_event_t *el;
311 * M_io_t *io = NULL;
312 * M_io_error_t ioerr;
313 *
314 *
315 * ioerr = M_io_net_server_create(&io, 8999, NULL, M_IO_NET_IPV4);
316 * if (ioerr != M_IO_ERROR_SUCCESS) {
317 * M_printf("Could not start server: %s\n", M_io_error_string(ioerr));
318 * return 0;
319 * }
320 *
321 * el = M_event_create(M_EVENT_FLAG_NONE);
322 *
323 * M_event_add(el, io, listen_cb, NULL);
324 * M_event_loop(el, M_TIMEOUT_INF);
325 *
326 * M_event_destroy(el);
327 * return 0;
328 * }
329 * \endcode
330 *
331 * @{
332 */
333
334/*! IP connection type. */
336 M_IO_NET_ANY = 1, /*!< Either ipv4 or ipv6 */
337 M_IO_NET_IPV4 = 2, /*!< ipv4 only */
338 M_IO_NET_IPV6 = 3 /*!< ipv6 only */
341
342#include <mstdlib/io/m_dns.h>
343
344/*! Create a server listener net object.
345 *
346 * \param[out] io_out io object for communication.
347 * \param[in] port Port to listen on. If 0 is used, the OS will assign an unused port which can be retrieved
348 * immediately after successful return of this function via M_io_net_get_port().
349 * \param[in] bind_ip NULL to listen on all interfaces, or an explicit ip address to listen on.
350 * Note that listening on localhost ::1 will be ipv6 only, or localhost 127.0.0.1 will be
351 * ipv4 only.
352 * \param[in] type Connection type.
353 *
354 * \return Result.
355 */
356M_API M_io_error_t M_io_net_server_create(M_io_t **io_out, unsigned short port, const char *bind_ip, M_io_net_type_t type);
357
358
359/*! Create a client net object.
360 *
361 * \param[out] io_out io object for communication.
362 * \param[in] dns DNS object for host name lookup. Required. It will be reference counted allowing it to be
363 * destroyed while still in use by the io object.
364 * \param[in] host Host to connect to. Can be a host name or ip address.
365 * \param[in] port Port to connect to.
366 * \param[in] type Connection type.
367 *
368 * \return Result.
369 */
370M_API M_io_error_t M_io_net_client_create(M_io_t **io_out, M_dns_t *dns, const char *host, unsigned short port, M_io_net_type_t type);
371
372
373/*! Set keep alive.
374 *
375 * \param[in] io io object.
376 * \param[in] idle_time_s Idle time in seconds.
377 * \param[in] retry_time_s Retry time in seconds.
378 * \param[in] retry_cnt Retry count.
379 *
380 * \return M_TRUE on success, otherwise M_FALSE on failure.
381 */
382M_API M_bool M_io_net_set_keepalives(M_io_t *io, M_uint64 idle_time_s, M_uint64 retry_time_s, M_uint64 retry_cnt);
383
384
385/*! Enable/disable Nagle algorithm.
386 *
387 * \param[in] io io object.
388 * \param[in] nagle_enabled M_TRUE to enable, M_FALSE to disable.
389 *
390 * \return M_TRUE on success, otherwise M_FALSE on error.
391 * Will return M_TRUE if state being set is the same as already set.
392 * Meaning, enabling on an io that already has it enabled will return success.
393 */
394M_API M_bool M_io_net_set_nagle(M_io_t *io, M_bool nagle_enabled);
395
396
397/*! Set connect timeout.
398 *
399 * This is the timeout to wait for a connection to finish.
400 *
401 * \param[in] io io object.
402 * \param[in] timeout_ms Timeout in milliseconds.
403 *
404 * \return M_TRUE on success, otherwise M_FALSE on error.
405 */
406M_API M_bool M_io_net_set_connect_timeout_ms(M_io_t *io, M_uint64 timeout_ms);
407
408
409/*! Get the Fully Qualified Domain Name.
410 *
411 * \return String.
412 */
413M_API char *M_io_net_get_fqdn(void);
414
415
416/*! Get the hostname of the connected endpoint.
417 *
418 * This may return an IP address for inbound connections, or for outbound connections
419 * where an ip address was passed. This will not do a reverse hostname lookup.
420 *
421 * \param[in] io io object.
422 *
423 * \return String.
424 */
425M_API const char *M_io_net_get_host(M_io_t *io);
426
427
428/*! Get the IP address of the connected endpoint.
429 *
430 * \param[in] io io object.
431 *
432 * \return String.
433 */
434M_API const char *M_io_net_get_ipaddr(M_io_t *io);
435
436
437/*! Get the IP address of the server interface the end point connected to.
438 *
439 * \param[in] io io object.
440 *
441 * \return String.
442 */
443M_API const char *M_io_net_get_server_ipaddr(M_io_t *io);
444
445
446/*! Get the port of the connected endpoint.
447 *
448 * \param[in] io io object.
449 *
450 * \return Port.
451 */
452M_API unsigned short M_io_net_get_port(M_io_t *io);
453
454
455/*! Get the ephemeral (dynamic) port of the connected endpoint
456 *
457 * \param[in] io io object.
458 *
459 * \return Port.
460 */
461M_API unsigned short M_io_net_get_ephemeral_port(M_io_t *io);
462
463
464/*! Get connection type
465 *
466 * \param[in] io io object.
467 *
468 * \return Type.
469 */
471
472
473/*! Get connection timeout
474 *
475 * This is not the amount of time connec took, this is the
476 * timeout allowe for connect to take place.
477 *
478 * \param[in] io io object.
479 *
480 * \return Timeout in milliseconds.
481 */
483
484/*! Amount of time DNS query took
485 *
486 * \param[in] io io object.
487 *
488 * \return Time in milliseconds.
489 */
490M_API M_uint64 M_io_net_time_dns_ms(M_io_t *io);
491
492
493/*! Amount of time connection establishment took, not including DNS resolution time
494 *
495 * \param[in] io io object.
496 *
497 * \return Time in milliseconds.
498 */
500
501
502/*! Convert an ip address in string form into its binary network byte order representation.
503 *
504 * \param[out] ipaddr_bin User-supplied buffer of at least 16 bytes to store result.
505 * \param[in] ipaddr_bin_size Size of user-supplied buffer.
506 * \param[in] ipaddr_str IPv4 or IPv6 address in string form.
507 * \param[out] ipaddr_bin_len Pointer to hold length of ip address in binary form. Result
508 * will be 4 or 16 depending on the address type.
509 * \return M_TRUE if conversion was possible, M_FALSE otherwise
510 */
511M_API M_bool M_io_net_ipaddr_to_bin(unsigned char *ipaddr_bin, size_t ipaddr_bin_size, const char *ipaddr_str, size_t *ipaddr_bin_len);
512
513
514/*! Convert an ip address in its binary network byte order representation to string form.
515 *
516 * \param[out] ipaddr_str User-supplied buffer of at least 40 bytes to store IPv6
517 * address, or 16 bytes to store IPv4 address.
518 * \param[in] ipaddr_str_size Size of user-supplied buffer.
519 * \param[in] ipaddr_bin IPv4 or IPv6 address in binary form.
520 * \param[in] ipaddr_bin_len Length of ip address in binary form (must be 4 or 16).
521 *
522 * \return M_TRUE if conversion was possible, M_FALSE otherwise
523 */
524M_API M_bool M_io_net_bin_to_ipaddr(char *ipaddr_str, size_t ipaddr_str_size, const unsigned char *ipaddr_bin, size_t ipaddr_bin_len);
525
526/*! @} */
527
528__END_DECLS
529
530
531#endif
struct M_dns M_dns_t
Definition: m_dns.h:43
M_bool M_io_net_set_nagle(M_io_t *io, M_bool nagle_enabled)
enum M_io_net_type M_io_net_type_t
Definition: m_io_net.h:340
const char * M_io_net_get_host(M_io_t *io)
unsigned short M_io_net_get_port(M_io_t *io)
M_uint64 M_io_net_time_dns_ms(M_io_t *io)
M_bool M_io_net_ipaddr_to_bin(unsigned char *ipaddr_bin, size_t ipaddr_bin_size, const char *ipaddr_str, size_t *ipaddr_bin_len)
enum M_io_net_type M_io_net_get_type(M_io_t *io)
char * M_io_net_get_fqdn(void)
M_io_net_type
Definition: m_io_net.h:335
unsigned short M_io_net_get_ephemeral_port(M_io_t *io)
M_io_error_t M_io_net_client_create(M_io_t **io_out, M_dns_t *dns, const char *host, unsigned short port, M_io_net_type_t type)
M_bool M_io_net_bin_to_ipaddr(char *ipaddr_str, size_t ipaddr_str_size, const unsigned char *ipaddr_bin, size_t ipaddr_bin_len)
M_uint64 M_io_net_time_connect_ms(M_io_t *io)
M_io_error_t M_io_net_server_create(M_io_t **io_out, unsigned short port, const char *bind_ip, M_io_net_type_t type)
M_bool M_io_net_set_keepalives(M_io_t *io, M_uint64 idle_time_s, M_uint64 retry_time_s, M_uint64 retry_cnt)
const char * M_io_net_get_ipaddr(M_io_t *io)
M_bool M_io_net_set_connect_timeout_ms(M_io_t *io, M_uint64 timeout_ms)
const char * M_io_net_get_server_ipaddr(M_io_t *io)
M_uint64 M_io_net_get_connect_timeout_ms(M_io_t *io)
@ M_IO_NET_ANY
Definition: m_io_net.h:336
@ M_IO_NET_IPV6
Definition: m_io_net.h:338
@ M_IO_NET_IPV4
Definition: m_io_net.h:337
enum M_io_error M_io_error_t
Definition: m_io.h:93
struct M_io M_io_t
Definition: m_io.h:59