Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_network.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
27#include <errno.h>
28#include <limits.h>
29#include <stdarg.h>
30#include <pthread.h>
31#include <unistd.h>
32#include <string.h>
33#include <ctype.h>
34#include <sys/types.h>
35
36#include "nilorea/n_network.h"
38#include "nilorea/n_log.h"
39#include "nilorea/n_hash.h"
40#include "nilorea/n_base64.h"
41
42/* MinGW does not provide strndup */
43#ifdef __windows__
44static char* _n_strndup(const char* s, size_t n) {
45 size_t len = strlen(s);
46 if (n < len) len = n;
47 char* p = (char*)malloc(len + 1);
48 if (p) {
49 memcpy(p, s, len);
50 p[len] = '\0';
51 }
52 return p;
53}
54#define strndup _n_strndup
55#endif
56
57#ifdef HAVE_OPENSSL
58#include <openssl/sha.h>
59#include <openssl/rand.h>
60#endif
61
62/* ---- error capture infrastructure ---- */
63
65#ifndef _Thread_local
66#define _Thread_local __thread
67#endif
68static _Thread_local char s_connect_errors[8][512];
71
73static void _netw_capture_error(NETWORK* netw, const char* fmt, ...) {
74 if (!netw) return;
75 va_list ap;
76 va_start(ap, fmt);
77 vsnprintf(netw->netw_errors[netw->netw_err_next], 512, fmt, ap);
78 va_end(ap);
81}
82
84static void _netw_capture_connect_error(const char* fmt, ...) {
85 va_list ap;
86 va_start(ap, fmt);
87 vsnprintf(s_connect_errors[s_connect_err_next], 512, fmt, ap);
88 va_end(ap);
91}
92
93/* ---- public error accessors ---- */
94
98
99const char* n_netw_get_error(NETWORK* netw, int index) {
100 if (!netw || index < 0 || index >= netw->netw_err_count) return NULL;
101 int oldest = (netw->netw_err_next - netw->netw_err_count + 8) % 8;
102 return netw->netw_errors[(oldest + index) % 8];
103}
104
106 if (!netw) return;
107 netw->netw_err_count = 0;
108 netw->netw_err_next = 0;
109}
110
114
115const char* n_netw_get_connect_error(int index) {
116 if (index < 0 || index >= s_connect_err_count) return NULL;
117 int oldest = (s_connect_err_next - s_connect_err_count + 8) % 8;
118 return s_connect_errors[(oldest + index) % 8];
119}
120
125
128
129
134size_t htonst(size_t value) {
135#if __BYTE_ORDER == __LITTLE_ENDIAN
136 if (sizeof(size_t) == 4) {
137 return (size_t)htonl((uint32_t)value);
138 } else if (sizeof(size_t) == 8) {
139 return ((size_t)htonl((uint32_t)(value >> 32)) |
140 ((size_t)htonl((uint32_t)value) << 32));
141 }
142#endif
143 return value; // No conversion needed for big-endian
144}
145
151size_t ntohst(size_t value) {
152#if __BYTE_ORDER == __LITTLE_ENDIAN
153 if (sizeof(size_t) == 4) {
154 return (size_t)ntohl((uint32_t)value);
155 } else if (sizeof(size_t) == 8) {
156 return ((size_t)ntohl((uint32_t)(value >> 32)) |
157 ((size_t)ntohl((uint32_t)value) << 32));
158 }
159#endif
160 return value; // No conversion needed for big-endian
161}
162
163#ifdef __windows__
164
170char* wchar_to_char(const wchar_t* pwchar) {
171 // get the number of characters in the string.
172 int currentCharIndex = 0;
173 char currentChar = (char)pwchar[currentCharIndex];
174 char* filePathC = NULL;
175
176 while (currentChar != '\0') {
177 currentCharIndex++;
178 currentChar = (char)pwchar[currentCharIndex];
179 }
180
181 const int charCount = currentCharIndex + 1;
182
183 // allocate a new block of memory size char (1 byte) instead of wide char (2 bytes)
184 Malloc(filePathC, char, (size_t)charCount);
185 __n_assert(filePathC, return NULL);
186
187 for (int i = 0; i < charCount; i++) {
188 // convert to char (1 byte)
189 char character = (char)pwchar[i];
190
191 *filePathC = character;
192
193 filePathC += sizeof(char);
194 }
195 filePathC += '\0';
196
197 filePathC -= (sizeof(char) * (size_t)charCount);
198
199 return filePathC;
200}
201
203#define NETW_BUFLEN_CAST(x) ((int)(x))
204
206#define NETW_CALL_RETRY(__retvar, __expression, __max_tries) \
207 do { \
208 int __nb_retries = 0; \
209 do { \
210 __retvar = (__expression); \
211 __nb_retries++; \
212 } while (__retvar == -1 && (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK) && __nb_retries < (__max_tries)); \
213 if (__retvar == -1 && __nb_retries >= (__max_tries)) __retvar = -2; \
214 } while (0)
215
217#define neterrno WSAGetLastError()
218
220#define netstrerror(code) ({ \
221 wchar_t* __netstrerr_ws = NULL; \
222 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, \
223 NULL, (DWORD)(code), \
224 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), \
225 (LPWSTR) & __netstrerr_ws, 0, NULL); \
226 char* netstr = wchar_to_char(__netstrerr_ws); \
227 LocalFree(__netstrerr_ws); \
228 netstr; \
229})
230
231#if __GNUC__ <= 6 && __GNUC_MINOR__ <= 3
232
233/*--------------------------------------------------------------------------------------
234 By Marco Ladino - mladinox.. jan/2016
235 MinGW 3.45 thru 4.5 versions, don't have the socket functions:
236 --> inet_ntop(..)
237 --> inet_pton(..)
238 But with this adapted code using the original functions from FreeBSD,
239 one can to use it in the C/C++ Applications, without problem..!
240 This implementation, include tests for IPV4 and IPV6 addresses,
241 and is full C/C++ compatible..
242 --------------------------------------------------------------------------------------*/
243/* OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp */
244/*-
245 * Copyright (c) 1998 Todd C. Miller <Todd.Miller at courtesan.com>
246 *
247 * Permission to use, copy, modify, and distribute this software for any
248 * purpose with or without fee is hereby granted, provided that the above
249 * copyright notice and this permission notice appear in all copies.
250 *
251 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
252 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
253 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
254 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
255 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
256 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
257 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
258 */
259
267size_t strlcpy(char* dst, const char* src, size_t siz) {
268 char* d = dst;
269 const char* s = src;
270 size_t n = siz;
271
272 /* Copy as many bytes as will fit */
273 if (n != 0) {
274 while (--n != 0) {
275 if ((*d++ = *s++) == '\0')
276 break;
277 }
278 }
279
280 /* Not enough room in dst, add NUL and traverse rest of src */
281 if (n == 0) {
282 if (siz != 0)
283 *d = '\0'; /* NUL-terminate dst */
284 while (*s++);
285 }
286
287 return (s - src - 1); /* count does not include NUL */
288}
289
290/*
291 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
292 * Copyright (c) 1996-1999 by Internet Software Consortium.
293 *
294 * Permission to use, copy, modify, and distribute this software for any
295 * purpose with or without fee is hereby granted, provided that the above
296 * copyright notice and this permission notice appear in all copies.
297 *
298 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
299 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
300 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
301 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
302 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
303 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
304 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
305 */
306
307/*
308 * WARNING: Don't even consider trying to compile this on a system where
309 * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
310 */
311
312static char* inet_ntop4(const unsigned char* src, char* dst, socklen_t size);
313static char* inet_ntop6(const unsigned char* src, char* dst, socklen_t size);
314
323char* inet_ntop(int af, const void* src, char* dst, socklen_t size) {
324 switch (af) {
325 case AF_INET:
326 return (inet_ntop4((const unsigned char*)src, dst, size));
327 case AF_INET6:
328 return (inet_ntop6((const unsigned char*)src, dst, size));
329 default:
330 return (NULL);
331 }
332 /* NOTREACHED */
333}
334
335/* const char *
336 * inet_ntop4(src, dst, size)
337 * format an IPv4 address
338 * return:
339 * `dst' (as a const)
340 * notes:
341 * (1) uses no statics
342 * (2) takes a u_char* not an in_addr as input
343 * author:
344 * Paul Vixie, 1996.
345 */
346static char* inet_ntop4(const unsigned char* src, char* dst, socklen_t size) {
347 static const char fmt[] = "%u.%u.%u.%u";
348 char tmp[sizeof "255.255.255.255"];
349 int l;
350
351 l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
352 if (l <= 0 || (socklen_t)l >= size) {
353 return (NULL);
354 }
355 strlcpy(dst, tmp, size);
356 return (dst);
357}
358
359/* const char *
360 * inet_ntop6(src, dst, size)
361 * convert IPv6 binary address into presentation (printable) format
362 * author:
363 * Paul Vixie, 1996.
364 */
365static char* inet_ntop6(const unsigned char* src, char* dst, socklen_t size) {
366 /*
367 * Note that int32_t and int16_t need only be "at least" large enough
368 * to contain a value of the specified size. On some systems, like
369 * Crays, there is no such thing as an integer variable with 16 bits.
370 * Keep this in mind if you think this function should have been coded
371 * to use pointer overlays. All the world's not a VAX.
372 */
373 char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
374 struct
375 {
376 int base, len;
377 } best, cur;
378#define NS_IN6ADDRSZ 16
379#define NS_INT16SZ 2
380 u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
381 int i;
382
383 /*
384 * Preprocess:
385 * Copy the input (bytewise) array into a wordwise array.
386 * Find the longest run of 0x00's in src[] for :: shorthanding.
387 */
388 memset(words, '\0', sizeof words);
389 for (i = 0; i < NS_IN6ADDRSZ; i++)
390 words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
391 best.base = -1;
392 best.len = 0;
393 cur.base = -1;
394 cur.len = 0;
395 for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
396 if (words[i] == 0) {
397 if (cur.base == -1)
398 cur.base = i, cur.len = 1;
399 else
400 cur.len++;
401 } else {
402 if (cur.base != -1) {
403 if (best.base == -1 || cur.len > best.len)
404 best = cur;
405 cur.base = -1;
406 }
407 }
408 }
409 if (cur.base != -1) {
410 if (best.base == -1 || cur.len > best.len)
411 best = cur;
412 }
413 if (best.base != -1 && best.len < 2)
414 best.base = -1;
415
416 /*
417 * Format the result.
418 */
419 tp = tmp;
420 for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
421 /* Are we inside the best run of 0x00's? */
422 if (best.base != -1 && i >= best.base &&
423 i < (best.base + best.len)) {
424 if (i == best.base)
425 *tp++ = ':';
426 continue;
427 }
428 /* Are we following an initial run of 0x00s or any real hex? */
429 if (i != 0)
430 *tp++ = ':';
431 /* Is this address an encapsulated IPv4? */
432 if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 7 && words[7] != 0x0001) || (best.len == 5 && words[5] == 0xffff))) {
433 if (!inet_ntop4(src + 12, tp, sizeof tmp - (tp - tmp)))
434 return (NULL);
435 tp += strlen(tp);
436 break;
437 }
438 tp += sprintf(tp, "%x", words[i]);
439 }
440 /* Was it a trailing run of 0x00's? */
441 if (best.base != -1 && (best.base + best.len) ==
442 (NS_IN6ADDRSZ / NS_INT16SZ))
443 *tp++ = ':';
444 *tp++ = '\0';
445
446 /*
447 * Check for overflow, copy, and we're done.
448 */
449 if ((socklen_t)(tp - tmp) > size) {
450 return (NULL);
451 }
452 strcpy(dst, tmp);
453 return (dst);
454}
455
456/*
457 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
458 * Copyright (c) 1996,1999 by Internet Software Consortium.
459 *
460 * Permission to use, copy, modify, and distribute this software for any
461 * purpose with or without fee is hereby granted, provided that the above
462 * copyright notice and this permission notice appear in all copies.
463 *
464 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
465 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
466 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
467 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
468 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
469 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
470 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
471 */
472
473/*
474 * WARNING: Don't even consider trying to compile this on a system where
475 * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
476 */
477
478static int inet_pton4(const char* src, u_char* dst);
479static int inet_pton6(const char* src, u_char* dst);
480
488int inet_pton(int af, const char* src, void* dst) {
489 switch (af) {
490 case AF_INET:
491 return (inet_pton4(src, (unsigned char*)dst));
492 case AF_INET6:
493 return (inet_pton6(src, (unsigned char*)dst));
494 default:
495 return (-1);
496 }
497 /* NOTREACHED */
498}
499
500/* int
501 * inet_pton4(src, dst)
502 * like inet_aton() but without all the hexadecimal and shorthand.
503 * return:
504 * 1 if `src' is a valid dotted quad, else 0.
505 * notice:
506 * does not touch `dst' unless it's returning 1.
507 * author:
508 * Paul Vixie, 1996.
509 */
510static int inet_pton4(const char* src, u_char* dst) {
511 static const char digits[] = "0123456789";
512 int saw_digit, octets, ch;
513#define NS_INADDRSZ 4
514 u_char tmp[NS_INADDRSZ], *tp;
515
516 saw_digit = 0;
517 octets = 0;
518 *(tp = tmp) = 0;
519 while ((ch = *src++) != '\0') {
520 const char* pch;
521
522 if ((pch = strchr(digits, ch)) != NULL) {
523 u_int uiNew = *tp * 10 + (pch - digits);
524
525 if (saw_digit && *tp == 0)
526 return (0);
527 if (uiNew > 255)
528 return (0);
529 *tp = uiNew;
530 if (!saw_digit) {
531 if (++octets > 4)
532 return (0);
533 saw_digit = 1;
534 }
535 } else if (ch == '.' && saw_digit) {
536 if (octets == 4)
537 return (0);
538 *++tp = 0;
539 saw_digit = 0;
540 } else
541 return (0);
542 }
543 if (octets < 4)
544 return (0);
545 memcpy(dst, tmp, NS_INADDRSZ);
546 return (1);
547}
548
549/* int
550 * inet_pton6(src, dst)
551 * convert presentation level address to network order binary form.
552 * return:
553 * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
554 * notice:
555 * (1) does not touch `dst' unless it's returning 1.
556 * (2) :: in a full address is silently ignored.
557 * credit:
558 * inspired by Mark Andrews.
559 * author:
560 * Paul Vixie, 1996.
561 */
562static int inet_pton6(const char* src, u_char* dst) {
563 static const char xdigits_l[] = "0123456789abcdef",
564 xdigits_u[] = "0123456789ABCDEF";
565#define NS_IN6ADDRSZ 16
566#define NS_INT16SZ 2
567 u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
568 const char* curtok;
569 int ch, seen_xdigits;
570 u_int val;
571
572 memset((tp = tmp), '\0', NS_IN6ADDRSZ);
573 endp = tp + NS_IN6ADDRSZ;
574 colonp = NULL;
575 /* Leading :: requires some special handling. */
576 if (*src == ':')
577 if (*++src != ':')
578 return (0);
579 curtok = src;
580 seen_xdigits = 0;
581 val = 0;
582 while ((ch = *src++) != '\0') {
583 const char* xdigits : const char* pch;
584
585 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
586 pch = strchr((xdigits = xdigits_u), ch);
587 if (pch != NULL) {
588 val <<= 4;
589 val |= (pch - xdigits);
590 if (++seen_xdigits > 4)
591 return (0);
592 continue;
593 }
594 if (ch == ':') {
595 curtok = src;
596 if (!seen_xdigits) {
597 if (colonp)
598 return (0);
599 colonp = tp;
600 continue;
601 } else if (*src == '\0') {
602 return (0);
603 }
604 if (tp + NS_INT16SZ > endp)
605 return (0);
606 *tp++ = (u_char)(val >> 8) & 0xff;
607 *tp++ = (u_char)val & 0xff;
608 seen_xdigits = 0;
609 val = 0;
610 continue;
611 }
612 if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
613 inet_pton4(curtok, tp) > 0) {
614 tp += NS_INADDRSZ;
615 seen_xdigits = 0;
616 break; /* '\\0' was seen by inet_pton4(). */
617 }
618 return (0);
619 }
620 if (seen_xdigits) {
621 if (tp + NS_INT16SZ > endp)
622 return (0);
623 *tp++ = (u_char)(val >> 8) & 0xff;
624 *tp++ = (u_char)val & 0xff;
625 }
626 if (colonp != NULL) {
627 /*
628 * Since some memmove()'s erroneously fail to handle
629 * overlapping regions, we'll do the shift by hand.
630 */
631 const int n = tp - colonp;
632 int i;
633
634 if (tp == endp)
635 return (0);
636 for (i = 1; i <= n; i++) {
637 endp[-i] = colonp[n - i];
638 colonp[n - i] = 0;
639 }
640 tp = endp;
641 }
642 if (tp != endp)
643 return (0);
644 memcpy(dst, tmp, NS_IN6ADDRSZ);
645 return (1);
646}
647
648#endif /* if GCC_VERSION <= 4.5 */
649
650#else /* not __windows__ */
651
652#include <sys/types.h>
653#include <sys/wait.h>
654
656#define NETW_BUFLEN_CAST(x) ((size_t)(x))
657
659#define NETW_CALL_RETRY(__retvar, __expression, __max_tries) \
660 do { \
661 int __nb_retries = 0; \
662 do { \
663 __retvar = (__expression); \
664 __nb_retries++; \
665 } while (__retvar == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) && __nb_retries < (__max_tries)); \
666 if (__retvar == -1 && __nb_retries >= (__max_tries)) __retvar = -2; \
667 } while (0)
668
670#define neterrno errno
671
673/* #define netstrerror( code )({ \
674 size_t errmsglen = 512 ; // strerrorlen_s( code ) + 1 ; \
675 char *errmsg = NULL ; \
676 Malloc( errmsg , char , errmsglen ); \
677 if( errmsg ) \
678 { \
679 strerror_s( errmsg , errmsglen , code ); \
680 } \
681 errmsg ; \
682 }) */
683
685#define netstrerror(code) ({ \
686 char* __errmsg = NULL; \
687 errno = 0; \
688 __errmsg = strdup(strerror(code)); \
689 if (errno == ENOMEM) { \
690 __errmsg = NULL; \
691 } \
692 __errmsg; \
693})
694
695#endif /* if not def windows */
696
703NETWORK* netw_new(size_t send_list_limit, size_t recv_list_limit) {
704 NETWORK* netw = NULL;
705
706 Malloc(netw, NETWORK, 1);
707 __n_assert(netw, return NULL);
708
709 /* netw itself */
710 netw->nb_pending = -1;
711 netw->mode = -1;
712 netw->user_id = -1;
716
717 /* netw -> link */
718 netw->link.sock = INVALID_SOCKET;
719 netw->link.port =
720 netw->link.ip = NULL;
721 memset(&netw->link.hints, 0, sizeof(struct addrinfo));
722 memset(&netw->link.raddr, 0, sizeof(struct sockaddr_storage));
723
724 /*initiliaze mutexs*/
725 if (pthread_mutex_init(&netw->sendbolt, NULL) != 0) {
726 n_log(LOG_ERR, "Error initializing netw -> sendbolt");
727 Free(netw);
728 return NULL;
729 }
730 /*initiliaze mutexs*/
731 if (pthread_mutex_init(&netw->recvbolt, NULL) != 0) {
732 n_log(LOG_ERR, "Error initializing netw -> recvbolt");
733 pthread_mutex_destroy(&netw->sendbolt);
734 Free(netw);
735 return NULL;
736 }
737 /*initiliaze mutexs*/
738 if (pthread_mutex_init(&netw->eventbolt, NULL) != 0) {
739 n_log(LOG_ERR, "Error initializing netw -> eventbolt");
740 pthread_mutex_destroy(&netw->sendbolt);
741 pthread_mutex_destroy(&netw->recvbolt);
742 Free(netw);
743 return NULL;
744 }
745 /* initialize send sem bolt */
746 if (sem_init(&netw->send_blocker, 0, 0) != 0) {
747 n_log(LOG_ERR, "Error initializing netw -> eventbolt");
748 pthread_mutex_destroy(&netw->eventbolt);
749 pthread_mutex_destroy(&netw->sendbolt);
750 pthread_mutex_destroy(&netw->recvbolt);
751 Free(netw);
752 return NULL;
753 }
754 /*initialize queues */
755 netw->recv_buf = new_generic_list(recv_list_limit);
756 if (!netw->recv_buf) {
757 n_log(LOG_ERR, "Error when creating receive list with %d item limit", recv_list_limit);
759 return NULL;
760 }
761 netw->send_buf = new_generic_list(send_list_limit);
762 if (!netw->send_buf) {
763 n_log(LOG_ERR, "Error when creating send list with %d item limit", send_list_limit);
765 return NULL;
766 }
768 if (!netw->pools) {
769 n_log(LOG_ERR, "Error when creating pools list");
771 return NULL;
772 }
775 netw->so_reuseaddr = -1;
776 // netw -> so_reuseport = -1 ;
777 netw->so_keepalive = -1;
778 netw->tcpnodelay = -1;
779 netw->so_sndbuf = -1;
780 netw->so_rcvbuf = -1;
781 netw->so_rcvtimeo = -1;
782 netw->so_sndtimeo = -1;
783 netw->so_linger = -1;
789
792
793#ifdef HAVE_OPENSSL
794 netw->method = NULL;
795 netw->ctx = NULL;
796 netw->ssl = NULL;
797 netw->key = NULL;
798 netw->certificate = NULL;
799#endif
800
801 netw->link.is_blocking = 1;
802 return netw;
803
804} /* netw_new() */
805
811char* get_in_addr(struct sockaddr* sa) {
812 return sa->sa_family == AF_INET
813 ? (char*)&(((struct sockaddr_in*)sa)->sin_addr)
814 : (char*)&(((struct sockaddr_in6*)sa)->sin6_addr);
815}
816
824int netw_init_wsa(int mode, int v1, int v2) {
825 int compiler_warning_suppressor = 0;
826#if !defined(__linux__) && !defined(__sun) && !defined(_AIX)
827 static WSADATA WSAdata; /*WSA world*/
828 static int WSA_IS_INITIALIZED = 0; /*status checking*/
829
830 switch (mode) {
831 default:
832 /*returning WSA status*/
833 case 2:
834 return WSA_IS_INITIALIZED;
835 break;
836 /*loading WSA dll*/
837 case 1:
838 if (WSA_IS_INITIALIZED == 1)
839 return TRUE; /*already loaded*/
840 if ((WSAStartup(MAKEWORD(v1, v2), &WSAdata)) != 0) {
841 WSA_IS_INITIALIZED = 0;
842 return FALSE;
843 } else {
844 WSA_IS_INITIALIZED = 1;
845 return TRUE;
846 }
847 break;
848 /*unloading (closing) WSA */
849 case 0:
850 if (WSA_IS_INITIALIZED == 0)
851 return TRUE; /*already CLEANED or not loaded */
852 if (WSACleanup() == 0) {
853 WSA_IS_INITIALIZED = 0;
854 return TRUE;
855 }
856 break;
857 } /*switch(...)*/
858#endif /* ifndef __linux__ __sun _AIX */
859 compiler_warning_suppressor = mode + v1 + v2;
860 (void)compiler_warning_suppressor;
861 compiler_warning_suppressor = TRUE;
862 return compiler_warning_suppressor;
863} /*netw_init_wsa(...)*/
864
871int netw_set_blocking(NETWORK* netw, unsigned long int is_blocking) {
872 __n_assert(netw, return FALSE);
873
874 int error = 0;
875 (void)error;
876 char* errmsg = NULL;
877
878#if defined(__linux__) || defined(__sun)
879 int flags = 0;
880 flags = fcntl(netw->link.sock, F_GETFL, 0);
881 if (netw->link.is_blocking != 0 && !is_blocking) {
882 if (flags & O_NONBLOCK) {
883 n_log(LOG_DEBUG, "socket %d was already in non-blocking mode", netw->link.sock);
884 /* in case we missed it, let's update the link mode */
885 netw->link.is_blocking = 0;
886 return TRUE;
887 }
888 } else if (netw->link.is_blocking != 1 && is_blocking) {
889 if (!(flags & O_NONBLOCK)) {
890 n_log(LOG_DEBUG, "socket %d was already in blocking mode", netw->link.sock);
891 /* in case we missed it, let's update the link mode */
892 netw->link.is_blocking = 1;
893 return TRUE;
894 }
895 }
896 if (fcntl(netw->link.sock, F_SETFL, is_blocking ? flags & ~O_NONBLOCK : flags | O_NONBLOCK) == -1) {
897 error = neterrno;
898 errmsg = netstrerror(error);
899 _netw_capture_error(netw, "couldn't set blocking mode %d on %d: %s", is_blocking, netw->link.sock, _str(errmsg));
900 n_log(LOG_ERR, "couldn't set blocking mode %d on %d: %s", is_blocking, netw->link.sock, _str(errmsg));
901 FreeNoLog(errmsg);
902 return FALSE;
903 }
904#else
905 unsigned long int blocking = 1 - is_blocking;
906 int res = ioctlsocket(netw->link.sock, (long)FIONBIO, &blocking);
907 error = neterrno;
908 if (res != NO_ERROR) {
909 errmsg = netstrerror(error);
910 _netw_capture_error(netw, "ioctlsocket failed with error: %ld , neterrno: %s", res, _str(errmsg));
911 n_log(LOG_ERR, "ioctlsocket failed with error: %ld , neterrno: %s", res, _str(errmsg));
912 FreeNoLog(errmsg);
914 return FALSE;
915 }
916#endif
917 netw->link.is_blocking = is_blocking;
918 return TRUE;
919} /* netw_set_blocking */
920
929int netw_setsockopt(NETWORK* netw, int optname, int value) {
930 __n_assert(netw, return FALSE);
931
932 int error = 0;
933 char* errmsg = NULL;
934
935 switch (optname) {
936 case TCP_NODELAY:
937 if (value >= 0) {
938 /* disable naggle algorithm */
939 if (setsockopt(netw->link.sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&value, sizeof(value)) == -1) {
940 error = neterrno;
941 errmsg = netstrerror(error);
942 _netw_capture_error(netw, "Error from setsockopt(TCP_NODELAY) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
943 n_log(LOG_ERR, "Error from setsockopt(TCP_NODELAY) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
944 FreeNoLog(errmsg);
945 return FALSE;
946 }
947 }
948 netw->tcpnodelay = value;
949 break;
950 case SO_SNDBUF:
951 /* socket sending buffer size */
952 if (value >= 0) {
953 if (setsockopt(netw->link.sock, SOL_SOCKET, SO_SNDBUF, (const char*)&value, sizeof(value)) == -1) {
954 error = neterrno;
955 errmsg = netstrerror(error);
956 _netw_capture_error(netw, "Error from setsockopt(SO_SNDBUF) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
957 n_log(LOG_ERR, "Error from setsockopt(SO_SNDBUF) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
958 FreeNoLog(errmsg);
959 return FALSE;
960 }
961 }
962 netw->so_sndbuf = value;
963 break;
964 case SO_RCVBUF:
965 /* socket receiving buffer */
966 if (value >= 0) {
967 if (setsockopt(netw->link.sock, SOL_SOCKET, SO_RCVBUF, (const char*)&value, sizeof(value)) == -1) {
968 error = neterrno;
969 errmsg = netstrerror(error);
970 _netw_capture_error(netw, "Error from setsockopt(SO_RCVBUF) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
971 n_log(LOG_ERR, "Error from setsockopt(SO_RCVBUF) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
972 FreeNoLog(errmsg);
973 return FALSE;
974 }
975 }
976 netw->so_rcvbuf = value;
977 break;
978 case SO_REUSEADDR:
979 /* lose the pesky "Address already in use" error message*/
980 if (setsockopt(netw->link.sock, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(value)) == -1) {
981 error = neterrno;
982 errmsg = netstrerror(error);
983 _netw_capture_error(netw, "Error from setsockopt(SO_REUSEADDR) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
984 n_log(LOG_ERR, "Error from setsockopt(SO_REUSEADDR) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
985 FreeNoLog(errmsg);
986 return FALSE;
987 }
988 netw->so_reuseaddr = value;
989 break;
990 /*case SO_REUSEPORT :
991 // lose the pesky "port already in use" error message
992 if ( setsockopt( netw -> link . sock, SOL_SOCKET, SO_REUSEPORT, (char *)&value, sizeof( value ) ) == -1 )
993 {
994 error=neterrno ;
995 errmsg = netstrerror( error );
996 n_log( LOG_ERR, "Error from setsockopt(SO_REUSEPORT) on socket %d. neterrno: %s", netw -> link . sock, _str( errmsg ) );
997 FreeNoLog( errmsg );
998 return FALSE ;
999 }
1000 netw -> so_reuseport = value ;
1001 break ;*/
1002 case SO_LINGER: {
1003 struct linger ling;
1004 if (value < 0) {
1005 ling.l_onoff = 0;
1006 ling.l_linger = 0;
1007 } else if (value == 0) {
1008 ling.l_onoff = 1;
1009 ling.l_linger = 0;
1010 } else {
1011 ling.l_onoff = 1;
1012#ifdef __windows__
1013 ling.l_linger = (u_short)value;
1014#else
1015 ling.l_linger = value;
1016#endif
1017 }
1018#ifndef __windows__
1019 if (setsockopt(netw->link.sock, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)) == -1) {
1020 error = neterrno;
1021 errmsg = netstrerror(error);
1022 _netw_capture_error(netw, "Error from setsockopt(SO_LINGER) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1023 n_log(LOG_ERR, "Error from setsockopt(SO_LINGER) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1024 FreeNoLog(errmsg);
1025 return FALSE;
1026 }
1027#else
1028 if (setsockopt(netw->link.sock, SOL_SOCKET, SO_LINGER, (const char*)&ling, sizeof(ling)) == -1) {
1029 error = neterrno;
1030 errmsg = netstrerror(error);
1031 _netw_capture_error(netw, "Error from setsockopt(SO_LINGER) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1032 n_log(LOG_ERR, "Error from setsockopt(SO_LINGER) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1033 FreeNoLog(errmsg);
1034 return FALSE;
1035 }
1036#endif // __windows__
1037 netw->so_linger = value;
1038 } break;
1039 case SO_RCVTIMEO:
1040 if (value >= 0) {
1041#ifndef __windows__
1042 {
1043 struct timeval tv;
1044 tv.tv_sec = value;
1045 tv.tv_usec = 0;
1046 if (setsockopt(netw->link.sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv) == -1) {
1047 error = neterrno;
1048 errmsg = netstrerror(error);
1049 _netw_capture_error(netw, "Error from setsockopt(SO_RCVTIMEO) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1050 n_log(LOG_ERR, "Error from setsockopt(SO_RCVTIMEO) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1051 FreeNoLog(errmsg);
1052 return FALSE;
1053 }
1054 }
1055#else
1056 if (setsockopt(netw->link.sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&value, sizeof value) == -1) {
1057 error = neterrno;
1058 errmsg = netstrerror(error);
1059 _netw_capture_error(netw, "Error from setsockopt(SO_RCVTIMEO) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1060 n_log(LOG_ERR, "Error from setsockopt(SO_RCVTIMEO) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1061 FreeNoLog(errmsg);
1062 return FALSE;
1063 }
1064#endif
1065 }
1066 netw->so_rcvtimeo = value;
1067 break;
1068 case SO_SNDTIMEO:
1069 if (value >= 0) {
1070#ifndef __windows__
1071 {
1072 struct timeval tv;
1073 tv.tv_sec = value;
1074 tv.tv_usec = 0;
1075
1076 if (setsockopt(netw->link.sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof tv) == -1) {
1077 error = neterrno;
1078 errmsg = netstrerror(error);
1079 _netw_capture_error(netw, "Error from setsockopt(SO_SNDTIMEO) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1080 n_log(LOG_ERR, "Error from setsockopt(SO_SNDTIMEO) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1081 FreeNoLog(errmsg);
1082 return FALSE;
1083 }
1084 }
1085#else
1086 if (setsockopt(netw->link.sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&value, sizeof value) == -1) {
1087 error = neterrno;
1088 errmsg = netstrerror(error);
1089 _netw_capture_error(netw, "Error from setsockopt(SO_SNDTIMEO) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1090 n_log(LOG_ERR, "Error from setsockopt(SO_SNDTIMEO) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1091 FreeNoLog(errmsg);
1092 return FALSE;
1093 }
1094#endif
1095 }
1096 netw->so_sndtimeo = value;
1097 break;
1100 break;
1103 break;
1105 netw->wait_close_timeout = value;
1106 break;
1109 break;
1110#ifdef __linux__
1111 case TCP_USER_TIMEOUT:
1112 if (value >= 0) {
1113 if (setsockopt(netw->link.sock, IPPROTO_TCP, TCP_USER_TIMEOUT, (const char*)&value, sizeof value) == -1) {
1114 error = neterrno;
1115 errmsg = netstrerror(error);
1116 _netw_capture_error(netw, "Error from setsockopt(TCP_USER_TIMEOUT) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1117 n_log(LOG_ERR, "Error from setsockopt(TCP_USER_TIMEOUT) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1118 FreeNoLog(errmsg);
1119 return FALSE;
1120 }
1121 }
1122 break;
1123 case TCP_QUICKACK:
1124 if (setsockopt(netw->link.sock, IPPROTO_TCP, TCP_QUICKACK, &value, sizeof(value)) < 0) {
1125 error = neterrno;
1126 errmsg = netstrerror(error);
1127 _netw_capture_error(netw, "Error setting setsockopt(TCP_QUICKACK) to %d on sock %d. neterrno: %s", value, netw->link.sock, _str(errmsg));
1128 n_log(LOG_ERR, "Error setting setsockopt(TCP_QUICKACK) to %d on sock %d. neterrno: %s", value, netw->link.sock, _str(errmsg));
1129 FreeNoLog(errmsg);
1130 return FALSE;
1131 }
1132 break;
1133#endif
1134 case SO_KEEPALIVE:
1135 if (setsockopt(netw->link.sock, SOL_SOCKET, SO_KEEPALIVE, (const char*)&value, sizeof value) == -1) {
1136 error = neterrno;
1137 errmsg = netstrerror(error);
1138 _netw_capture_error(netw, "Error from setsockopt(SO_KEEPALIVE) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1139 n_log(LOG_ERR, "Error from setsockopt(SO_KEEPALIVE) on socket %d. neterrno: %s", netw->link.sock, _str(errmsg));
1140 FreeNoLog(errmsg);
1141 return FALSE;
1142 }
1143 netw->so_keepalive = value;
1144 break;
1145
1146 default:
1147 _netw_capture_error(netw, "%d is not a supported setsockopt", optname);
1148 n_log(LOG_ERR, "%d is not a supported setsockopt", optname);
1149 return FALSE;
1150 }
1151 return TRUE;
1152} /* netw_set_sock_opt */
1153
1154#ifdef HAVE_OPENSSL
1155
1161 BIO* bio = BIO_new(BIO_s_mem());
1162 if (!bio) {
1163 return NULL;
1164 }
1165
1166 ERR_print_errors(bio); // Write errors to the BIO
1167
1168 char* buf;
1169 size_t len = (size_t)BIO_get_mem_data(bio, &buf); // Get data from the BIO. Can return 0 if empty of failled
1170
1171 // Allocate memory for the error string and copy it
1172 char* error_str = malloc(len + 1);
1173 if (error_str) {
1174 memcpy(error_str, buf, len);
1175 error_str[len] = '\0'; // Null-terminate the string
1176 }
1177
1178 BIO_free(bio); // Free the BIO
1179
1180 return error_str;
1181}
1182
1188 unsigned long error = 0;
1189 while ((error = ERR_get_error())) {
1190 n_log(LOG_ERR, "socket %d: %s", socket, ERR_reason_error_string(error));
1191 }
1192}
1193
1194/***************************************************************************
1195 * _ _ ____ _
1196 * Project ___| | | | _ \| |
1197 * / __| | | | |_) | |
1198 * | (__| |_| | _ <| |___
1199 * \___|\___/|_| \_\_____|
1200 *
1201 * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel at haxx.se>, et al.
1202 *
1203 * This software is licensed as described in the file COPYING, which
1204 * you should have received as part of this distribution. The terms
1205 * are also available at https://curl.haxx.se/docs/copyright.html.
1206 *
1207 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1208 * copies of the Software, and permit persons to whom the Software is
1209 * furnished to do so, under the terms of the COPYING file.
1210 *
1211 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1212 * KIND, either express or implied.
1213 *
1214 ***************************************************************************/
1215/* <DESC>
1216 * Show the required mutex callback setups for GnuTLS and OpenSSL when using
1217 * libcurl multi-threaded.
1218 * </DESC>
1219 */
1220/* A multi-threaded example that uses pthreads and fetches 4 remote files at
1221 * once over HTTPS. The lock callbacks and stuff assume OpenSSL <1.1 or GnuTLS
1222 * (libgcrypt) so far.
1223 *
1224 * OpenSSL docs for this:
1225 * https://www.openssl.org/docs/man1.0.2/man3/CRYPTO_num_locks.html
1226 * gcrypt docs for this:
1227 * https://gnupg.org/documentation/manuals/gcrypt/Multi_002dThreading.html
1228 */
1229
1230/* we have this global to let the callback get easy access to it */
1231static pthread_mutex_t* netw_ssl_lockarray;
1232
1233__attribute__((unused)) static void netw_ssl_lock_callback(int mode, int type, char* file, int line) {
1234 (void)file;
1235 (void)line;
1236 if (mode & CRYPTO_LOCK) {
1237 pthread_mutex_lock(&(netw_ssl_lockarray[type]));
1238 } else {
1239 pthread_mutex_unlock(&(netw_ssl_lockarray[type]));
1240 }
1241}
1242
1243__attribute__((unused)) static unsigned long thread_id(void) {
1244 unsigned long ret;
1245
1246 ret = (unsigned long)pthread_self();
1247 return ret;
1248}
1249
1250static void netw_init_locks(void) {
1251 int i;
1252
1253 size_t lock_count = (size_t)CRYPTO_num_locks();
1254 netw_ssl_lockarray = (pthread_mutex_t*)OPENSSL_malloc((size_t)(lock_count * sizeof(pthread_mutex_t)));
1255
1256 for (i = 0; i < CRYPTO_num_locks(); i++) {
1257 pthread_mutex_init(&(netw_ssl_lockarray[i]), NULL);
1258 }
1259
1260 CRYPTO_set_id_callback((unsigned long (*)())thread_id);
1261 CRYPTO_set_locking_callback((void (*)())netw_ssl_lock_callback);
1262}
1263
1264static void netw_kill_locks(void) {
1265 int i;
1266
1267 CRYPTO_set_locking_callback(NULL);
1268 for (i = 0; i < CRYPTO_num_locks(); i++)
1269 pthread_mutex_destroy(&(netw_ssl_lockarray[i]));
1270
1271 OPENSSL_free(netw_ssl_lockarray);
1272}
1273
1275
1281 if (OPENSSL_IS_INITIALIZED == 1)
1282 return TRUE; /*already loaded*/
1283
1284 SSL_library_init();
1285 SSL_load_error_strings();
1286 // Before OpenSSL 1.1.0 (< 0x10100000L): ERR_load_BIO_strings(); was required to load error messages for BIO functions
1287#if OPENSSL_VERSION_NUMBER < 0x10100000L
1288 ERR_load_BIO_strings();
1289#endif
1290 OpenSSL_add_all_algorithms();
1292
1294
1295 return TRUE;
1296} /*netw_init_openssl(...)*/
1297
1303 if (OPENSSL_IS_INITIALIZED == 0)
1304 return TRUE; /*already unloaded*/
1305
1307 EVP_cleanup();
1308
1310
1311 return TRUE;
1312} /*netw_unload_openssl(...)*/
1313
1321int netw_set_crypto(NETWORK* netw, char* key, char* certificate) {
1322 __n_assert(netw, return FALSE);
1323
1324 char* tmp_key = NULL;
1325 char* tmp_cert = NULL;
1326 if (key && strlen(key) > 0) {
1327 tmp_key = strdup(key);
1328 if (!tmp_key) {
1329 n_log(LOG_ERR, "strdup failed for key in netw_set_crypto");
1330 return FALSE;
1331 }
1332 }
1333 if (certificate && strlen(certificate) > 0) {
1334 tmp_cert = strdup(certificate);
1335 if (!tmp_cert) {
1336 n_log(LOG_ERR, "strdup failed for certificate in netw_set_crypto");
1337 FreeNoLog(tmp_key);
1338 return FALSE;
1339 }
1340 }
1341 if (tmp_key) {
1342 FreeNoLog(netw->key);
1343 netw->key = tmp_key;
1344 }
1345 if (tmp_cert) {
1347 netw->certificate = tmp_cert;
1348 }
1349 if (key && certificate) {
1351#if OPENSSL_VERSION_NUMBER >= 0x10100000L // OpenSSL 1.1.0 or later
1352 netw->method = TLS_method(); // create new server-method instance
1353#else
1354 netw->method = TLSv1_2_method(); // create new server-method instance
1355#endif
1356 netw->ctx = SSL_CTX_new(netw->method); // create new context from method
1357 // SSL_CTX_set_verify(netw -> ctx, SSL_VERIFY_PEER, NULL); // Enable certificate verification
1358
1359 if (netw->ctx == NULL) {
1361 return FALSE;
1362 }
1363
1364 // Load default system certs
1365 if (SSL_CTX_load_verify_locations(netw->ctx, NULL, "/etc/ssl/certs/") != 1) {
1367 return FALSE;
1368 }
1369
1370 if (SSL_CTX_use_certificate_file(netw->ctx, certificate, SSL_FILETYPE_PEM) <= 0) {
1372 return FALSE;
1373 }
1374 if (SSL_CTX_use_PrivateKey_file(netw->ctx, key, SSL_FILETYPE_PEM) <= 0) {
1376 return FALSE;
1377 }
1378
1381
1383 }
1384
1385 return TRUE;
1386} /* netw_set_crypto */
1387
1395int netw_set_crypto_pem(NETWORK* netw, const char* key_pem, const char* cert_pem) {
1396 __n_assert(netw, return FALSE);
1397 __n_assert(key_pem, return FALSE);
1398 __n_assert(cert_pem, return FALSE);
1399
1401#if OPENSSL_VERSION_NUMBER >= 0x10100000L
1402 netw->method = TLS_method();
1403#else
1404 netw->method = TLSv1_2_method();
1405#endif
1406 netw->ctx = SSL_CTX_new(netw->method);
1407 if (netw->ctx == NULL) {
1409 return FALSE;
1410 }
1411
1412 /* Load certificate from PEM string */
1413 BIO* cert_bio = BIO_new_mem_buf(cert_pem, -1);
1414 if (!cert_bio) {
1415 n_log(LOG_ERR, "Failed to create BIO for certificate PEM");
1416 return FALSE;
1417 }
1418 X509* cert = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL);
1419 BIO_free(cert_bio);
1420 if (!cert) {
1421 n_log(LOG_ERR, "Failed to parse certificate PEM");
1423 return FALSE;
1424 }
1425 if (SSL_CTX_use_certificate(netw->ctx, cert) <= 0) {
1426 X509_free(cert);
1428 return FALSE;
1429 }
1430 X509_free(cert);
1431
1432 /* Load private key from PEM string */
1433 BIO* key_bio = BIO_new_mem_buf(key_pem, -1);
1434 if (!key_bio) {
1435 n_log(LOG_ERR, "Failed to create BIO for key PEM");
1436 return FALSE;
1437 }
1438 EVP_PKEY* pkey = PEM_read_bio_PrivateKey(key_bio, NULL, NULL, NULL);
1439 BIO_free(key_bio);
1440 if (!pkey) {
1441 n_log(LOG_ERR, "Failed to parse private key PEM");
1443 return FALSE;
1444 }
1445 if (SSL_CTX_use_PrivateKey(netw->ctx, pkey) <= 0) {
1446 EVP_PKEY_free(pkey);
1448 return FALSE;
1449 }
1450 EVP_PKEY_free(pkey);
1451
1452 /* Verify that key matches certificate */
1453 if (!SSL_CTX_check_private_key(netw->ctx)) {
1454 n_log(LOG_ERR, "Private key does not match the certificate");
1455 return FALSE;
1456 }
1457
1461
1462 return TRUE;
1463} /* netw_set_crypto_pem */
1464
1473int netw_set_crypto_chain(NETWORK* netw, char* key, char* certificate, char* ca_file) {
1474 __n_assert(netw, return FALSE);
1475 __n_assert(key, return FALSE);
1476 __n_assert(certificate, return FALSE);
1477 __n_assert(ca_file, return FALSE);
1478
1479 if (netw_set_crypto(netw, key, certificate) == FALSE) {
1480 return FALSE;
1481 }
1482
1483 /* Load the full certificate chain */
1484 if (SSL_CTX_use_certificate_chain_file(netw->ctx, certificate) <= 0) {
1485 n_log(LOG_ERR, "Failed to load certificate chain from %s", certificate);
1487 return FALSE;
1488 }
1489
1490 /* Load CA file for verification */
1491 if (SSL_CTX_load_verify_locations(netw->ctx, ca_file, NULL) != 1) {
1492 n_log(LOG_ERR, "Failed to load CA file %s", ca_file);
1494 return FALSE;
1495 }
1496
1497 return TRUE;
1498} /* netw_set_crypto_chain */
1499
1508int netw_set_crypto_chain_pem(NETWORK* netw, const char* key_pem, const char* cert_pem, const char* ca_pem) {
1509 __n_assert(netw, return FALSE);
1510 __n_assert(key_pem, return FALSE);
1511 __n_assert(cert_pem, return FALSE);
1512 __n_assert(ca_pem, return FALSE);
1513
1514 if (netw_set_crypto_pem(netw, key_pem, cert_pem) == FALSE) {
1515 return FALSE;
1516 }
1517
1518 /* Load CA certificate from PEM string into the trust store */
1519 BIO* ca_bio = BIO_new_mem_buf(ca_pem, -1);
1520 if (!ca_bio) {
1521 n_log(LOG_ERR, "Failed to create BIO for CA PEM");
1522 return FALSE;
1523 }
1524
1525 X509_STORE* store = SSL_CTX_get_cert_store(netw->ctx);
1526 if (!store) {
1527 BIO_free(ca_bio);
1528 n_log(LOG_ERR, "Failed to get certificate store from SSL context");
1529 return FALSE;
1530 }
1531
1532 X509* ca_cert = NULL;
1533 int ca_loaded = 0;
1534 while ((ca_cert = PEM_read_bio_X509(ca_bio, NULL, NULL, NULL)) != NULL) {
1535 if (X509_STORE_add_cert(store, ca_cert) != 1) {
1536 n_log(LOG_ERR, "Failed to add CA certificate to store");
1537 X509_free(ca_cert);
1538 BIO_free(ca_bio);
1539 return FALSE;
1540 }
1541 X509_free(ca_cert);
1542 ca_loaded++;
1543 }
1544 BIO_free(ca_bio);
1545
1546 if (ca_loaded == 0) {
1547 n_log(LOG_ERR, "No CA certificates were loaded from PEM string");
1548 return FALSE;
1549 }
1550
1551 /* Also load any extra chain certificates from the cert PEM */
1552 BIO* chain_bio = BIO_new_mem_buf(cert_pem, -1);
1553 if (chain_bio) {
1554 /* Skip the first certificate (already loaded as the leaf) */
1555 X509* skip_cert = PEM_read_bio_X509(chain_bio, NULL, NULL, NULL);
1556 if (skip_cert) {
1557 X509_free(skip_cert);
1558 }
1559 /* Load remaining certificates as chain intermediates */
1560 X509* chain_cert = NULL;
1561 while ((chain_cert = PEM_read_bio_X509(chain_bio, NULL, NULL, NULL)) != NULL) {
1562 if (SSL_CTX_add_extra_chain_cert(netw->ctx, chain_cert) != 1) {
1563 n_log(LOG_ERR, "Failed to add chain certificate");
1564 X509_free(chain_cert);
1565 }
1566 /* Note: SSL_CTX_add_extra_chain_cert takes ownership, so no X509_free on success */
1567 }
1568 BIO_free(chain_bio);
1569 }
1570
1571 return TRUE;
1572} /* netw_set_crypto_chain_pem */
1573
1581int netw_ssl_set_ca(NETWORK* netw, const char* ca_file, const char* ca_path) {
1582 __n_assert(netw, return FALSE);
1583 __n_assert(netw->ctx, n_log(LOG_ERR, "SSL context not initialized, call netw_set_crypto first"); return FALSE);
1584
1585 if (!ca_file && !ca_path) {
1586 n_log(LOG_ERR, "At least one of ca_file or ca_path must be specified");
1587 return FALSE;
1588 }
1589
1590 if (SSL_CTX_load_verify_locations(netw->ctx, ca_file, ca_path) != 1) {
1591 n_log(LOG_ERR, "Failed to load CA from file=%s path=%s", _str(ca_file), _str(ca_path));
1592 _netw_capture_error(netw, "Failed to load CA from file=%s path=%s", _str(ca_file), _str(ca_path));
1594 return FALSE;
1595 }
1596
1597 return TRUE;
1598} /* netw_ssl_set_ca */
1599
1607 __n_assert(netw, return FALSE);
1608 __n_assert(netw->ctx, n_log(LOG_ERR, "SSL context not initialized, call netw_set_crypto first"); return FALSE);
1609
1610 if (enable) {
1611 SSL_CTX_set_verify(netw->ctx, SSL_VERIFY_PEER, NULL);
1612 } else {
1613 SSL_CTX_set_verify(netw->ctx, SSL_VERIFY_NONE, NULL);
1614 }
1615
1616 return TRUE;
1617} /* netw_ssl_set_verify */
1618
1626int netw_ssl_set_client_cert(NETWORK* netw, const char* cert_file, const char* key_file) {
1627 __n_assert(netw, return FALSE);
1628 __n_assert(netw->ctx, n_log(LOG_ERR, "SSL context not initialized"); return FALSE);
1629 __n_assert(cert_file, return FALSE);
1630
1631 if (SSL_CTX_use_certificate_file(netw->ctx, cert_file, SSL_FILETYPE_PEM) != 1) {
1632 n_log(LOG_ERR, "Failed to load client certificate from %s", cert_file);
1633 _netw_capture_error(netw, "Failed to load client certificate from %s", cert_file);
1635 return FALSE;
1636 }
1637
1638 const char* kf = key_file ? key_file : cert_file;
1639 if (SSL_CTX_use_PrivateKey_file(netw->ctx, kf, SSL_FILETYPE_PEM) != 1) {
1640 n_log(LOG_ERR, "Failed to load client private key from %s", kf);
1641 _netw_capture_error(netw, "Failed to load client private key from %s", kf);
1643 return FALSE;
1644 }
1645
1646 if (SSL_CTX_check_private_key(netw->ctx) != 1) {
1647 n_log(LOG_ERR, "Client certificate and private key do not match");
1648 _netw_capture_error(netw, "Client certificate and private key do not match");
1650 return FALSE;
1651 }
1652
1653 return TRUE;
1654} /* netw_ssl_set_client_cert */
1655
1656#endif /* HAVE_OPENSSL */
1657
1670int netw_connect_ex(NETWORK** netw, char* host, char* port, size_t send_list_limit, size_t recv_list_limit, int ip_version, char* ssl_key_file, char* ssl_cert_file) {
1671 // kill compilation warning when there is no openssl
1672 (void)ssl_key_file;
1673 (void)ssl_cert_file;
1674 int error = 0, net_status = 0;
1675 char* errmsg = NULL;
1676
1677 /*do not work over an already used netw*/
1678 if ((*netw)) {
1679 n_log(LOG_ERR, "Unable to allocate (*netw), already existing. You must use empty NETWORK *structs.");
1680 return FALSE;
1681 }
1682
1683 /*creating array*/
1684 (*netw) = netw_new(send_list_limit, recv_list_limit);
1685 __n_assert(netw && (*netw), return FALSE);
1686
1687 /*checking WSA when under windows*/
1688 if (netw_init_wsa(1, 2, 2) == FALSE) {
1689 n_log(LOG_ERR, "Unable to load WSA dll's");
1691 return FALSE;
1692 }
1693
1694 /* choose ip version */
1695 if (ip_version == NETWORK_IPV4) {
1696 (*netw)->link.hints.ai_family = AF_INET; /* Allow IPv4 */
1697 } else if (ip_version == NETWORK_IPV6) {
1698 (*netw)->link.hints.ai_family = AF_INET6; /* Allow IPv6 */
1699 } else {
1700 /* NETWORK_ALL or unknown value */
1701 (*netw)->link.hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
1702 }
1703
1704 (*netw)->link.hints.ai_socktype = SOCK_STREAM;
1705 (*netw)->link.hints.ai_protocol = IPPROTO_TCP;
1706 (*netw)->link.hints.ai_flags = AI_PASSIVE;
1707 (*netw)->link.hints.ai_canonname = NULL;
1708 (*netw)->link.hints.ai_next = NULL;
1709
1710 /* Note: on some system, i.e Solaris, it WILL show leak in getaddrinfo.
1711 * Testing it inside a 1,100 loop showed not effect on the amount of leaked
1712 * memory */
1713 error = getaddrinfo(host, port, &(*netw)->link.hints, &(*netw)->link.rhost);
1714 if (error != 0) {
1715 _netw_capture_error(*netw, "Error when resolving %s:%s getaddrinfo: %s", host, port, gai_strerror(error));
1716 n_log(LOG_ERR, "Error when resolving %s:%s getaddrinfo: %s", host, port, gai_strerror(error));
1717 _netw_capture_connect_error("DNS resolution failed for %s:%s: %s", host, port, gai_strerror(error));
1719 return FALSE;
1720 }
1721 (*netw)->addr_infos_loaded = 1;
1722 Malloc((*netw)->link.ip, char, 64);
1723 __n_assert((*netw)->link.ip, netw_close(netw); return FALSE);
1724
1725 /* getaddrinfo() returns a list of address structures. Try each address until we successfully connect. If socket or connect fails, we close the socket and try the next address. */
1726 struct addrinfo* rp = NULL;
1727 for (rp = (*netw)->link.rhost; rp != NULL; rp = rp->ai_next) {
1728 SOCKET sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
1729 if (sock == INVALID_SOCKET) {
1730 error = neterrno;
1731 errmsg = netstrerror(error);
1732 n_log(LOG_ERR, "Error while trying to make a socket: %s", _str(errmsg));
1733 FreeNoLog(errmsg);
1734 continue;
1735 }
1736
1737 (*netw)->link.sock = sock;
1738
1739 net_status = connect(sock, rp->ai_addr, (socklen_t)rp->ai_addrlen);
1740 if (net_status == -1) {
1741 error = neterrno;
1742 errmsg = netstrerror(error);
1743 n_log(LOG_INFO, "connecting to %s:%s : %s", host, port, _str(errmsg));
1744 FreeNoLog(errmsg);
1745 closesocket(sock);
1746 (*netw)->link.sock = INVALID_SOCKET;
1747 continue;
1748 } else {
1749 /*storing connected port and ip address*/
1750 if (!inet_ntop(rp->ai_family, get_in_addr(rp->ai_addr), (*netw)->link.ip, 64)) {
1751 error = neterrno;
1752 errmsg = netstrerror(error);
1753 n_log(LOG_ERR, "inet_ntop: %p , %s", rp, _str(errmsg));
1754 FreeNoLog(errmsg);
1755 }
1756 break; /* Success */
1757 }
1758 }
1759 if (rp == NULL) {
1760 /* No address succeeded */
1761 _netw_capture_error(*netw, "Couldn't connect to %s:%s : no address succeeded", host, port);
1762 n_log(LOG_ERR, "Couldn't connect to %s:%s : no address succeeded", host, port);
1763 _netw_capture_connect_error("TCP connect failed for %s:%s: no address succeeded", host, port);
1765 return FALSE;
1766 }
1767
1768 (*netw)->link.port = strdup(port);
1769 __n_assert((*netw)->link.port, netw_close(netw); return FALSE);
1770
1771 if (ssl_key_file && ssl_cert_file) {
1772#ifdef HAVE_OPENSSL
1773 if (netw_set_crypto((*netw), ssl_key_file, ssl_cert_file) == FALSE) {
1774 /* could not initialize SSL */
1775 n_log(LOG_ERR, "couldn't initialize SSL !");
1776 _netw_capture_error(*netw, "SSL initialization failed");
1778 return FALSE;
1779 }
1780
1781 (*netw)->ssl = SSL_new((*netw)->ctx);
1782 SSL_set_fd((*netw)->ssl, (int)(*netw)->link.sock);
1783
1784 // Perform SSL Handshake
1785 if (SSL_connect((*netw)->ssl) <= 0) {
1786 /* could not connect with SSL */
1787 n_log(LOG_ERR, "SSL Handshake error !");
1788 _netw_capture_error(*netw, "SSL handshake failed for %s:%s", (*netw)->link.ip, (*netw)->link.port);
1790 return FALSE;
1791 }
1792 n_log(LOG_DEBUG, "SSL-Connected to %s:%s", (*netw)->link.ip, (*netw)->link.port);
1793#else
1794 _netw_capture_error(*netw, "%s:%s trying to configure SSL but application was compiled without SSL support !", (*netw)->link.ip, (*netw)->link.port);
1795 n_log(LOG_ERR, "%s:%s trying to configure SSL but application was compiled without SSL support !", (*netw)->link.ip, (*netw)->link.port);
1796#endif
1797 } else {
1798 n_log(LOG_DEBUG, "Connected to %s:%s", (*netw)->link.ip, (*netw)->link.port);
1799 }
1800
1802
1803 return TRUE;
1804} /* netw_connect_ex(...)*/
1805
1814int netw_connect(NETWORK** netw, char* host, char* port, int ip_version) {
1815 n_log(LOG_INFO, "Trying to connect to %s : %s", _str(host), _str(port));
1816 return netw_connect_ex(netw, host, port, MAX_LIST_ITEMS, MAX_LIST_ITEMS, ip_version, NULL, NULL);
1817} /* netw_connect() */
1818
1819#ifdef HAVE_OPENSSL
1830int netw_ssl_connect(NETWORK** netw, char* host, char* port, int ip_version, char* ssl_key_file, char* ssl_cert_file) {
1831 n_log(LOG_INFO, "Trying to connect to %s : %s", _str(host), _str(port));
1832 return netw_connect_ex(netw, host, port, MAX_LIST_ITEMS, MAX_LIST_ITEMS, ip_version, ssl_key_file, ssl_cert_file);
1833} /* netw_connect() */
1834
1849int netw_ssl_connect_client(NETWORK** netw, char* host, char* port, int ip_version) {
1850 __n_assert(netw, return FALSE);
1851 /* TCP connect first (no SSL params) */
1852 if (netw_connect_ex(netw, host, port, MAX_LIST_ITEMS, MAX_LIST_ITEMS, ip_version, NULL, NULL) == FALSE) {
1853 return FALSE;
1854 }
1855 /* Create SSL context for client */
1857#if OPENSSL_VERSION_NUMBER >= 0x10100000L
1858 (*netw)->method = TLS_client_method();
1859#else
1860 (*netw)->method = TLSv1_2_client_method();
1861#endif
1862 (*netw)->ctx = SSL_CTX_new((*netw)->method);
1863 if (!(*netw)->ctx) {
1864 netw_ssl_print_errors((*netw)->link.sock);
1866 return FALSE;
1867 }
1868 /* Load default system CA paths */
1869 SSL_CTX_set_default_verify_paths((*netw)->ctx);
1870 /* Set send/recv to SSL variants */
1871 (*netw)->send_data = &send_ssl_data;
1872 (*netw)->recv_data = &recv_ssl_data;
1873 (*netw)->crypto_algo = NETW_ENCRYPT_OPENSSL;
1874 return TRUE;
1875} /* netw_ssl_connect_client */
1876
1888int netw_ssl_do_handshake(NETWORK* netw, const char* sni_hostname) {
1889 __n_assert(netw, return FALSE);
1890 __n_assert(netw->ctx, n_log(LOG_ERR, "netw_ssl_do_handshake: no SSL context"); return FALSE);
1891
1892 netw->ssl = SSL_new(netw->ctx);
1893 if (!netw->ssl) {
1894 _netw_capture_error(netw, "SSL_new failed");
1896 return FALSE;
1897 }
1898 SSL_set_fd(netw->ssl, (int)netw->link.sock);
1899 if (sni_hostname) {
1900 SSL_set_tlsext_host_name(netw->ssl, sni_hostname);
1901 }
1902 if (SSL_connect(netw->ssl) <= 0) {
1903 n_log(LOG_ERR, "SSL handshake failed");
1904 unsigned long err = ERR_peek_error();
1905 _netw_capture_error(netw, "SSL handshake failed for %s:%s: %s",
1907 err ? ERR_reason_error_string(err) : "unknown error");
1909 return FALSE;
1910 }
1911 n_log(LOG_DEBUG, "SSL handshake completed with %s:%s", _str(netw->link.ip), _str(netw->link.port));
1912 return TRUE;
1913} /* netw_ssl_do_handshake */
1914
1915#endif
1916
1924int netw_get_state(NETWORK* netw, uint32_t* state, int* thr_engine_status) {
1925 if (netw) {
1926 /* use eventbolt: netw_set() writes state/threaded_engine_status under eventbolt */
1927 pthread_mutex_lock(&netw->eventbolt);
1928 if (state)
1929 (*state) = netw_atomic_read_state(netw);
1930 if (thr_engine_status)
1931 (*thr_engine_status) = netw->threaded_engine_status;
1932 pthread_mutex_unlock(&netw->eventbolt);
1933 return TRUE;
1934 } else {
1935 n_log(LOG_ERR, "Can't get status of a NULL network");
1936 }
1937 return FALSE;
1938} /*netw_get_state() */
1939
1946int netw_set(NETWORK* netw, int flag) {
1947 __n_assert(netw, return FALSE);
1948 if (flag & NETW_EMPTY_SENDBUF) {
1949 pthread_mutex_lock(&netw->sendbolt);
1950 if (netw->send_buf)
1952 pthread_mutex_unlock(&netw->sendbolt);
1953 };
1954 if (flag & NETW_EMPTY_RECVBUF) {
1955 pthread_mutex_lock(&netw->recvbolt);
1956 if (netw->recv_buf)
1958 pthread_mutex_unlock(&netw->recvbolt);
1959 }
1960 if (flag & NETW_DESTROY_SENDBUF) {
1961 pthread_mutex_lock(&netw->sendbolt);
1962 if (netw->send_buf)
1964 pthread_mutex_unlock(&netw->sendbolt);
1965 };
1966 if (flag & NETW_DESTROY_RECVBUF) {
1967 pthread_mutex_lock(&netw->recvbolt);
1968 if (netw->recv_buf)
1970 pthread_mutex_unlock(&netw->recvbolt);
1971 }
1972 pthread_mutex_lock(&netw->eventbolt);
1973 if (flag & NETW_CLIENT) {
1975 }
1976 if (flag & NETW_SERVER) {
1978 }
1979 if (flag & NETW_RUN) {
1981 }
1982 if (flag & NETW_EXITED) {
1984 }
1985 if (flag & NETW_ERROR) {
1987 }
1988 if (flag & NETW_EXIT_ASKED) {
1990 }
1991 if (flag & NETW_THR_ENGINE_STARTED) {
1993 }
1994 if (flag & NETW_THR_ENGINE_STOPPED) {
1996 }
1997 pthread_mutex_unlock(&netw->eventbolt);
1998
1999 sem_post(&netw->send_blocker);
2000
2001 return TRUE;
2002} /* netw_set(...) */
2003
2010int deplete_send_buffer(int fd, int timeout) {
2011#if defined(__linux__)
2012 int outstanding = 0;
2013 if (timeout <= 0) {
2014 return 0;
2015 }
2016 for (int it = 0; it < timeout; it += 100) {
2017 outstanding = 0;
2018 if (ioctl(fd, SIOCOUTQ, &outstanding) == -1) {
2019 int error = errno;
2020 n_log(LOG_ERR, "ioctl SIOCOUTQ returned -1: %s for socket %d", strerror(error), fd);
2021 return -1;
2022 }
2023 if (!outstanding) {
2024 break;
2025 }
2026 usleep(100000);
2027 }
2028 return outstanding;
2029#else
2030 (void)fd;
2031 (void)timeout;
2032 return 0;
2033#endif
2034}
2035
2042 __n_assert(netw && (*netw), return FALSE);
2043 uint32_t state = 0;
2044 int thr_engine_status = 0;
2045 int nb_running = 0;
2046
2047 if ((*netw)->deplete_queues_timeout > 0) {
2048 /* use iteration count to avoid integer overflow with large timeouts */
2049 int max_iterations = (*netw)->deplete_queues_timeout * 10; /* each iteration ~100ms */
2050 netw_get_state((*netw), &state, &thr_engine_status);
2051 if (thr_engine_status == NETW_THR_ENGINE_STARTED) {
2052 int it = 0;
2053 do {
2054 pthread_mutex_lock(&(*netw)->eventbolt);
2055 nb_running = (*netw)->nb_running_threads;
2056 pthread_mutex_unlock(&(*netw)->eventbolt);
2057 usleep(100000);
2058 it++;
2059 } while (nb_running > 0 && it < max_iterations);
2060 netw_get_state((*netw), &state, &thr_engine_status);
2061 if (it >= max_iterations && nb_running > 0) {
2062 n_log(LOG_ERR, "netw %d: %d threads are still running after %d seconds, netw is in state %s (%" PRIu32 ")", (*netw)->link.sock, nb_running, (*netw)->deplete_queues_timeout, N_ENUM_ENTRY(__netw_code_type, toString)(state), state);
2063 }
2064 }
2065 }
2066
2067 if ((*netw)->link.sock != INVALID_SOCKET) {
2068 int remaining = deplete_send_buffer((int)(*netw)->link.sock, (*netw)->deplete_socket_timeout);
2069 // cppcheck-suppress knownConditionTrueFalse -- remaining can be > 0 on Linux (SIOCOUTQ)
2070 if (remaining > 0) {
2071 n_log(LOG_ERR, "socket %d (%s:%s) %d octets still in send buffer before closing after a wait of %d msecs", (*netw)->link.sock, (*netw)->link.ip, (*netw)->link.port, remaining, (*netw)->deplete_socket_timeout);
2072 }
2073 }
2074
2075 list_foreach(node, (*netw)->pools) {
2076 NETWORK_POOL* pool = (NETWORK_POOL*)node->ptr;
2078 }
2079 list_destroy(&(*netw)->pools);
2080
2081 netw_get_state((*netw), &state, &thr_engine_status);
2082 if (thr_engine_status == NETW_THR_ENGINE_STARTED) {
2084 }
2085
2086 /* recompute nb_running after joining threads so the SSL_shutdown
2087 * decision reflects the actual post-join state */
2088 pthread_mutex_lock(&(*netw)->eventbolt);
2089 nb_running = (*netw)->nb_running_threads;
2090 pthread_mutex_unlock(&(*netw)->eventbolt);
2091
2092 if ((*netw)->link.sock != INVALID_SOCKET) {
2093#ifdef HAVE_OPENSSL
2094 if ((*netw)->crypto_algo == NETW_ENCRYPT_OPENSSL) {
2095 if ((*netw)->ssl) {
2096 /* only attempt SSL_shutdown if threads have fully stopped,
2097 * otherwise SSL operations may race with send/recv threads */
2098 if (nb_running == 0) {
2099 int shutdown_res = SSL_shutdown((*netw)->ssl);
2100 if (shutdown_res == 0) {
2101 /* try again to complete bidirectional shutdown;
2102 * peer may have already closed (common for HTTP),
2103 * so SYSCALL/SSL errors are non-fatal here */
2104 shutdown_res = SSL_shutdown((*netw)->ssl);
2105 }
2106 if (shutdown_res < 0) {
2107 int err = SSL_get_error((*netw)->ssl, shutdown_res);
2108 if (err != SSL_ERROR_SYSCALL && err != SSL_ERROR_SSL) {
2109 n_log(LOG_ERR, "SSL_shutdown() failed: %d", err);
2110 } else {
2111 n_log(LOG_DEBUG, "SSL_shutdown() peer already closed: %d", err);
2112 }
2113 }
2114 } else {
2115 n_log(LOG_WARNING, "netw %d: forcing bidirectional SSL_shutdown with %d threads still running", (*netw)->link.sock, nb_running);
2116 int shutdown_res = SSL_shutdown((*netw)->ssl);
2117 if (shutdown_res == 0) {
2118 /* send close_notify and wait for peer's close_notify */
2119 shutdown_res = SSL_shutdown((*netw)->ssl);
2120 }
2121 if (shutdown_res < 0) {
2122 int err = SSL_get_error((*netw)->ssl, shutdown_res);
2123 n_log(LOG_ERR, "netw %d: SSL_shutdown() failed with %d threads still running: %d", (*netw)->link.sock, nb_running, err);
2124 }
2125 }
2126 SSL_free((*netw)->ssl);
2127 } else {
2128 if ((*netw)->mode != NETW_SERVER) {
2129 n_log(LOG_ERR, "SSL handle of socket %d was already NULL", (*netw)->link.sock);
2130 } else {
2131 n_log(LOG_DEBUG, "listening socket %d has no SSL handle (expected)", (*netw)->link.sock);
2132 }
2133 }
2134 }
2135#endif
2136
2137 /* inform peer that we have finished */
2138 shutdown((*netw)->link.sock, SHUT_WR);
2139
2140 if ((*netw)->wait_close_timeout > 0) {
2141 /* wait for fin ack using select() to avoid busy-waiting.
2142 * Use iteration count to avoid integer overflow. */
2143 char buffer[4096] = "";
2144 int max_iters = (*netw)->wait_close_timeout * 10; /* each iteration ~100ms */
2145 for (int it = 0; it < max_iters; it++) {
2146 fd_set rfds;
2147 struct timeval tv;
2148 FD_ZERO(&rfds);
2149#pragma GCC diagnostic push
2150#pragma GCC diagnostic ignored "-Wsign-conversion"
2151 FD_SET((*netw)->link.sock, &rfds);
2152#pragma GCC diagnostic pop
2153 tv.tv_sec = 0;
2154 tv.tv_usec = 100000; /* 100ms */
2155 int sel = select((int)(*netw)->link.sock + 1, &rfds, NULL, NULL, &tv);
2156 if (sel > 0) {
2157 ssize_t res = recv((*netw)->link.sock, buffer, 4096, NETFLAGS);
2158 if (!res)
2159 break;
2160 if (res < 0) {
2161 int error = neterrno;
2162 if (error != ENOTCONN && error != EINTR && error != ECONNRESET
2163#ifdef __windows__
2164 && error != WSAENOTCONN && error != WSAECONNRESET && error != WSAESHUTDOWN && error != WSAEWOULDBLOCK && error != WSAEINTR
2165#endif
2166 ) {
2167 char* errmsg = netstrerror(error);
2168 n_log(LOG_ERR, "read returned error %d when closing socket %d (%s:%s): %s", error, (*netw)->link.sock, _str((*netw)->link.ip), (*netw)->link.port, _str(errmsg));
2169 FreeNoLog(errmsg);
2170 } else {
2171 n_log(LOG_DEBUG, "wait close: connection gracefully closed on socket %d (%s:%s)", (*netw)->link.sock, _str((*netw)->link.ip), (*netw)->link.port);
2172 }
2173 break;
2174 }
2175 } else if (sel < 0) {
2176 /* select error, bail out */
2177 int error = neterrno;
2178 if (error != EINTR
2179#ifdef __windows__
2180 && error != WSAEINTR
2181#endif
2182 ) {
2183 char* errmsg = netstrerror(error);
2184 n_log(LOG_ERR, "select() error on socket %d during close: %s", (*netw)->link.sock, _str(errmsg));
2185 FreeNoLog(errmsg);
2186 break;
2187 }
2188 }
2189 /* sel == 0: timeout, continue waiting */
2190 }
2191 }
2192
2193 /* effectively close socket */
2194 closesocket((*netw)->link.sock);
2195
2196#ifdef HAVE_OPENSSL
2197 /* clean openssl state */
2198 if ((*netw)->crypto_algo == NETW_ENCRYPT_OPENSSL) {
2199 if ((*netw)->ctx) {
2200 SSL_CTX_free((*netw)->ctx);
2201 } else {
2202 n_log(LOG_ERR, "SSL context of socket %d was already NULL", (*netw)->link.sock);
2203 }
2204 }
2205 n_log(LOG_DEBUG, "socket %d closed", (*netw)->link.sock);
2206 FreeNoLog((*netw)->key);
2207 FreeNoLog((*netw)->certificate);
2208#endif
2209 }
2210
2211 FreeNoLog((*netw)->link.ip);
2212 FreeNoLog((*netw)->link.port);
2213
2214 if ((*netw)->link.rhost) {
2215 freeaddrinfo((*netw)->link.rhost);
2216 }
2217
2218 /*list freeing*/
2220
2221 pthread_mutex_destroy(&(*netw)->recvbolt);
2222 pthread_mutex_destroy(&(*netw)->sendbolt);
2223 pthread_mutex_destroy(&(*netw)->eventbolt);
2224 sem_destroy(&(*netw)->send_blocker);
2225
2226 Free((*netw));
2227
2228 return TRUE;
2229} /* netw_close(...)*/
2230
2240int netw_make_listening(NETWORK** netw, char* addr, char* port, int nbpending, int ip_version) {
2241 __n_assert(port, return FALSE);
2242
2243 int error = 0;
2244 char* errmsg = NULL;
2245
2246 if (*netw) {
2247 n_log(LOG_ERR, "Cannot use an allocated network. Please pass a NULL network to modify");
2248 return FALSE;
2249 }
2250
2251 /*checking WSA when under windows*/
2252 if (netw_init_wsa(1, 2, 2) == FALSE) {
2253 n_log(LOG_ERR, "Unable to load WSA dll's");
2254 return FALSE;
2255 }
2256
2258 __n_assert((*netw), return FALSE);
2259 (*netw)->link.port = strdup(port);
2260 /*creating array*/
2261 if (ip_version == NETWORK_IPV4) {
2262 (*netw)->link.hints.ai_family = AF_INET; /* Allow IPv4 */
2263 } else if (ip_version == NETWORK_IPV6) {
2264 (*netw)->link.hints.ai_family = AF_INET6; /* Allow IPv6 */
2265 } else {
2266 /* NETWORK_ALL or unknown value */
2267 (*netw)->link.hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
2268 }
2269 if (!addr) {
2270 (*netw)->link.hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
2271 }
2272 (*netw)->link.hints.ai_socktype = SOCK_STREAM;
2273 (*netw)->link.hints.ai_protocol = IPPROTO_TCP;
2274 (*netw)->link.hints.ai_canonname = NULL;
2275 (*netw)->link.hints.ai_next = NULL;
2276
2277 error = getaddrinfo(addr, port, &(*netw)->link.hints, &(*netw)->link.rhost);
2278 if (error != 0) {
2279 _netw_capture_error(*netw, "Error when resolving %s:%s getaddrinfo: %s", _str(addr), port, gai_strerror(error));
2280 n_log(LOG_ERR, "Error when resolving %s:%s getaddrinfo: %s", _str(addr), port, gai_strerror(error));
2282 return FALSE;
2283 }
2284 (*netw)->addr_infos_loaded = 1;
2285
2286 /* getaddrinfo() returns a list of address structures.
2287 Try each address until we successfully connect(2).
2288 If socket(2) (or connect(2)) fails, we (close the socket
2289 and) try the next address. */
2290 struct addrinfo* rp = NULL;
2291 for (rp = (*netw)->link.rhost; rp != NULL; rp = rp->ai_next) {
2292 (*netw)->link.sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
2293 if ((*netw)->link.sock == INVALID_SOCKET) {
2294 error = neterrno;
2295 errmsg = netstrerror(error);
2296 n_log(LOG_ERR, "Error while trying to make a socket: %s", _str(errmsg));
2297 FreeNoLog(errmsg);
2298 continue;
2299 }
2300 netw_setsockopt((*netw), SO_REUSEADDR, 1);
2301 // netw_setsockopt( (*netw), SO_REUSEPORT, 1 );
2302 if (bind((*netw)->link.sock, rp->ai_addr, (socklen_t)rp->ai_addrlen) == 0) {
2303 char* ip = NULL;
2304 Malloc(ip, char, 64);
2305 if (!ip) {
2306 n_log(LOG_ERR, "Error allocating 64 bytes for ip");
2308 return FALSE;
2309 }
2310 if (!inet_ntop(rp->ai_family, get_in_addr(rp->ai_addr), ip, 64)) {
2311 error = neterrno;
2312 errmsg = netstrerror(error);
2313 n_log(LOG_ERR, "inet_ntop: %p , %s", (*netw)->link.raddr, _str(errmsg));
2314 FreeNoLog(errmsg);
2315 }
2316 /*n_log( LOG_DEBUG, "Socket %d successfully binded to %s %s", (*netw) -> link . sock, _str( ip ) , _str( port ) );*/
2317 (*netw)->link.ip = ip;
2318 break; /* Success */
2319 }
2320 error = neterrno;
2321 errmsg = netstrerror(error);
2322 _netw_capture_error(*netw, "Error from bind() on port %s neterrno: %s", port, errmsg);
2323 n_log(LOG_ERR, "Error from bind() on port %s neterrno: %s", port, errmsg);
2324 FreeNoLog(errmsg);
2325 closesocket((*netw)->link.sock);
2326 }
2327 if (rp == NULL) {
2328 /* No address succeeded */
2329 _netw_capture_error(*netw, "Couldn't get a socket for listening on port %s", port);
2330 n_log(LOG_ERR, "Couldn't get a socket for listening on port %s", port);
2332 return FALSE;
2333 }
2334
2335 /* nb_pending connections*/
2336 (*netw)->nb_pending = nbpending;
2337 if (listen((*netw)->link.sock, (*netw)->nb_pending) != 0) {
2338 error = neterrno;
2339 errmsg = netstrerror(error);
2340 _netw_capture_error(*netw, "listen() failed on port %s: %s", port, _str(errmsg));
2341 n_log(LOG_ERR, "listen() failed on port %s: %s", port, _str(errmsg));
2342 FreeNoLog(errmsg);
2344 return FALSE;
2345 }
2346
2348
2349 return TRUE;
2350} /* netw_make_listening(...)*/
2351
2360int netw_bind_udp(NETWORK** netw, char* addr, char* port, int ip_version) {
2361 __n_assert(port, return FALSE);
2362
2363 int error = 0;
2364 char* errmsg = NULL;
2365
2366 if (*netw) {
2367 n_log(LOG_ERR, "Cannot use an allocated network. Please pass a NULL network to modify");
2368 return FALSE;
2369 }
2370
2371 /*checking WSA when under windows*/
2372 if (netw_init_wsa(1, 2, 2) == FALSE) {
2373 n_log(LOG_ERR, "Unable to load WSA dll's");
2374 return FALSE;
2375 }
2376
2378 __n_assert((*netw), return FALSE);
2379 (*netw)->link.port = strdup(port);
2380 (*netw)->transport_type = NETWORK_UDP;
2381
2382 /* choose ip version */
2383 if (ip_version == NETWORK_IPV4) {
2384 (*netw)->link.hints.ai_family = AF_INET;
2385 } else if (ip_version == NETWORK_IPV6) {
2386 (*netw)->link.hints.ai_family = AF_INET6;
2387 } else {
2388 (*netw)->link.hints.ai_family = AF_UNSPEC;
2389 }
2390 if (!addr) {
2391 (*netw)->link.hints.ai_flags = AI_PASSIVE;
2392 }
2393 (*netw)->link.hints.ai_socktype = SOCK_DGRAM;
2394 (*netw)->link.hints.ai_protocol = IPPROTO_UDP;
2395 (*netw)->link.hints.ai_canonname = NULL;
2396 (*netw)->link.hints.ai_next = NULL;
2397
2398 error = getaddrinfo(addr, port, &(*netw)->link.hints, &(*netw)->link.rhost);
2399 if (error != 0) {
2400 _netw_capture_error(*netw, "Error when resolving %s:%s getaddrinfo: %s", _str(addr), port, gai_strerror(error));
2401 n_log(LOG_ERR, "Error when resolving %s:%s getaddrinfo: %s", _str(addr), port, gai_strerror(error));
2403 return FALSE;
2404 }
2405 (*netw)->addr_infos_loaded = 1;
2406
2407 struct addrinfo* rp = NULL;
2408 for (rp = (*netw)->link.rhost; rp != NULL; rp = rp->ai_next) {
2409 (*netw)->link.sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
2410 if ((*netw)->link.sock == INVALID_SOCKET) {
2411 error = neterrno;
2412 errmsg = netstrerror(error);
2413 n_log(LOG_ERR, "Error while trying to make a UDP socket: %s", _str(errmsg));
2414 FreeNoLog(errmsg);
2415 continue;
2416 }
2417 netw_setsockopt((*netw), SO_REUSEADDR, 1);
2418 if (bind((*netw)->link.sock, rp->ai_addr, (socklen_t)rp->ai_addrlen) == 0) {
2419 char* ip = NULL;
2420 Malloc(ip, char, 64);
2421 if (!ip) {
2422 n_log(LOG_ERR, "Error allocating 64 bytes for ip");
2424 return FALSE;
2425 }
2426 if (!inet_ntop(rp->ai_family, get_in_addr(rp->ai_addr), ip, 64)) {
2427 error = neterrno;
2428 errmsg = netstrerror(error);
2429 n_log(LOG_ERR, "inet_ntop: %p , %s", (*netw)->link.raddr, _str(errmsg));
2430 FreeNoLog(errmsg);
2431 }
2432 (*netw)->link.ip = ip;
2433 break; /* Success */
2434 }
2435 error = neterrno;
2436 errmsg = netstrerror(error);
2437 _netw_capture_error(*netw, "Error from bind() on UDP port %s neterrno: %s", port, errmsg);
2438 n_log(LOG_ERR, "Error from bind() on UDP port %s neterrno: %s", port, errmsg);
2439 FreeNoLog(errmsg);
2440 closesocket((*netw)->link.sock);
2441 }
2442 if (rp == NULL) {
2443 _netw_capture_error(*netw, "Couldn't get a socket for UDP binding on port %s", port);
2444 n_log(LOG_ERR, "Couldn't get a socket for UDP binding on port %s", port);
2446 return FALSE;
2447 }
2448
2449 /* set send/recv to UDP functions */
2450 (*netw)->send_data = &send_udp_data;
2451 (*netw)->recv_data = &recv_udp_data;
2452
2454
2455 n_log(LOG_DEBUG, "UDP socket bound to %s:%s", _str((*netw)->link.ip), port);
2456
2457 return TRUE;
2458} /* netw_bind_udp(...)*/
2459
2468int netw_connect_udp(NETWORK** netw, char* host, char* port, int ip_version) {
2469 int error = 0;
2470 char* errmsg = NULL;
2471
2472 if ((*netw)) {
2473 n_log(LOG_ERR, "Unable to allocate (*netw), already existing. You must use empty NETWORK *structs.");
2474 return FALSE;
2475 }
2476
2478 __n_assert(netw && (*netw), return FALSE);
2479 (*netw)->transport_type = NETWORK_UDP;
2480
2481 if (netw_init_wsa(1, 2, 2) == FALSE) {
2482 n_log(LOG_ERR, "Unable to load WSA dll's");
2484 return FALSE;
2485 }
2486
2487 /* choose ip version */
2488 if (ip_version == NETWORK_IPV4) {
2489 (*netw)->link.hints.ai_family = AF_INET;
2490 } else if (ip_version == NETWORK_IPV6) {
2491 (*netw)->link.hints.ai_family = AF_INET6;
2492 } else {
2493 (*netw)->link.hints.ai_family = AF_UNSPEC;
2494 }
2495
2496 (*netw)->link.hints.ai_socktype = SOCK_DGRAM;
2497 (*netw)->link.hints.ai_protocol = IPPROTO_UDP;
2498 (*netw)->link.hints.ai_flags = AI_PASSIVE;
2499 (*netw)->link.hints.ai_canonname = NULL;
2500 (*netw)->link.hints.ai_next = NULL;
2501
2502 error = getaddrinfo(host, port, &(*netw)->link.hints, &(*netw)->link.rhost);
2503 if (error != 0) {
2504 _netw_capture_error(*netw, "Error when resolving %s:%s getaddrinfo: %s", host, port, gai_strerror(error));
2505 n_log(LOG_ERR, "Error when resolving %s:%s getaddrinfo: %s", host, port, gai_strerror(error));
2507 return FALSE;
2508 }
2509 (*netw)->addr_infos_loaded = 1;
2510 Malloc((*netw)->link.ip, char, 64);
2511 __n_assert((*netw)->link.ip, netw_close(netw); return FALSE);
2512
2513 struct addrinfo* rp = NULL;
2514 for (rp = (*netw)->link.rhost; rp != NULL; rp = rp->ai_next) {
2515 SOCKET sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
2516 if (sock == INVALID_SOCKET) {
2517 error = neterrno;
2518 errmsg = netstrerror(error);
2519 n_log(LOG_ERR, "Error while trying to make a UDP socket: %s", _str(errmsg));
2520 FreeNoLog(errmsg);
2521 continue;
2522 }
2523
2524 (*netw)->link.sock = sock;
2525
2526 /* connect() on UDP sets the default destination for send/recv */
2527 if (connect(sock, rp->ai_addr, (socklen_t)rp->ai_addrlen) == -1) {
2528 error = neterrno;
2529 errmsg = netstrerror(error);
2530 n_log(LOG_INFO, "UDP connecting to %s:%s : %s", host, port, _str(errmsg));
2531 FreeNoLog(errmsg);
2532 closesocket(sock);
2533 (*netw)->link.sock = INVALID_SOCKET;
2534 continue;
2535 } else {
2536 if (!inet_ntop(rp->ai_family, get_in_addr(rp->ai_addr), (*netw)->link.ip, 64)) {
2537 error = neterrno;
2538 errmsg = netstrerror(error);
2539 n_log(LOG_ERR, "inet_ntop: %p , %s", rp, _str(errmsg));
2540 FreeNoLog(errmsg);
2541 }
2542 break; /* Success */
2543 }
2544 }
2545 if (rp == NULL) {
2546 _netw_capture_error(*netw, "Couldn't connect UDP to %s:%s : no address succeeded", host, port);
2547 n_log(LOG_ERR, "Couldn't connect UDP to %s:%s : no address succeeded", host, port);
2549 return FALSE;
2550 }
2551
2552 (*netw)->link.port = strdup(port);
2553 __n_assert((*netw)->link.port, netw_close(netw); return FALSE);
2554
2555 /* set send/recv to UDP functions */
2556 (*netw)->send_data = &send_udp_data;
2557 (*netw)->recv_data = &recv_udp_data;
2558
2560
2561 n_log(LOG_DEBUG, "UDP-Connected to %s:%s", (*netw)->link.ip, (*netw)->link.port);
2562
2563 return TRUE;
2564} /* netw_connect_udp(...)*/
2565
2573ssize_t send_udp_data(void* netw, char* buf, uint32_t n) {
2575 __n_assert(buf, return NETW_SOCKET_ERROR);
2576
2577 SOCKET s = ((NETWORK*)netw)->link.sock;
2578 int error = 0;
2579 char* errmsg = NULL;
2580
2581 if (n == 0) {
2582 _netw_capture_error((NETWORK*)netw, "Send of 0 is unsupported.");
2583 n_log(LOG_ERR, "Send of 0 is unsupported.");
2584 return NETW_SOCKET_ERROR;
2585 }
2586
2587 ssize_t bs = send(s, buf, NETW_BUFLEN_CAST(n), NETFLAGS);
2588 error = neterrno;
2589 if (bs < 0) {
2590 if (error == ECONNRESET || error == ENOTCONN
2591#ifdef __windows__
2592 || error == WSAECONNRESET || error == WSAENOTCONN
2593#endif
2594 ) {
2595 n_log(LOG_DEBUG, "UDP socket %d disconnected !", s);
2597 }
2598 errmsg = netstrerror(error);
2599 _netw_capture_error((NETWORK*)netw, "UDP Socket %d send Error: %zd , %s", s, bs, _str(errmsg));
2600 n_log(LOG_ERR, "UDP Socket %d send Error: %zd , %s", s, bs, _str(errmsg));
2601 FreeNoLog(errmsg);
2602 return NETW_SOCKET_ERROR;
2603 }
2604 return bs;
2605} /*send_udp_data(...)*/
2606
2614ssize_t recv_udp_data(void* netw, char* buf, uint32_t n) {
2616 __n_assert(buf, return NETW_SOCKET_ERROR);
2617
2618 SOCKET s = ((NETWORK*)netw)->link.sock;
2619
2620 if (n == 0) {
2621 _netw_capture_error((NETWORK*)netw, "Recv of 0 is unsupported.");
2622 n_log(LOG_ERR, "Recv of 0 is unsupported.");
2623 return NETW_SOCKET_ERROR;
2624 }
2625
2626 ssize_t br = recv(s, buf, NETW_BUFLEN_CAST(n), NETFLAGS);
2627 int error = neterrno;
2628 if (br < 0) {
2629 char* errmsg = netstrerror(error);
2630 _netw_capture_error((NETWORK*)netw, "UDP socket %d recv returned %zd, error: %s", s, br, _str(errmsg));
2631 n_log(LOG_ERR, "UDP socket %d recv returned %zd, error: %s", s, br, _str(errmsg));
2632 FreeNoLog(errmsg);
2633 return NETW_SOCKET_ERROR;
2634 }
2635 if (br == 0) {
2636 n_log(LOG_DEBUG, "UDP socket %d: zero-length recv", s);
2637 }
2638 return br;
2639} /*recv_udp_data(...)*/
2640
2650ssize_t netw_udp_sendto(NETWORK* netw, char* buf, uint32_t n, struct sockaddr* dest_addr, socklen_t dest_len) {
2652 __n_assert(buf, return NETW_SOCKET_ERROR);
2653 __n_assert(dest_addr, return NETW_SOCKET_ERROR);
2654
2655 if (n == 0) {
2656 _netw_capture_error(netw, "Sendto of 0 is unsupported.");
2657 n_log(LOG_ERR, "Sendto of 0 is unsupported.");
2658 return NETW_SOCKET_ERROR;
2659 }
2660
2661 ssize_t bs = sendto(netw->link.sock, buf, NETW_BUFLEN_CAST(n), NETFLAGS, dest_addr, dest_len);
2662 if (bs < 0) {
2663 int error = neterrno;
2664 char* errmsg = netstrerror(error);
2665 _netw_capture_error(netw, "UDP socket %d sendto Error: %zd , %s", netw->link.sock, bs, _str(errmsg));
2666 n_log(LOG_ERR, "UDP socket %d sendto Error: %zd , %s", netw->link.sock, bs, _str(errmsg));
2667 FreeNoLog(errmsg);
2668 return NETW_SOCKET_ERROR;
2669 }
2670 return bs;
2671} /*netw_udp_sendto(...)*/
2672
2682ssize_t netw_udp_recvfrom(NETWORK* netw, char* buf, uint32_t n, struct sockaddr* src_addr, socklen_t* src_len) {
2684 __n_assert(buf, return NETW_SOCKET_ERROR);
2685
2686 if (n == 0) {
2687 _netw_capture_error(netw, "Recvfrom of 0 is unsupported.");
2688 n_log(LOG_ERR, "Recvfrom of 0 is unsupported.");
2689 return NETW_SOCKET_ERROR;
2690 }
2691
2692 ssize_t br = recvfrom(netw->link.sock, buf, NETW_BUFLEN_CAST(n), NETFLAGS, src_addr, src_len);
2693 if (br < 0) {
2694 int error = neterrno;
2695 char* errmsg = netstrerror(error);
2696 _netw_capture_error(netw, "UDP socket %d recvfrom returned %zd, error: %s", netw->link.sock, br, _str(errmsg));
2697 n_log(LOG_ERR, "UDP socket %d recvfrom returned %zd, error: %s", netw->link.sock, br, _str(errmsg));
2698 FreeNoLog(errmsg);
2699 return NETW_SOCKET_ERROR;
2700 }
2701 return br;
2702} /*netw_udp_recvfrom(...)*/
2703
2713NETWORK* netw_accept_from_ex(NETWORK* from, size_t send_list_limit, size_t recv_list_limit, int blocking, int* retval) {
2714 SOCKET tmp = INVALID_SOCKET;
2715 int error;
2716 char* errmsg = NULL;
2717
2718#if defined(__linux__) || defined(__sun) || defined(_AIX)
2719 socklen_t sin_size = 0;
2720#else
2721 int sin_size = 0;
2722#endif
2723
2724 NETWORK* netw = NULL;
2725
2726 /*checking WSA when under windows*/
2727 if (netw_init_wsa(1, 2, 2) == FALSE) {
2728 n_log(LOG_ERR, "Unable to load WSA dll's");
2729 return NULL;
2730 }
2731
2732 __n_assert(from, return NULL);
2733
2734 netw = netw_new(send_list_limit, recv_list_limit);
2735
2736 sin_size = sizeof(netw->link.raddr);
2737
2738 if (blocking > 0) {
2739 int secs = blocking / 1000;
2740 int usecs = (blocking % 1000) * 1000;
2741 struct timeval select_timeout = {secs, usecs};
2742
2743 fd_set accept_set;
2744 FD_ZERO(&accept_set);
2745#pragma GCC diagnostic push
2746#pragma GCC diagnostic ignored "-Wsign-conversion"
2747 FD_SET(from->link.sock, &accept_set);
2748#pragma GCC diagnostic pop
2749
2750 int ret = select((int)(from->link.sock + 1), &accept_set, NULL, NULL, &select_timeout);
2751 if (ret == -1) {
2752 error = neterrno;
2753 errmsg = netstrerror(error);
2754 if (retval != NULL)
2755 (*retval) = error;
2756 n_log(LOG_DEBUG, "error on select with timeout %ds (%d.%ds), neterrno: %s", blocking, secs, usecs, _str(errmsg));
2757 FreeNoLog(errmsg);
2758 netw_close(&netw);
2759 return NULL;
2760 } else if (ret == 0) {
2761 /* that one produce waaaay too much logs under a lot of cases */
2762 n_log(LOG_DEBUG, "No connection waiting on %d", from->link.sock);
2763 netw_close(&netw);
2764 return NULL;
2765 }
2766#pragma GCC diagnostic push
2767#pragma GCC diagnostic ignored "-Wsign-conversion"
2768 if (FD_ISSET(from->link.sock, &accept_set)) {
2769#pragma GCC diagnostic pop
2770 // n_log( LOG_DEBUG, "select accept call on %d", from -> link . sock );
2771 tmp = accept(from->link.sock, (struct sockaddr*)&netw->link.raddr, &sin_size);
2772 if (tmp == INVALID_SOCKET) {
2773 error = neterrno;
2774 errmsg = netstrerror(error);
2775 if (retval != NULL)
2776 (*retval) = error;
2777 n_log(LOG_DEBUG, "error accepting on %d, %s", netw->link.sock, _str(errmsg));
2778 FreeNoLog(errmsg);
2779 netw_close(&netw);
2780 return NULL;
2781 }
2782 } else {
2783 _netw_capture_error(from, "FD_ISSET returned false on %d", from->link.sock);
2784 n_log(LOG_ERR, "FD_ISSET returned false on %d", from->link.sock);
2785 netw_close(&netw);
2786 return NULL;
2787 }
2788 netw->link.is_blocking = 0;
2789 } else if (blocking == -1) {
2790 if (from->link.is_blocking != 0) {
2791 netw_set_blocking(from, 0);
2792 }
2793 tmp = accept(from->link.sock, (struct sockaddr*)&netw->link.raddr, &sin_size);
2794 error = neterrno;
2795 if (retval != NULL)
2796 (*retval) = error;
2797 if (tmp == INVALID_SOCKET) {
2798 if (error != EINTR && error != EAGAIN
2799#ifdef __windows__
2800 && error != WSAEWOULDBLOCK
2801#endif
2802 ) {
2803 char* errmsg_nb = netstrerror(error);
2804 // cppcheck-suppress unknownMacro -- SOCKET_SIZE_FORMAT is a platform-specific printf format macro
2805 n_log(LOG_ERR, "accept returned an invalid socket (" SOCKET_SIZE_FORMAT "), neterrno: %s", tmp, _str(errmsg_nb));
2806 FreeNoLog(errmsg_nb);
2807 }
2808 netw_close(&netw);
2809 return NULL;
2810 }
2811 netw->link.is_blocking = 0;
2812 } else {
2813 if (from->link.is_blocking == 0) {
2814 netw_set_blocking(from, 1);
2815 n_log(LOG_DEBUG, "(default) blocking accept call on socket %d", from->link.sock);
2816 }
2817 tmp = accept(from->link.sock, (struct sockaddr*)&netw->link.raddr, &sin_size);
2818 if (tmp == INVALID_SOCKET) {
2819 error = neterrno;
2820 errmsg = netstrerror(error);
2821 if (retval != NULL)
2822 (*retval) = error;
2823 n_log(LOG_DEBUG, "error accepting on socket %d, %s", netw->link.sock, _str(errmsg));
2824 FreeNoLog(errmsg);
2825 netw_close(&netw);
2826 return NULL;
2827 }
2828 netw->link.is_blocking = 1;
2829 }
2830 netw->link.sock = tmp;
2831
2832 /* On Windows and Solaris, the accepted socket inherits the non-blocking
2833 * mode from the listening socket. The send/recv engine expects a
2834 * blocking socket, so force it to blocking here. On Linux this is a
2835 * harmless no-op since accept() always returns a blocking socket. */
2837 netw->link.is_blocking = 1;
2838
2839 netw->link.port = strdup(from->link.port);
2840 Malloc(netw->link.ip, char, 64);
2841 if (!netw->link.ip) {
2842 n_log(LOG_ERR, "Error allocating 64 bytes for ip");
2843 netw_close(&netw);
2844 return NULL;
2845 }
2846 if (!inet_ntop(netw->link.raddr.ss_family, get_in_addr(((struct sockaddr*)&netw->link.raddr)), netw->link.ip, 64)) {
2847 error = neterrno;
2848 errmsg = netstrerror(error);
2849 n_log(LOG_ERR, "inet_ntop: %p , %s", netw->link.raddr, _str(errmsg));
2850 FreeNoLog(errmsg);
2851 netw_close(&netw);
2852 return NULL;
2853 }
2854
2855 netw_setsockopt(netw, SO_REUSEADDR, 1);
2856 // netw_setsockopt( netw, SO_REUSEPORT, 1 );
2857 netw_setsockopt(netw, SO_KEEPALIVE, 1);
2859 // netw_set_blocking(netw, 1);
2860
2861 n_log(LOG_DEBUG, "Connection accepted from %s:%s socket %d", netw->link.ip, netw->link.port, netw->link.sock);
2862
2863#ifdef HAVE_OPENSSL
2864 if (from->crypto_algo == NETW_ENCRYPT_OPENSSL) {
2865 netw->ssl = SSL_new(from->ctx);
2866 SSL_set_fd(netw->ssl, (int)netw->link.sock);
2867
2870
2871 if (SSL_accept(netw->ssl) <= 0) {
2872 error = errno;
2873 _netw_capture_error(netw, "SSL error on %d", netw->link.sock);
2874 n_log(LOG_ERR, "SSL error on %d", netw->link.sock);
2875 if (retval != NULL)
2876 (*retval) = error;
2878 netw_close(&netw);
2879 return NULL;
2880 } else {
2881 n_log(LOG_DEBUG, " socket %d: SSL connection established", netw->link.sock);
2882 }
2883 }
2884#endif
2885
2886 return netw;
2887} /* netw_accept_from_ex(...) */
2888
2895 return netw_accept_from_ex(from, MAX_LIST_ITEMS, MAX_LIST_ITEMS, 0, NULL);
2896} /* network_accept_from( ... ) */
2897
2905 return netw_accept_from_ex(from, MAX_LIST_ITEMS, MAX_LIST_ITEMS, blocking, NULL);
2906} /* network_accept_from( ... ) */
2907
2915 __n_assert(netw, return FALSE);
2916 __n_assert(msg, return FALSE);
2917 __n_assert(msg->data, return FALSE);
2918
2919 if (msg->length == 0) {
2920 _netw_capture_error(netw, "Empty messages are not supported. msg(%p)->lenght=%d", msg, msg->length);
2921 n_log(LOG_ERR, "Empty messages are not supported. msg(%p)->lenght=%d", msg, msg->length);
2922 return FALSE;
2923 }
2924
2925 pthread_mutex_lock(&netw->sendbolt);
2926
2927 if (list_push(netw->send_buf, msg, free_nstr_ptr) == FALSE) {
2928 pthread_mutex_unlock(&netw->sendbolt);
2929 return FALSE;
2930 }
2931
2932 pthread_mutex_unlock(&netw->sendbolt);
2933
2934 sem_post(&netw->send_blocker);
2935
2936 return TRUE;
2937} /* netw_add_msg(...) */
2938
2946int netw_add_msg_ex(NETWORK* netw, char* str, unsigned int length) {
2947 __n_assert(netw, return FALSE);
2948 __n_assert(str, return FALSE);
2949 if (length == 0) {
2950 return FALSE;
2951 }
2952
2953 N_STR* nstr = NULL;
2954 Malloc(nstr, N_STR, 1);
2955 __n_assert(nstr, return FALSE);
2956
2957 nstr->data = str;
2958 nstr->written = nstr->length = length;
2959
2960 pthread_mutex_lock(&netw->sendbolt);
2961 if (list_push(netw->send_buf, nstr, free_nstr_ptr) == FALSE) {
2962 pthread_mutex_unlock(&netw->sendbolt);
2963 return FALSE;
2964 }
2965 pthread_mutex_unlock(&netw->sendbolt);
2966
2967 sem_post(&netw->send_blocker);
2968
2969 return TRUE;
2970} /* netw_add_msg_ex(...) */
2971
2978 N_STR* ptr = NULL;
2979
2980 __n_assert(netw, return NULL);
2981
2982 pthread_mutex_lock(&netw->recvbolt);
2983
2984 ptr = list_shift(netw->recv_buf, N_STR);
2985
2986 pthread_mutex_unlock(&netw->recvbolt);
2987
2988 return ptr;
2989} /* netw_get_msg(...)*/
2990
2998N_STR* netw_wait_msg(NETWORK* netw, unsigned int refresh, size_t timeout) {
2999 size_t timed = 0;
3000 unsigned int secs = 0;
3001 unsigned int usecs = 0;
3002
3003 __n_assert(netw, return NULL);
3004
3005 usecs = refresh;
3006 if (refresh > 999999) {
3007 secs = refresh / 1000000;
3008 usecs = refresh % 1000000;
3009 }
3010
3011 if (timeout > 0)
3012 timed = timeout;
3013
3014 n_log(LOG_DEBUG, "wait from socket %d, refresh: %zu usec (%zu secs, %zu usecs), timeout %zu usec", netw->link.sock, refresh, secs, usecs, timeout);
3015 uint32_t state = NETW_RUN;
3016 int thr_state = 0;
3017 do {
3018 N_STR* nstrptr = netw_get_msg(netw);
3019 if (nstrptr)
3020 return nstrptr;
3021
3022 if (timeout > 0) {
3023 if (timed >= refresh)
3024 timed -= refresh;
3025 if (timed == 0 || timed < refresh) {
3026 _netw_capture_error(netw, "netw %d, status: %s (%" PRIu32 "), timeouted after waiting %zu msecs", netw->link.sock, N_ENUM_ENTRY(__netw_code_type, toString)(state), state, timeout);
3027 n_log(LOG_ERR, "netw %d, status: %s (%" PRIu32 "), timeouted after waiting %zu msecs", netw->link.sock, N_ENUM_ENTRY(__netw_code_type, toString)(state), state, timeout);
3028 return NULL;
3029 }
3030 }
3031 if (secs > 0)
3032 sleep(secs);
3033 if (usecs > 0)
3034 u_sleep(usecs);
3035
3036 netw_get_state(netw, &state, &thr_state);
3037 } while (state != NETW_EXITED && state != NETW_ERROR);
3038
3039 _netw_capture_error(netw, "got no answer and netw %d is no more running, state: %s (%" PRIu32 ")", netw->link.sock, N_ENUM_ENTRY(__netw_code_type, toString)(state), state);
3040 n_log(LOG_ERR, "got no answer and netw %d is no more running, state: %s (%" PRIu32 ")", netw->link.sock, N_ENUM_ENTRY(__netw_code_type, toString)(state), state);
3041
3042 return NULL;
3043} /* netw_wait_msg(...) */
3044
3051 __n_assert(netw, return FALSE);
3052
3053 pthread_mutex_lock(&netw->eventbolt);
3055 _netw_capture_error(netw, "THR Engine already started for network %p (%s)", netw, _str(netw->link.ip));
3056 n_log(LOG_ERR, "THR Engine already started for network %p (%s)", netw, _str(netw->link.ip));
3057 pthread_mutex_unlock(&netw->eventbolt);
3058 return FALSE;
3059 }
3060
3061 if (pthread_create(&netw->recv_thr, NULL, netw_recv_func, (void*)netw) != 0) {
3062 _netw_capture_error(netw, "Unable to create recv_thread for network %p (%s)", netw, _str(netw->link.ip));
3063 n_log(LOG_ERR, "Unable to create recv_thread for network %p (%s)", netw, _str(netw->link.ip));
3064 pthread_mutex_unlock(&netw->eventbolt);
3065 return FALSE;
3066 }
3068 if (pthread_create(&netw->send_thr, NULL, netw_send_func, (void*)netw) != 0) {
3069 _netw_capture_error(netw, "Unable to create send_thread for network %p (%s)", netw, _str(netw->link.ip));
3070 n_log(LOG_ERR, "Unable to create send_thread for network %p (%s)", netw, _str(netw->link.ip));
3071 pthread_cancel(netw->recv_thr);
3072 pthread_join(netw->recv_thr, NULL);
3074 pthread_mutex_unlock(&netw->eventbolt);
3075 return FALSE;
3076 }
3078
3080
3081 pthread_mutex_unlock(&netw->eventbolt);
3082
3083 return TRUE;
3084} /* netw_create_recv_thread(....) */
3085
3091void* netw_send_func(void* NET) {
3092 int DONE = 0;
3093
3094 ssize_t net_status = 0;
3095
3096 uint32_t state;
3097 uint32_t nboctet;
3098
3099 char nboct[5] = "";
3100
3101 N_STR* ptr = NULL;
3102
3103 NETWORK* netw = (NETWORK*)NET;
3104 __n_assert(netw, return NULL);
3105
3106 do {
3107 /* do not consume cpu for nothing, reduce delay */
3108 sem_wait(&netw->send_blocker);
3109 int message_sent = 0;
3110 while (message_sent == 0 && !DONE) {
3112 if (state & NETW_ERROR) {
3113 DONE = 666;
3114 } else if (state & NETW_EXITED) {
3115 DONE = 100;
3116 } else if (state & NETW_EXIT_ASKED) {
3117 DONE = 100;
3118 /* sending state */
3119 nboctet = htonl(NETW_EXIT_ASKED);
3120 memcpy(nboct, &nboctet, sizeof(uint32_t));
3121 n_log(LOG_DEBUG, "%d Sending Quit !", netw->link.sock);
3122 net_status = netw->send_data(netw, nboct, sizeof(int32_t));
3123 if (net_status < 0)
3124 DONE = 4;
3125 n_log(LOG_DEBUG, "%d Quit sent!", netw->link.sock);
3126 } else {
3127 pthread_mutex_lock(&netw->sendbolt);
3128 ptr = list_shift(netw->send_buf, N_STR);
3129 pthread_mutex_unlock(&netw->sendbolt);
3130 if (ptr && ptr->length > 0 && ptr->data) {
3131 if (ptr->written <= UINT_MAX) {
3132 n_log(LOG_DEBUG, "Sending ptr size %zu written %zu...", ptr->length, ptr->written);
3133
3134 /* sending state */
3135 nboctet = htonl(state);
3136 memcpy(nboct, &nboctet, sizeof(uint32_t));
3137 /* sending state */
3138 if (!DONE) {
3139 net_status = netw->send_data(netw, nboct, sizeof(int32_t));
3140 if (net_status < 0)
3141 DONE = 1;
3142 }
3143 if (!DONE) {
3144 /* sending number of octet */
3145 nboctet = htonl((uint32_t)ptr->written);
3146 memcpy(nboct, &nboctet, sizeof(uint32_t));
3147 /* sending the number of octet to receive on next message */
3148 net_status = netw->send_data(netw, nboct, sizeof(uint32_t));
3149 if (net_status < 0)
3150 DONE = 2;
3151 }
3152 /* sending the data itself */
3153 if (!DONE) {
3154 net_status = netw->send_data(netw, ptr->data, (uint32_t)ptr->written);
3155 if (net_status < 0)
3156 DONE = 3;
3157 }
3160 }
3161 message_sent = 1;
3162 free_nstr(&ptr);
3163#ifdef __linux
3164 fsync(netw->link.sock);
3165#endif
3166 } else {
3167 _netw_capture_error(netw, "discarded packet of size %zu which is greater than %" PRIu32, ptr->written, UINT_MAX);
3168 n_log(LOG_ERR, "discarded packet of size %zu which is greater than %" PRIu32, ptr->written, UINT_MAX);
3169 message_sent = 1;
3170 free_nstr(&ptr);
3171 }
3172 }
3173 }
3174 }
3175 } while (!DONE);
3176
3177 if (DONE == 1) {
3178 _netw_capture_error(netw, "Error when sending state %" PRIu32 " on socket %d (%s), network: %s", netw_atomic_read_state(netw), netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3179 n_log(LOG_ERR, "Error when sending state %" PRIu32 " on socket %d (%s), network: %s", netw_atomic_read_state(netw), netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3180 } else if (DONE == 2) {
3181 _netw_capture_error(netw, "Error when sending number of octet to socket %d (%s), network: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3182 n_log(LOG_ERR, "Error when sending number of octet to socket %d (%s), network: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3183 } else if (DONE == 3)
3184 n_log(LOG_DEBUG, "Error when sending data on socket %d (%s), network: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3185 else if (DONE == 4) {
3186 _netw_capture_error(netw, "Error when sending state QUIT on socket %d (%s), network: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3187 n_log(LOG_ERR, "Error when sending state QUIT on socket %d (%s), network: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3188 } else if (DONE == 5) {
3189 _netw_capture_error(netw, "Error when sending state QUIT number of octet (0) on socket %d (%s), network: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3190 n_log(LOG_ERR, "Error when sending state QUIT number of octet (0) on socket %d (%s), network: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3191 }
3192
3193 if (DONE == 100) {
3194 n_log(LOG_DEBUG, "Socket %d: Sending thread exiting correctly", netw->link.sock);
3196 } else {
3197 _netw_capture_error(netw, "Socket %d (%s): Sending thread exiting with error %d !", netw->link.sock, _str(netw->link.ip), DONE);
3198 n_log(LOG_ERR, "Socket %d (%s): Sending thread exiting with error %d !", netw->link.sock, _str(netw->link.ip), DONE);
3200 }
3201
3202 pthread_mutex_lock(&netw->eventbolt);
3204 pthread_mutex_unlock(&netw->eventbolt);
3205
3206 pthread_exit(0);
3207
3208 /* suppress compiler warning */
3209#if !defined(__linux__)
3210 return NULL;
3211#endif
3212} /* netw_send_func(...) */
3213
3219void* netw_recv_func(void* NET) {
3220 int DONE = 0;
3221 ssize_t net_status = 0;
3222
3223 uint32_t nboctet;
3224 uint32_t tmpstate;
3225 uint32_t state;
3226
3227 char nboct[5] = "";
3228
3229 N_STR* recvdmsg = NULL;
3230
3231 NETWORK* netw = (NETWORK*)NET;
3232
3233 __n_assert(netw, return NULL);
3234
3235 do {
3237 if (state & NETW_EXIT_ASKED || state & NETW_EXITED) {
3238 DONE = 100;
3239 }
3240 if (state & NETW_ERROR) {
3241 DONE = 666;
3242 }
3243 if (!DONE) {
3244 n_log(LOG_DEBUG, "socket %d : waiting to receive status", netw->link.sock);
3245 /* receiving state */
3246 net_status = netw->recv_data(netw, nboct, sizeof(uint32_t));
3247 if (net_status < 0) {
3248 DONE = 1;
3249 } else {
3250 memcpy(&nboctet, nboct, sizeof(uint32_t));
3251 tmpstate = ntohl(nboctet);
3252 nboctet = tmpstate;
3253 if (tmpstate == NETW_EXIT_ASKED) {
3254 n_log(LOG_DEBUG, "socket %d : receiving order to QUIT !", netw->link.sock);
3255 DONE = 100;
3256 } else {
3257 n_log(LOG_DEBUG, "socket %d : waiting to receive next message nboctets", netw->link.sock);
3258 /* receiving nboctet */
3259 net_status = netw->recv_data(netw, nboct, sizeof(uint32_t));
3260 if (net_status < 0) {
3261 DONE = 2;
3262 } else {
3263 memcpy(&nboctet, nboct, sizeof(uint32_t));
3264 tmpstate = ntohl(nboctet);
3265 nboctet = tmpstate;
3266
3267 Malloc(recvdmsg, N_STR, 1);
3268 if (!recvdmsg) {
3269 DONE = 3;
3270 } else {
3271 n_log(LOG_DEBUG, "socket %d : %" PRIu32 " octets to receive...", netw->link.sock, nboctet);
3272 Malloc(recvdmsg->data, char, nboctet + 1);
3273 if (!recvdmsg->data) {
3274 free_nstr(&recvdmsg);
3275 DONE = 4;
3276 } else {
3277 recvdmsg->length = nboctet + 1;
3278 recvdmsg->written = nboctet;
3279
3280 /* receiving the data itself */
3281 net_status = netw->recv_data(netw, recvdmsg->data, nboctet);
3282 if (net_status < 0) {
3283 free_nstr(&recvdmsg);
3284 DONE = 5;
3285 } else {
3286 pthread_mutex_lock(&netw->recvbolt);
3287 if (list_push(netw->recv_buf, recvdmsg, free_nstr_ptr) == FALSE)
3288 DONE = 6;
3289 pthread_mutex_unlock(&netw->recvbolt);
3290 n_log(LOG_DEBUG, "socket %d : %" PRIu32 " octets received !", netw->link.sock, nboctet);
3291 } /* recv data */
3292 } /* recv data allocation */
3293 } /* recv struct allocation */
3294 } /* recv nb octet*/
3295 } /* exit asked */
3296 } /* recv state */
3297 } /* if( !done) */
3298 } while (!DONE);
3299
3300 if (DONE == 1) {
3301 _netw_capture_error(netw, "Error when receiving state from socket %d (%s), net_status: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3302 n_log(LOG_ERR, "Error when receiving state from socket %d (%s), net_status: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3303 } else if (DONE == 2) {
3304 _netw_capture_error(netw, "Error when receiving nboctet from socket %d (%s), net_status: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3305 n_log(LOG_ERR, "Error when receiving nboctet from socket %d (%s), net_status: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3306 } else if (DONE == 3) {
3307 _netw_capture_error(netw, "Error when receiving data from socket %d (%s), net_status: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3308 n_log(LOG_ERR, "Error when receiving data from socket %d (%s), net_status: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3309 } else if (DONE == 4) {
3310 _netw_capture_error(netw, "Error allocating received message struct from socket %d (%s), net_status: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3311 n_log(LOG_ERR, "Error allocating received message struct from socket %d (%s), net_status: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3312 } else if (DONE == 5) {
3313 _netw_capture_error(netw, "Error allocating received messages data array from socket %d (%s), net_status: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3314 n_log(LOG_ERR, "Error allocating received messages data array from socket %d (%s), net_status: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3315 } else if (DONE == 6) {
3316 _netw_capture_error(netw, "Error adding receved message from socket %d (%s), net_status: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3317 n_log(LOG_ERR, "Error adding receved message from socket %d (%s), net_status: %s", netw->link.sock, _str(netw->link.ip), (net_status == NETW_SOCKET_DISCONNECTED) ? "disconnected" : "socket error");
3318 }
3319
3320 if (DONE == 100) {
3321 n_log(LOG_DEBUG, "Socket %d (%s): Receive thread exiting correctly", netw->link.sock, _str(netw->link.ip));
3323 } else {
3324 _netw_capture_error(netw, "Socket %d (%s): Receive thread exiting with code %d !", netw->link.sock, _str(netw->link.ip), DONE);
3325 n_log(LOG_ERR, "Socket %d (%s): Receive thread exiting with code %d !", netw->link.sock, _str(netw->link.ip), DONE);
3327 }
3328
3329 pthread_mutex_lock(&netw->eventbolt);
3331 pthread_mutex_unlock(&netw->eventbolt);
3332
3333 pthread_exit(0);
3334
3335 /* suppress compiler warning */
3336#if !defined(__linux__)
3337 return NULL;
3338#endif
3339} /* netw_recv_func(...) */
3340
3347 __n_assert(netw, return FALSE);
3348 uint32_t state = 0;
3349 int thr_engine_status = 0;
3350
3351 n_log(LOG_DEBUG, "Network %d stop threads event", netw->link.sock);
3352
3353 netw_get_state(netw, &state, &thr_engine_status);
3354
3355 if (thr_engine_status != NETW_THR_ENGINE_STARTED) {
3356 _netw_capture_error(netw, "Thread engine status already stopped for network %p", netw);
3357 n_log(LOG_ERR, "Thread engine status already stopped for network %p", netw);
3358 return FALSE;
3359 }
3360
3362
3363 n_log(LOG_DEBUG, "Network %d waits for send threads to stop", netw->link.sock);
3364 for (int it = 0; it < 10; it++) {
3365 sem_post(&netw->send_blocker);
3366 usleep(1000);
3367 }
3368 pthread_join(netw->send_thr, NULL);
3369 n_log(LOG_DEBUG, "Network %d waits for recv threads to stop", netw->link.sock);
3370 pthread_join(netw->recv_thr, NULL);
3371
3373
3374 n_log(LOG_DEBUG, "Network %d threads Stopped !", netw->link.sock);
3375
3376 return TRUE;
3377} /* Stop_Network( ... ) */
3378
3386ssize_t send_data(void* netw, char* buf, uint32_t n) {
3388 __n_assert(buf, return NETW_SOCKET_ERROR);
3389
3390 SOCKET s = ((NETWORK*)netw)->link.sock;
3391 ssize_t bcount = 0; /* counts bytes sent */
3392 int error;
3393 char* errmsg = NULL;
3394
3395 if (n == 0) {
3396 _netw_capture_error((NETWORK*)netw, "Send of 0 is unsupported.");
3397 n_log(LOG_ERR, "Send of 0 is unsupported.");
3398 return NETW_SOCKET_ERROR;
3399 }
3400
3401 char* tmp_buf = buf;
3402 while (bcount < n) /* loop until all sent */
3403 {
3404 ssize_t bs = 0; /* bytes sent this pass */
3405
3406 NETW_CALL_RETRY(bs, send(s, tmp_buf, NETW_BUFLEN_CAST(n - bcount), NETFLAGS), NETW_MAX_RETRIES);
3407 error = neterrno;
3408
3409 if (bs > 0) {
3410 bcount += bs; /* increment byte counter */
3411 tmp_buf += bs; /* move buffer ptr for next send */
3412 } else if (error == ECONNRESET || error == ENOTCONN || error == EPIPE
3413#ifdef __windows__
3414 || error == WSAECONNRESET || error == WSAENOTCONN || error == WSAESHUTDOWN
3415#endif
3416 ) {
3417 n_log(LOG_DEBUG, "socket %d disconnected !", s);
3419 } else if (bs == -1) {
3420 /* real network error */
3421 errmsg = netstrerror(error);
3422 _netw_capture_error((NETWORK*)netw, "Socket %d send error: %d, %s", s, bs, _str(errmsg));
3423 n_log(LOG_ERR, "Socket %d send error: %d, %s", s, bs, _str(errmsg));
3424 FreeNoLog(errmsg);
3425 return NETW_SOCKET_ERROR;
3426 } else if (bs == -2) {
3427 /* EINTR/WOULDBLOCK retries exhausted */
3428 _netw_capture_error((NETWORK*)netw, "Socket %d : retry storm on send (%d retries)", s, NETW_MAX_RETRIES);
3429 n_log(LOG_ERR, "Socket %d : retry storm on send (%d retries)", s, NETW_MAX_RETRIES);
3430 return NETW_SOCKET_ERROR;
3431 } else if (bs == 0) {
3432 /* should never happen on send() */
3433 n_log(LOG_DEBUG, "socket %d : send returned 0", s);
3435 }
3436 }
3437 return bcount;
3438} /*send_data(...)*/
3439
3447ssize_t recv_data(void* netw, char* buf, uint32_t n) {
3449 __n_assert(buf, return NETW_SOCKET_ERROR);
3450
3451 SOCKET s = ((NETWORK*)netw)->link.sock;
3452 ssize_t bcount = 0; /* counts bytes read */
3453 int error;
3454 char* errmsg = NULL;
3455
3456#if defined(NETWORK_DISABLE_ZERO_LENGTH_RECV) && (NETWORK_DISABLE_ZERO_LENGTH_RECV == TRUE)
3457 if (n == 0) {
3458 _netw_capture_error((NETWORK*)netw, "Recv of 0 is unsupported.");
3459 n_log(LOG_ERR, "Recv of 0 is unsupported.");
3460 return NETW_SOCKET_ERROR;
3461 }
3462#endif
3463
3464 char* tmp_buf = buf;
3465 while (bcount < n) /* loop until all received */
3466 {
3467 ssize_t br = 0; /* bytes received this pass */
3468
3469 NETW_CALL_RETRY(br, recv(s, tmp_buf, NETW_BUFLEN_CAST(n - bcount), NETFLAGS), NETW_MAX_RETRIES);
3470 error = neterrno;
3471
3472 if (br > 0) {
3473 bcount += br; /* increment byte counter */
3474 tmp_buf += br; /* move buffer ptr for next recv */
3475 } else if (br == 0) {
3476 /* clean shutdown from peer */
3477 n_log(LOG_DEBUG, "socket %d : peer closed connection", s);
3479 } else if (error == ECONNRESET || error == ENOTCONN
3480#ifdef __windows__
3481 || error == WSAECONNRESET || error == WSAENOTCONN || error == WSAESHUTDOWN
3482#endif
3483 ) {
3484 /* connection reset or not connected */
3485 n_log(LOG_DEBUG, "socket %d disconnected !", s);
3487 } else if (br == -1) {
3488 /* real network error */
3489 errmsg = netstrerror(error);
3490 _netw_capture_error((NETWORK*)netw, "Socket %d recv error: %d, %s", s, br, _str(errmsg));
3491 n_log(LOG_ERR, "Socket %d recv error: %d, %s", s, br, _str(errmsg));
3492 FreeNoLog(errmsg);
3493 return NETW_SOCKET_ERROR;
3494 } else if (br == -2) {
3495 /* EINTR/WOULDBLOCK retries exhausted */
3496 _netw_capture_error((NETWORK*)netw, "Socket %d : retry storm on recv (%d retries)", s, NETW_MAX_RETRIES);
3497 n_log(LOG_ERR, "Socket %d : retry storm on recv (%d retries)", s, NETW_MAX_RETRIES);
3498 return NETW_SOCKET_ERROR;
3499 }
3500 }
3501 return bcount;
3502} /*recv_data(...)*/
3503
3504#ifdef HAVE_OPENSSL
3512ssize_t send_ssl_data(void* netw, char* buf, uint32_t n) {
3513 __n_assert(netw, return -1);
3514 __n_assert(buf, return -1);
3515
3516 SSL* ssl = ((NETWORK*)netw)->ssl;
3517 __n_assert(ssl, return -1);
3518
3519 SOCKET s = ((NETWORK*)netw)->link.sock;
3520
3521 ssize_t bcount = 0; // counts bytes sent
3522 int error;
3523 char* errmsg = NULL;
3524
3525 if (n == 0) {
3526 _netw_capture_error((NETWORK*)netw, "Send of 0 is unsupported.");
3527 n_log(LOG_ERR, "Send of 0 is unsupported.");
3528 return -1;
3529 }
3530
3531 while (bcount < n) // loop until full buffer
3532 {
3533 size_t bs = 0; // bytes sent this pass
3534#if OPENSSL_VERSION_NUMBER < 0x1010107fL
3535 int status = SSL_write(ssl, buf, (int)(n - bcount)); // OpenSSL < 1.1.1
3536 bs = (status > 0) ? (size_t)status : 0;
3537#else
3538 int status = SSL_write_ex(ssl, buf, (size_t)(n - bcount), &bs); // OpenSSL >= 1.1.1
3539#endif
3540 error = neterrno;
3541 if (status > 0) {
3542 bcount += (ssize_t)bs; // increment byte counter
3543 buf += bs; // move buffer ptr for next read
3544 } else {
3545 int ssl_error = SSL_get_error(ssl, status);
3546 if (ssl_error == SSL_ERROR_WANT_READ || ssl_error == SSL_ERROR_WANT_WRITE) {
3547 usleep(0);
3548 continue;
3549 }
3550 errmsg = netstrerror(error);
3551 switch (ssl_error) {
3552 case SSL_ERROR_SYSCALL:
3553 // Handle syscall failure
3554 _netw_capture_error((NETWORK*)netw, "socket %d SSL_write syscall error: connection closed by peer", s);
3555 n_log(LOG_ERR, "socket %d SSL_write syscall error: connection closed by peer", s);
3556 break;
3557 case SSL_ERROR_SSL:
3558 // Handle SSL protocol failure
3559 _netw_capture_error((NETWORK*)netw, "socket %d SSL_write returned %d, error: %s", s, bs, ERR_reason_error_string(ERR_get_error()));
3560 n_log(LOG_ERR, "socket %d SSL_write returned %d, error: %s", s, bs, ERR_reason_error_string(ERR_get_error()));
3561 break;
3562 default:
3563 // Other errors
3564 _netw_capture_error((NETWORK*)netw, "socket %d SSL_write returned %d, errno: %s", s, bs, _str(errmsg));
3565 n_log(LOG_ERR, "socket %d SSL_write returned %d, errno: %s", s, bs, _str(errmsg));
3566 break;
3567 }
3568 FreeNoLog(errmsg);
3569 return -1;
3570 }
3571 }
3572 return bcount;
3573} /*send_ssl_data(...)*/
3574
3582ssize_t recv_ssl_data(void* netw, char* buf, uint32_t n) {
3583 __n_assert(netw, return -1);
3584 __n_assert(buf, return -1);
3585
3586 SSL* ssl = ((NETWORK*)netw)->ssl;
3587 __n_assert(ssl, return -1);
3588
3589 SOCKET s = ((NETWORK*)netw)->link.sock;
3590 ssize_t bcount = 0; // counts bytes read
3591 int error;
3592 char* errmsg = NULL;
3593
3594 if (n == 0) {
3595 _netw_capture_error((NETWORK*)netw, "Recv of 0 is unsupported.");
3596 n_log(LOG_ERR, "Recv of 0 is unsupported.");
3597 return -1;
3598 }
3599
3600 while (bcount < n) {
3601 size_t br = 0; // bytes read this pass
3602 // loop until full buffer
3603#if OPENSSL_VERSION_NUMBER < 0x10101000L
3604 int status = SSL_read(ssl, buf, (int)(n - bcount)); // OpenSSL < 1.1.1
3605 br = (status > 0) ? (size_t)status : 0;
3606#else
3607 int status = SSL_read_ex(ssl, buf, (size_t)(n - bcount), &br); // OpenSSL >= 1.1.1
3608#endif
3609 error = neterrno;
3610 if (status > 0) {
3611 bcount += (ssize_t)br; // increment byte counter
3612 buf += br; // move buffer ptr for next read
3613 } else {
3614 int ssl_error = SSL_get_error(ssl, status);
3615 if (ssl_error == SSL_ERROR_WANT_READ || ssl_error == SSL_ERROR_WANT_WRITE) {
3616 usleep(0);
3617 continue;
3618 }
3619 errmsg = netstrerror(error);
3620 switch (ssl_error) {
3621 case SSL_ERROR_SYSCALL:
3622 // Handle syscall failure
3623 _netw_capture_error((NETWORK*)netw, "socket %d SSL_read syscall error: connection closed by peer", s);
3624 n_log(LOG_ERR, "socket %d SSL_read syscall error: connection closed by peer", s);
3625 break;
3626 case SSL_ERROR_SSL:
3627 // Handle SSL protocol failure
3628 _netw_capture_error((NETWORK*)netw, "socket %d SSL_read returned %d, error: %s", s, br, ERR_reason_error_string(ERR_get_error()));
3629 n_log(LOG_ERR, "socket %d SSL_read returned %d, error: %s", s, br, ERR_reason_error_string(ERR_get_error()));
3630 break;
3631 default:
3632 // Other errors
3633 _netw_capture_error((NETWORK*)netw, "socket %d SSL_read returned %d, errno: %s", s, br, _str(errmsg));
3634 n_log(LOG_ERR, "socket %d SSL_read returned %d, errno: %s", s, br, _str(errmsg));
3635 break;
3636 }
3637 FreeNoLog(errmsg);
3638 return -1;
3639 }
3640 }
3641 return bcount;
3642} /*recv_ssl_data(...)*/
3643#endif
3644
3653ssize_t send_php(SOCKET s, int _code, char* buf, int n) {
3654 ssize_t bcount = 0; /* counts bytes read */
3655 ssize_t bs = 0; /* bytes read this pass */
3656
3657 int error;
3658 char* errmsg = NULL;
3659
3660 char* ptr = NULL; /* temp char ptr ;-) */
3661 char head[HEAD_SIZE + 1] = "";
3662 char code[HEAD_CODE + 1] = "";
3663
3664 // Format and assign to head
3665 snprintf(head, HEAD_SIZE + 1, "%0*d", HEAD_SIZE, n);
3666 // Format and assign to code
3667 snprintf(code, HEAD_CODE + 1, "%0*d", HEAD_CODE, _code);
3668
3669 /* sending head */
3670 bcount = bs = 0;
3671 ptr = head;
3672 while (bcount < HEAD_SIZE) /* loop until full buffer */
3673 {
3674 bs = send(s, ptr, NETW_BUFLEN_CAST(HEAD_SIZE - bcount), NETFLAGS);
3675 error = neterrno;
3676 if (bs > 0) {
3677 bcount += bs; /* increment byte counter */
3678 ptr += bs; /* move buffer ptr for next read */
3679 } else {
3680 /* signal an error to the caller */
3681 errmsg = netstrerror(error);
3682 n_log(LOG_ERR, "Socket %d sending Error %d when sending head size, neterrno: %s", s, bs, _str(errmsg));
3683 FreeNoLog(errmsg);
3684 return -1;
3685 }
3686 }
3687
3688 /* sending code */
3689 bcount = bs = 0;
3690 ptr = code;
3691 while (bcount < HEAD_CODE) /* loop until full buffer */
3692 {
3693 bs = send(s, ptr, NETW_BUFLEN_CAST(HEAD_CODE - bcount), NETFLAGS);
3694 error = neterrno;
3695 if (bs > 0) {
3696 bcount += bs; /* increment byte counter */
3697 ptr += bs; /* move buffer ptr for next read */
3698 } else {
3699 errmsg = netstrerror(error);
3700 n_log(LOG_ERR, "Socket %d sending Error %d when sending head code, neterrno: %s", s, bs, _str(errmsg));
3701 FreeNoLog(errmsg);
3702 return -1;
3703 }
3704 }
3705
3706 /* sending buf */
3707 bcount = 0;
3708 bs = 0;
3709 while (bcount < n) /* loop until full buffer */
3710 {
3711 bs = send(s, buf, NETW_BUFLEN_CAST(n - bcount), NETFLAGS);
3712 error = neterrno;
3713 if (bs > 0) {
3714 bcount += bs; /* increment byte counter */
3715 buf += bs; /* move buffer ptr for next read */
3716 } else {
3717 /* signal an error to the caller */
3718 errmsg = netstrerror(error);
3719 n_log(LOG_ERR, "Socket %d sending Error %d when sending message of size %d, neterrno: %s", s, bs, n, _str(errmsg));
3720 FreeNoLog(errmsg);
3721 return -1;
3722 }
3723 }
3724
3725 return bcount;
3726
3727} /*send_php(...)*/
3728
3736ssize_t recv_php(SOCKET s, int* _code, char** buf) {
3737 ssize_t bcount = 0; /* counts bytes read */
3738 ssize_t br = 0; /* bytes read this pass */
3739 long int tmpnb = 0, size = 0; /* size of message to receive */
3740 char* ptr = NULL;
3741
3742 int error;
3743 char* errmsg = NULL;
3744
3745 char head[HEAD_SIZE + 1] = "";
3746 char code[HEAD_CODE + 1] = "";
3747
3748 /* Receiving total message size */
3749 bcount = br = 0;
3750 ptr = head;
3751 while (bcount < HEAD_SIZE) {
3752 /* loop until full buffer */
3753 br = recv(s, ptr, NETW_BUFLEN_CAST(HEAD_SIZE - bcount), NETFLAGS);
3754 error = neterrno;
3755 if (br > 0) {
3756 bcount += br; /* increment byte counter */
3757 ptr += br; /* move buffer ptr for next read */
3758 } else {
3759 /* signal an error to the caller */
3760 errmsg = netstrerror(error);
3761 n_log(LOG_ERR, "Socket %d receive %d Error %s", s, br, _str(errmsg));
3762 FreeNoLog(errmsg);
3763 return FALSE;
3764 }
3765 }
3766
3767 tmpnb = strtol(head, NULL, 10);
3768 if (tmpnb == LONG_MIN || tmpnb == LONG_MAX) {
3769 n_log(LOG_ERR, "Size received ( %ld ) can not be determined on socket %d", tmpnb, s);
3770 return FALSE;
3771 }
3772
3773 size = tmpnb;
3774
3775 /* receiving request code */
3776 bcount = br = 0;
3777 ptr = code;
3778 while (bcount < HEAD_CODE) {
3779 /* loop until full buffer */
3780 br = recv(s, ptr, NETW_BUFLEN_CAST(HEAD_CODE - bcount), NETFLAGS);
3781 error = neterrno;
3782 if (br > 0) {
3783 bcount += br; /* increment byte counter */
3784 ptr += br; /* move buffer ptr for next read */
3785 } else {
3786 /* signal an error to the caller */
3787 errmsg = netstrerror(error);
3788 n_log(LOG_ERR, "Socket %d receive %d Error , neterrno: %s", s, br, _str(errmsg));
3789 FreeNoLog(errmsg);
3790 return FALSE;
3791 }
3792 }
3793
3794 tmpnb = strtol(code, NULL, 10);
3795 if (tmpnb <= INT_MIN || tmpnb >= INT_MAX) {
3796 n_log(LOG_ERR, "Code received ( %ld ) too big or too little to be valid code on socket %d", tmpnb, s);
3797 return FALSE;
3798 }
3799
3800 (*_code) = (int)tmpnb;
3801
3802 /* receving message */
3803 if ((*buf)) {
3804 // cppcheck-suppress identicalInnerCondition -- Free() macro rechecks pointer internally
3805 Free((*buf));
3806 }
3807 Malloc((*buf), char, (size_t)(size + 1));
3808 if (!(*buf)) {
3809 n_log(LOG_ERR, "Could not allocate PHP receive buf");
3810 return FALSE;
3811 }
3812
3813 bcount = 0;
3814 br = 0;
3815 ptr = (*buf);
3816 while (bcount < size) {
3817 /* loop until full buffer */
3818 br = recv(s, ptr, NETW_BUFLEN_CAST(size - bcount), NETFLAGS);
3819 error = neterrno;
3820 if (br > 0) {
3821 bcount += br; /* increment byte counter */
3822 ptr += br; /* move buffer ptr for next read */
3823 } else {
3824 /* signal an error to the caller */
3825 errmsg = netstrerror(error);
3826 n_log(LOG_ERR, "Socket %d receive %d Error neterrno: %s", s, br, _str(errmsg));
3827 FreeNoLog(errmsg);
3828 return FALSE;
3829 }
3830 }
3831 return size;
3832} /*recv_php(...)*/
3833
3842int netw_get_queue_status(NETWORK* netw, size_t* nb_to_send, size_t* nb_to_read) {
3843 __n_assert(netw, return FALSE);
3844
3845 pthread_mutex_lock(&netw->sendbolt);
3846 (*nb_to_send) = netw->send_buf->nb_items;
3847 pthread_mutex_unlock(&netw->sendbolt);
3848
3849 pthread_mutex_lock(&netw->recvbolt);
3850 (*nb_to_read) = netw->recv_buf->nb_items;
3851 pthread_mutex_unlock(&netw->recvbolt);
3852
3853 return TRUE;
3854} /* get queue states */
3855
3861NETWORK_POOL* netw_new_pool(size_t nb_min_element) {
3862 NETWORK_POOL* netw_pool = NULL;
3863
3864 Malloc(netw_pool, NETWORK_POOL, 1);
3865 __n_assert(netw_pool, return NULL);
3866
3867 netw_pool->pool = new_ht(nb_min_element);
3868 __n_assert(netw_pool->pool, Free(netw_pool); return NULL);
3869
3870 init_lock(netw_pool->rwlock);
3871
3872 return netw_pool;
3873} /* netw_new_pool() */
3874
3881 __n_assert(netw_pool && (*netw_pool), return FALSE);
3882
3883 write_lock((*netw_pool)->rwlock);
3884 if ((*netw_pool)->pool)
3885 destroy_ht(&(*netw_pool)->pool);
3886 unlock((*netw_pool)->rwlock);
3887
3888 rw_lock_destroy((*netw_pool)->rwlock);
3889
3890 Free((*netw_pool));
3891
3892 return TRUE;
3893} /* netw_destroy_pool() */
3894
3899void netw_pool_netw_close(void* netw_ptr) {
3900 NETWORK* netw = (NETWORK*)netw_ptr;
3901 __n_assert(netw, return);
3902 n_log(LOG_DEBUG, "Network pool %p: network id %d still active !!", netw, netw->link.sock);
3903 return;
3904} /* netw_pool_netw_close() */
3905
3913 __n_assert(netw_pool, return FALSE);
3914 __n_assert(netw, return FALSE);
3915
3916 n_log(LOG_DEBUG, "Trying to add %lld to %p", (unsigned long long)netw->link.sock, netw_pool->pool);
3917
3918 /* write lock the pool */
3919 write_lock(netw_pool->rwlock);
3920 /* test if not already added */
3921 N_STR* key = NULL;
3922 nstrprintf(key, "%lld", (unsigned long long)netw->link.sock);
3923 HASH_NODE* node = NULL;
3924 if (ht_get_ptr(netw_pool->pool, _nstr(key), (void*)&node) == TRUE) {
3925 _netw_capture_error(netw, "Network id %d already added !", netw->link.sock);
3926 n_log(LOG_ERR, "Network id %d already added !", netw->link.sock);
3927 free_nstr(&key);
3928 unlock(netw_pool->rwlock);
3929 return FALSE;
3930 }
3931 int retval;
3932 /* add it */
3933 if ((retval = ht_put_ptr(netw_pool->pool, _nstr(key), netw, &netw_pool_netw_close, NULL)) == TRUE) {
3934 if ((retval = list_push(netw->pools, netw_pool, NULL)) == TRUE) {
3935 n_log(LOG_DEBUG, "added netw %d to pool %p", netw->link.sock, netw_pool);
3936 } else {
3937 _netw_capture_error(netw, "could not add netw %d to pool %p", netw->link.sock, netw_pool);
3938 n_log(LOG_ERR, "could not add netw %d to pool %p", netw->link.sock, netw_pool);
3939 }
3940 } else {
3941 _netw_capture_error(netw, "could not add netw %d to pool %p", netw->link.sock, netw_pool);
3942 n_log(LOG_ERR, "could not add netw %d to pool %p", netw->link.sock, netw_pool);
3943 }
3944 free_nstr(&key);
3945
3946 /* unlock the pool */
3947 unlock(netw_pool->rwlock);
3948
3949 return retval;
3950} /* netw_pool_add() */
3951
3959 __n_assert(netw_pool, return FALSE);
3960 __n_assert(netw, return FALSE);
3961
3962 /* write lock the pool */
3963 write_lock(netw_pool->rwlock);
3964 /* test if present */
3965 N_STR* key = NULL;
3966 nstrprintf(key, "%lld", (unsigned long long int)netw->link.sock);
3967 if (ht_remove(netw_pool->pool, _nstr(key)) == TRUE) {
3968 LIST_NODE* node = list_search(netw->pools, netw_pool);
3969 if (node) {
3970 if (!remove_list_node(netw->pools, node, NETWORK_POOL)) {
3971 _netw_capture_error(netw, "Network id %d could not be removed !", netw->link.sock);
3972 n_log(LOG_ERR, "Network id %d could not be removed !", netw->link.sock);
3973 }
3974 }
3975 unlock(netw_pool->rwlock);
3976 n_log(LOG_DEBUG, "Network id %d removed !", netw->link.sock);
3977 free_nstr(&key);
3978 return TRUE;
3979 }
3980 free_nstr(&key);
3981 _netw_capture_error(netw, "Network id %d already removed !", netw->link.sock);
3982 n_log(LOG_ERR, "Network id %d already removed !", netw->link.sock);
3983 /* unlock the pool */
3984 unlock(netw_pool->rwlock);
3985 return FALSE;
3986} /* netw_pool_remove */
3987
3995int netw_pool_broadcast(NETWORK_POOL* netw_pool, const NETWORK* from, N_STR* net_msg) {
3996 __n_assert(netw_pool, return FALSE);
3997 __n_assert(net_msg, return FALSE);
3998
3999 /* write lock the pool */
4000 read_lock(netw_pool->rwlock);
4001 ht_foreach(node, netw_pool->pool) {
4002 NETWORK* netw = hash_val(node, NETWORK);
4003 if (from) {
4004 if (netw->link.sock != from->link.sock)
4005 netw_add_msg(netw, nstrdup(net_msg));
4006 } else {
4007 netw_add_msg(netw, nstrdup(net_msg));
4008 }
4009 }
4010 unlock(netw_pool->rwlock);
4011 return TRUE;
4012} /* netw_pool_broadcast */
4013
4020 __n_assert(netw_pool, return 0);
4021
4022 size_t nb = 0;
4023 read_lock(netw_pool->rwlock);
4024 nb = netw_pool->pool->nb_keys;
4025 unlock(netw_pool->rwlock);
4026
4027 return nb;
4028} /* netw_pool_nbclients() */
4029
4037 __n_assert(netw, return FALSE);
4038 netw->user_id = id;
4039 return TRUE;
4040} /* netw_set_user_id() */
4041
4051int netw_send_ping(NETWORK* netw, int type, int id_from, int id_to, int time) {
4052 N_STR* tmpstr = NULL;
4053 __n_assert(netw, return FALSE);
4054
4055 tmpstr = netmsg_make_ping(type, id_from, id_to, time);
4056 __n_assert(tmpstr, return FALSE);
4057
4058 return netw_add_msg(netw, tmpstr);
4059} /* netw_send_ping( ... ) */
4060
4070int netw_send_ident(NETWORK* netw, int type, int id, N_STR* name, N_STR* passwd) {
4071 N_STR* tmpstr = NULL;
4072
4073 __n_assert(netw, return FALSE);
4074
4075 tmpstr = netmsg_make_ident(type, id, name, passwd);
4076 __n_assert(tmpstr, return FALSE);
4077
4078 return netw_add_msg(netw, tmpstr);
4079} /* netw_send_ident( ... ) */
4080
4094int netw_send_position(NETWORK* netw, int id, double X, double Y, double vx, double vy, double acc_x, double acc_y, int time_stamp) {
4095 N_STR* tmpstr = NULL;
4096
4097 __n_assert(netw, return FALSE);
4098
4099 tmpstr = netmsg_make_position_msg(id, X, Y, vx, vy, acc_x, acc_y, time_stamp);
4100
4101 __n_assert(tmpstr, return FALSE);
4102
4103 return netw_add_msg(netw, tmpstr);
4104} /* netw_send_position( ... ) */
4105
4116int netw_send_string_to(NETWORK* netw, int id_to, N_STR* name, N_STR* chan, N_STR* txt, int color) {
4117 N_STR* tmpstr = NULL;
4118
4119 __n_assert(netw, return FALSE);
4120
4121 tmpstr = netmsg_make_string_msg(netw->user_id, id_to, name, chan, txt, color);
4122 __n_assert(tmpstr, return FALSE);
4123
4124 return netw_add_msg(netw, tmpstr);
4125} /* netw_send_string_to( ... ) */
4126
4136int netw_send_string_to_all(NETWORK* netw, N_STR* name, N_STR* chan, N_STR* txt, int color) {
4137 N_STR* tmpstr = NULL;
4138
4139 __n_assert(netw, return FALSE);
4140
4141 tmpstr = netmsg_make_string_msg(netw->user_id, -1, name, chan, txt, color);
4142 __n_assert(tmpstr, return FALSE);
4143
4144 return netw_add_msg(netw, tmpstr);
4145} /* netw_send_string_to_all( ... ) */
4146
4153 __n_assert(netw, return FALSE);
4154
4155 N_STR* tmpstr = NULL;
4156
4157 tmpstr = netmsg_make_quit_msg();
4158 __n_assert(tmpstr, return FALSE);
4159
4160 return netw_add_msg(netw, tmpstr);
4161} /* netw_send_quit( ... ) */
4162
4169size_t netw_calculate_urlencoded_size(const char* str, size_t len) {
4170 __n_assert(str, return 0);
4171
4172 size_t encoded_size = 0;
4173
4174 for (size_t i = 0; i < len; i++) {
4175 unsigned char c = (unsigned char)str[i];
4176 if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
4177 encoded_size += 1; // Safe character, no encoding needed
4178 } else {
4179 encoded_size += 3; // Unsafe character, will be encoded as %XX
4180 }
4181 }
4182
4183 return encoded_size;
4184}
4185
4192char* netw_urlencode(const char* str, size_t len) {
4193 __n_assert(str, return NULL);
4194
4195 static const char* hex = "0123456789ABCDEF";
4196 size_t encoded_size = netw_calculate_urlencoded_size(str, len);
4197 char* encoded = (char*)malloc(encoded_size + 1); // Allocate memory for the encoded string (+1 for the null terminator)
4198
4199 if (!encoded) {
4200 return NULL; // Return NULL if memory allocation fails
4201 }
4202
4203 char* pbuf = encoded;
4204
4205 for (size_t i = 0; i < len; i++) {
4206 unsigned char c = (unsigned char)str[i];
4207 if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
4208 *pbuf++ = (char)c; // Copy safe characters directly
4209 } else {
4210 *pbuf++ = '%'; // Encode unsafe characters as %XX
4211 *pbuf++ = hex[c >> 4];
4212 *pbuf++ = hex[c & 0xF];
4213 }
4214 }
4215
4216 *pbuf = '\0'; // Null-terminate the encoded string
4217 return encoded;
4218}
4219
4225char* netw_extract_http_request_type(const char* request) {
4226 __n_assert(request, return NULL);
4227 // Find the first space in the request string
4228 const char* space = strchr(request, ' ');
4229
4230 if (space == NULL) {
4231 // No space found, invalid request format
4232 return NULL;
4233 }
4234
4235 // Calculate the length of the request type
4236 size_t method_length = (size_t)(space - request);
4237
4238 // Allocate memory for the method string (+1 for the null terminator)
4239 char* method = (char*)malloc(method_length + 1);
4240
4241 if (method == NULL) {
4242 // Memory allocation failed
4243 return NULL;
4244 }
4245
4246 // Copy the request method to the allocated memory
4247 strncpy(method, request, method_length);
4248 method[method_length] = '\0'; // Null-terminate the string
4249
4250 return method;
4251}
4252
4259 NETWORK_HTTP_INFO info = {.content_length = 0, .body = NULL, .type = NULL, .content_type = {0}};
4260
4261 __n_assert(request, return info);
4262
4263 // Find Request-Type
4264 info.type = netw_extract_http_request_type(request);
4265
4266 // Find Content-Type header (Optional)
4267 const char* content_type_header = strstr(request, "Content-Type:");
4268 if (content_type_header) {
4269 const char* start = content_type_header + strlen("Content-Type: ");
4270 const char* end = strstr(start, "\r\n");
4271 if (end) {
4272 size_t length = (size_t)(end - start);
4273 if (length > 255) length = 255;
4274 strncpy(info.content_type, start, length);
4275 info.content_type[length] = '\0';
4276 }
4277 } else {
4278 // If no Content-Type header found, set default
4279 strncpy(info.content_type, "text/plain", sizeof(info.content_type) - 1);
4280 }
4281
4282 // Find Content-Length header (Optional)
4283 const char* content_length_header = strstr(request, "Content-Length:");
4284 if (content_length_header) {
4285 const char* start = content_length_header + strlen("Content-Length: ");
4286 errno = 0;
4287 unsigned long tmp_cl = strtoul(start, NULL, 10); /* parse once */
4288 int error = errno;
4289 // Handle out-of-range error (e.g., set a safe default or return an error)
4290 // Only when ULONG_MAX is larger than SIZE_MAX, to prevent a constant false comparison warning
4291#if ULONG_MAX > SIZE_MAX
4292 if (error == ERANGE || tmp_cl > SIZE_MAX) {
4293#else
4294 if (error == ERANGE) {
4295#endif
4296 n_log(LOG_ERR, "could not get content_length for request %p, returned %s", request, strerror(error));
4297 info.content_length = SIZE_MAX; /* clamp to the maximum supported value */
4298 } else {
4299 info.content_length = (size_t)tmp_cl;
4300 }
4301 }
4302
4303 // Find the start of the body (after \r\n\r\n)
4304 const char* body_start = strstr(request, "\r\n\r\n");
4305 if (body_start) {
4306 body_start += 4; // Skip the \r\n\r\n
4307
4308 // If there is a Content-Length, and it's greater than 0, copy the body
4309 if (info.content_length > 0 && info.content_length < SIZE_MAX) {
4310 info.body = malloc(info.content_length + 1); // Allocate memory for body
4311 if (info.body) {
4312 strncpy(info.body, body_start, info.content_length);
4313 info.body[info.content_length] = '\0'; // Null-terminate the body
4314 }
4315 }
4316 }
4317 return info;
4318}
4319
4326 FreeNoLog(http_request.body);
4327 FreeNoLog(http_request.type);
4328 return TRUE;
4329}
4330
4338int netw_get_url_from_http_request(const char* request, char* url, size_t size) {
4339 __n_assert(request && strlen(request) > 0, return FALSE);
4340 __n_assert(url && size > 1, return FALSE);
4341
4342 /* Default output */
4343 strncpy(url, "/", size - 1);
4344 url[size - 1] = '\0';
4345
4346 /* Example request line: "GET /path/to/resource HTTP/1.1" */
4347 const char* first_space = strchr(request, ' ');
4348 if (!first_space) {
4349 /* Malformed request, return default '/' */
4350 return FALSE;
4351 }
4352
4353 const char* second_space = strchr(first_space + 1, ' ');
4354 if (!second_space) {
4355 /* Malformed request, return default '/' */
4356 return FALSE;
4357 }
4358
4359 size_t len = (size_t)(second_space - first_space - 1);
4360 if (len >= size) {
4361 len = size - 1;
4362 }
4363
4364 strncpy(url, first_space + 1, len);
4365 url[len] = '\0'; /* Null-terminate the URL */
4366
4367 return TRUE;
4368}
4369
4375char* netw_urldecode(const char* str) {
4376 __n_assert(str, return NULL);
4377 char* decoded = malloc(strlen(str) + 1);
4378 __n_assert(decoded, return NULL);
4379
4380 char* p = decoded;
4381 while (*str) {
4382 if (*str == '%') {
4383 if (isxdigit((unsigned char)str[1]) && isxdigit((unsigned char)str[2])) {
4384 unsigned int value;
4385 if (sscanf(str + 1, "%2x", &value) >= 1) {
4386 *p++ = (char)value;
4387 str += 3;
4388 } else {
4389 n_log(LOG_ERR, "sscanf could not parse char *str (%p) for a %%2x", str);
4390 Free(decoded);
4391 return NULL;
4392 }
4393 } else {
4394 *p++ = *str++;
4395 }
4396 } else if (*str == '+') {
4397 *p++ = ' ';
4398 str++;
4399 } else {
4400 *p++ = *str++;
4401 }
4402 }
4403 *p = '\0';
4404 return decoded;
4405}
4406
4412HASH_TABLE* netw_parse_post_data(const char* post_data) {
4413 __n_assert(post_data, return NULL);
4414
4415 // Create a copy of the post_data string because strtok modifies the string
4416 char* data = strdup(post_data);
4417 __n_assert(data, return NULL);
4418
4419 char* pair = data;
4420
4421 HASH_TABLE* post_data_table = new_ht(32);
4422
4423 while (pair != NULL) {
4424 // Find the next key-value pair separated by '&'
4425 char* ampersand_pos = strchr(pair, '&');
4426
4427 // If found, replace it with '\0' to isolate the current pair
4428 if (ampersand_pos != NULL) {
4429 *ampersand_pos = '\0';
4430 }
4431
4432 // Now split each pair by '=' to get the key and value
4433 char* equal_pos = strchr(pair, '=');
4434 if (equal_pos != NULL) {
4435 *equal_pos = '\0'; // Terminate the key string
4436 const char* key = pair;
4437 const char* value = equal_pos + 1;
4438
4439 // Decode the value since POST data is URL-encoded
4440 char* decoded_value = netw_urldecode(value);
4441 ht_put_string(post_data_table, key, decoded_value);
4442 // printf("Key: %s, Value: %s\n", key, decoded_value);
4443 free(decoded_value);
4444 }
4445 // Move to the next key-value pair (if any)
4446 pair = (ampersand_pos != NULL) ? (ampersand_pos + 1) : NULL;
4447 }
4448 // Free the duplicated string
4449 free(data);
4450 return post_data_table;
4451}
4452
4458const char* netw_guess_http_content_type(const char* url) {
4459 __n_assert(url, return NULL);
4460
4461 // Create a copy of the URL to work on (to avoid modifying the original string)
4462 char url_copy[1024];
4463 strncpy(url_copy, url, sizeof(url_copy) - 1);
4464 url_copy[sizeof(url_copy) - 1] = '\0';
4465
4466 // Find if there is a '?' (indicating GET parameters) and terminate the string there
4467 char* query_start = strchr(url_copy, '?');
4468 if (query_start) {
4469 *query_start = '\0'; // Terminate the string before the query parameters
4470 }
4471
4472 // Find the last occurrence of a dot in the URL (file extension)
4473 const char* ext = strrchr(url_copy, '.');
4474
4475 // If no extension is found, return "unknown"
4476 if (!ext) {
4477 return "unknown";
4478 }
4479
4480 // Compare the extension to known content types
4481 if (strcmp(ext, ".html") == 0 || strcmp(ext, ".htm") == 0) {
4482 return "text/html";
4483 } else if (strcmp(ext, ".txt") == 0) {
4484 return "text/plain";
4485 } else if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) {
4486 return "image/jpeg";
4487 } else if (strcmp(ext, ".png") == 0) {
4488 return "image/png";
4489 } else if (strcmp(ext, ".gif") == 0) {
4490 return "image/gif";
4491 } else if (strcmp(ext, ".css") == 0) {
4492 return "text/css";
4493 } else if (strcmp(ext, ".js") == 0) {
4494 return "application/javascript";
4495 } else if (strcmp(ext, ".json") == 0) {
4496 return "application/json";
4497 } else if (strcmp(ext, ".xml") == 0) {
4498 return "application/xml";
4499 } else if (strcmp(ext, ".pdf") == 0) {
4500 return "application/pdf";
4501 } else if (strcmp(ext, ".zip") == 0) {
4502 return "application/zip";
4503 } else if (strcmp(ext, ".mp4") == 0) {
4504 return "video/mp4";
4505 } else if (strcmp(ext, ".mp3") == 0) {
4506 return "audio/mpeg";
4507 } else if (strcmp(ext, ".wav") == 0) {
4508 return "audio/wav";
4509 } else if (strcmp(ext, ".ogg") == 0) {
4510 return "audio/ogg";
4511 }
4512
4513 // Return unknown for other types
4514 return "unknown";
4515}
4516
4522const char* netw_get_http_status_message(int status_code) {
4523 switch (status_code) {
4524 case 200:
4525 return "OK";
4526 case 204:
4527 return "No Content";
4528 case 304:
4529 return "Not Modified";
4530 case 404:
4531 return "Not Found";
4532 case 500:
4533 return "Internal Server Error";
4534 // Add more status codes as needed
4535 default:
4536 return "Unknown";
4537 }
4538}
4539
4546int netw_get_http_date(char* buffer, size_t buffer_size) {
4547 __n_assert(buffer, return FALSE);
4548 const time_t now = time(NULL);
4549 struct tm gmt;
4550#ifdef _WIN32
4551 if (gmtime_s(&gmt, &now) != 0) {
4552 n_log(LOG_ERR, "gmtime_s failed");
4553 return FALSE;
4554 }
4555#else
4556 if (!gmtime_r(&now, &gmt)) {
4557 n_log(LOG_ERR, "gmtime_r returned NULL");
4558 return FALSE;
4559 }
4560#endif
4561 if (strftime(buffer, buffer_size, "%a, %d %b %Y %H:%M:%S GMT", &gmt) == 0) {
4562 n_log(LOG_ERR, "strftime failed: buffer too small");
4563 return FALSE;
4564 }
4565 return TRUE;
4566}
4567
4578int netw_build_http_response(N_STR** http_response, int status_code, const char* server_name, const char* content_type, char* additional_headers, N_STR* body) {
4579 __n_assert(server_name, return FALSE);
4580 __n_assert(content_type, return FALSE);
4581 __n_assert(additional_headers, return FALSE);
4582
4583 const char* status_message = netw_get_http_status_message(status_code);
4584 const char* connection_type = "close";
4585
4586 // Buffer for the date header
4587 char date_buffer[128] = "";
4588 netw_get_http_date(date_buffer, sizeof(date_buffer));
4589
4590 if ((*http_response)) {
4591 (*http_response)->written = 0;
4592 }
4593
4594 if (!body || body->written == 0) {
4595 // Handle the case where there is no body
4596 nstrprintf((*http_response),
4597 "HTTP/1.1 %d %s\r\n"
4598 "Date: %s\r\n"
4599 "Server: %s\r\n"
4600 "Content-Length: 0\r\n"
4601 "%s"
4602 "Connection: %s\r\n\r\n",
4603 status_code, status_message, date_buffer, server_name, additional_headers, connection_type);
4604 n_log(LOG_DEBUG, "empty response");
4605 } else {
4606 // Build the response with body
4607 nstrprintf((*http_response),
4608 "HTTP/1.1 %d %s\r\n"
4609 "Date: %s\r\n"
4610 "Server: %s\r\n"
4611 "Content-Type: %s\r\n"
4612 "Content-Length: %zu\r\n"
4613 "%s"
4614 "Connection: %s\r\n\r\n",
4615 status_code, status_message, date_buffer, server_name, content_type, body->written, additional_headers, connection_type);
4616 nstrcat((*http_response), body);
4617 n_log(LOG_DEBUG, "body response");
4618 }
4619 return TRUE;
4620}
4621
4622#ifdef HAVE_OPENSSL
4623
4629static ssize_t _ws_write(N_WS_CONN* conn, const void* buf, size_t len) {
4630 __n_assert(conn, return -1);
4631 __n_assert(conn->netw, return -1);
4632 if (conn->netw->crypto_algo == NETW_ENCRYPT_OPENSSL && conn->netw->ssl) {
4633 int ret = SSL_write(conn->netw->ssl, buf, (int)len);
4634 return (ret > 0) ? (ssize_t)ret : -1;
4635 }
4636 ssize_t ret = send(conn->netw->link.sock, buf, len, NETFLAGS);
4637 return ret;
4638}
4639
4645static ssize_t _ws_read(N_WS_CONN* conn, void* buf, size_t len) {
4646 __n_assert(conn, return -1);
4647 __n_assert(conn->netw, return -1);
4648 size_t total = 0;
4649 char* p = (char*)buf;
4650 while (total < len) {
4651 ssize_t ret = 0;
4652 if (conn->netw->crypto_algo == NETW_ENCRYPT_OPENSSL && conn->netw->ssl) {
4653 ret = SSL_read(conn->netw->ssl, p + total, (int)(len - total));
4654 } else {
4655 ret = recv(conn->netw->link.sock, p + total, len - total, 0);
4656 }
4657 if (ret <= 0) {
4658 return -1;
4659 }
4660 total += (size_t)ret;
4661 }
4662 return (ssize_t)total;
4663}
4664
4676N_WS_CONN* n_ws_connect(const char* host, const char* port, const char* path, int use_ssl) {
4677 __n_assert(host, return NULL);
4678 __n_assert(port, return NULL);
4679 __n_assert(path, return NULL);
4680
4681 N_WS_CONN* conn = NULL;
4682 Malloc(conn, N_WS_CONN, 1);
4683 __n_assert(conn, return NULL);
4684
4685 conn->netw = NULL;
4686 conn->connected = 0;
4687 conn->host = strdup(host);
4688 conn->path = strdup(path);
4689 if (!conn->host || !conn->path) {
4690 n_log(LOG_ERR, "n_ws_connect: strdup failed");
4691 goto ws_connect_fail;
4692 }
4693
4694 /* establish TCP + optional SSL */
4695 if (use_ssl) {
4696 if (netw_ssl_connect_client(&conn->netw, (char*)host, (char*)port, NETWORK_IPALL) == FALSE) {
4697 n_log(LOG_ERR, "n_ws_connect: TCP+SSL context setup failed for %s:%s", host, port);
4698 goto ws_connect_fail;
4699 }
4700 if (netw_ssl_do_handshake(conn->netw, host) == FALSE) {
4701 _netw_capture_error(conn->netw, "n_ws_connect: SSL handshake failed for %s:%s", host, port);
4702 n_log(LOG_ERR, "n_ws_connect: SSL handshake failed for %s:%s", host, port);
4703 goto ws_connect_fail;
4704 }
4705 } else {
4706 if (netw_connect(&conn->netw, (char*)host, (char*)port, NETWORK_IPALL) == FALSE) {
4707 n_log(LOG_ERR, "n_ws_connect: TCP connect failed for %s:%s", host, port);
4708 goto ws_connect_fail;
4709 }
4710 }
4711
4712 /* generate Sec-WebSocket-Key: base64 of 16 random bytes */
4713 unsigned char rand_bytes[16];
4714 if (RAND_bytes(rand_bytes, 16) != 1) {
4715 _netw_capture_error(conn->netw, "n_ws_connect: RAND_bytes failed");
4716 n_log(LOG_ERR, "n_ws_connect: RAND_bytes failed");
4717 goto ws_connect_fail;
4718 }
4719
4720 N_STR* rand_nstr = new_nstr(16);
4721 __n_assert(rand_nstr, goto ws_connect_fail);
4722 memcpy(rand_nstr->data, rand_bytes, 16);
4723 rand_nstr->written = 16;
4724
4725 N_STR* ws_key_nstr = n_base64_encode(rand_nstr);
4726 free_nstr(&rand_nstr);
4727 __n_assert(ws_key_nstr, goto ws_connect_fail);
4728 /* strip any trailing whitespace from base64 output */
4729 while (ws_key_nstr->written > 0 &&
4730 (ws_key_nstr->data[ws_key_nstr->written - 1] == '\n' ||
4731 ws_key_nstr->data[ws_key_nstr->written - 1] == '\r' ||
4732 ws_key_nstr->data[ws_key_nstr->written - 1] == ' ')) {
4733 ws_key_nstr->data[--ws_key_nstr->written] = '\0';
4734 }
4735
4736 /* build HTTP upgrade request */
4737 char upgrade_req[2048];
4738 int req_len = snprintf(upgrade_req, sizeof(upgrade_req),
4739 "GET %s HTTP/1.1\r\n"
4740 "Host: %s\r\n"
4741 "Upgrade: websocket\r\n"
4742 "Connection: Upgrade\r\n"
4743 "Sec-WebSocket-Key: %s\r\n"
4744 "Sec-WebSocket-Version: 13\r\n"
4745 "\r\n",
4746 path, host, ws_key_nstr->data);
4747
4748 if (req_len < 0 || (size_t)req_len >= sizeof(upgrade_req)) {
4749 _netw_capture_error(conn->netw, "n_ws_connect: upgrade request too large");
4750 n_log(LOG_ERR, "n_ws_connect: upgrade request too large");
4751 free_nstr(&ws_key_nstr);
4752 goto ws_connect_fail;
4753 }
4754
4755 /* send upgrade request */
4756 if (_ws_write(conn, upgrade_req, (size_t)req_len) < 0) {
4757 _netw_capture_error(conn->netw, "n_ws_connect: failed to send upgrade request");
4758 n_log(LOG_ERR, "n_ws_connect: failed to send upgrade request");
4759 free_nstr(&ws_key_nstr);
4760 goto ws_connect_fail;
4761 }
4762
4763 /* read response (up to 4096 bytes, look for \r\n\r\n) */
4764 char resp_buf[4096];
4765 memset(resp_buf, 0, sizeof(resp_buf));
4766 size_t resp_len = 0;
4767 while (resp_len < sizeof(resp_buf) - 1) {
4768 ssize_t r = 0;
4769 if (conn->netw->crypto_algo == NETW_ENCRYPT_OPENSSL && conn->netw->ssl) {
4770 r = SSL_read(conn->netw->ssl, resp_buf + resp_len, 1);
4771 } else {
4772 r = recv(conn->netw->link.sock, resp_buf + resp_len, 1, 0);
4773 }
4774 if (r <= 0) {
4775 _netw_capture_error(conn->netw, "n_ws_connect: failed reading handshake response");
4776 n_log(LOG_ERR, "n_ws_connect: failed reading handshake response");
4777 free_nstr(&ws_key_nstr);
4778 goto ws_connect_fail;
4779 }
4780 resp_len += (size_t)r;
4781 if (resp_len >= 4 &&
4782 resp_buf[resp_len - 4] == '\r' && resp_buf[resp_len - 3] == '\n' &&
4783 resp_buf[resp_len - 2] == '\r' && resp_buf[resp_len - 1] == '\n') {
4784 break;
4785 }
4786 }
4787 resp_buf[resp_len] = '\0';
4788
4789 /* verify 101 Switching Protocols */
4790 if (strstr(resp_buf, "101") == NULL) {
4791 _netw_capture_error(conn->netw, "n_ws_connect: server did not return 101: %.128s", resp_buf);
4792 n_log(LOG_ERR, "n_ws_connect: server did not return 101: %.128s", resp_buf);
4793 free_nstr(&ws_key_nstr);
4794 goto ws_connect_fail;
4795 }
4796
4797 /* verify Sec-WebSocket-Accept */
4798 static const char ws_magic[] = "258EAFA5-E914-47DA-95CA-5AB5FE44F513";
4799 char concat_key[256];
4800 snprintf(concat_key, sizeof(concat_key), "%s%s", ws_key_nstr->data, ws_magic);
4801 n_log(LOG_DEBUG, "n_ws_connect: key sent: [%s] len:%zu", ws_key_nstr->data, ws_key_nstr->written);
4802 n_log(LOG_DEBUG, "n_ws_connect: concat_key: [%s]", concat_key);
4803 free_nstr(&ws_key_nstr);
4804
4805 unsigned char sha1_hash[SHA_DIGEST_LENGTH];
4806 SHA1((const unsigned char*)concat_key, strlen(concat_key), sha1_hash);
4807
4808 N_STR* sha1_nstr = new_nstr(SHA_DIGEST_LENGTH);
4809 __n_assert(sha1_nstr, goto ws_connect_fail);
4810 memcpy(sha1_nstr->data, sha1_hash, SHA_DIGEST_LENGTH);
4811 sha1_nstr->written = SHA_DIGEST_LENGTH;
4812
4813 N_STR* expected_accept = n_base64_encode(sha1_nstr);
4814 free_nstr(&sha1_nstr);
4815 __n_assert(expected_accept, goto ws_connect_fail);
4816 /* strip trailing whitespace from base64 output */
4817 while (expected_accept->written > 0 &&
4818 (expected_accept->data[expected_accept->written - 1] == '\n' ||
4819 expected_accept->data[expected_accept->written - 1] == '\r' ||
4820 expected_accept->data[expected_accept->written - 1] == ' ')) {
4821 expected_accept->data[--expected_accept->written] = '\0';
4822 }
4823
4824 /* find Sec-WebSocket-Accept in response (case-insensitive search) */
4825 const char* accept_hdr = NULL;
4826 char* search_pos = resp_buf;
4827 while (*search_pos) {
4828 if (strncasecmp(search_pos, "Sec-WebSocket-Accept:", 21) == 0) {
4829 accept_hdr = search_pos + 21;
4830 break;
4831 }
4832 search_pos++;
4833 }
4834 if (!accept_hdr) {
4835 _netw_capture_error(conn->netw, "n_ws_connect: no Sec-WebSocket-Accept header in response");
4836 n_log(LOG_ERR, "n_ws_connect: no Sec-WebSocket-Accept header in response");
4837 free_nstr(&expected_accept);
4838 goto ws_connect_fail;
4839 }
4840 /* skip whitespace */
4841 while (*accept_hdr == ' ') accept_hdr++;
4842 /* compare up to expected length; trim trailing \r\n from accept_hdr */
4843 char accept_val[128];
4844 {
4845 int ai = 0;
4846 while (accept_hdr[ai] && accept_hdr[ai] != '\r' && accept_hdr[ai] != '\n' && ai < (int)sizeof(accept_val) - 1) {
4847 accept_val[ai] = accept_hdr[ai];
4848 ai++;
4849 }
4850 accept_val[ai] = '\0';
4851 }
4852 if (strcmp(accept_val, expected_accept->data) != 0) {
4853 /* Many proxies (Fly.io, Cloudflare, etc.) re-key the handshake,
4854 * so the accept may not match. Log as debug, not error. */
4855 n_log(LOG_DEBUG, "n_ws_connect: Sec-WebSocket-Accept mismatch (proxy?): got [%s] expected [%s]",
4856 accept_val, expected_accept->data);
4857 }
4858 free_nstr(&expected_accept);
4859
4860 conn->connected = 1;
4861 n_log(LOG_INFO, "n_ws_connect: WebSocket handshake completed with %s:%s%s", host, port, path);
4862 return conn;
4863
4864ws_connect_fail:
4865 n_ws_conn_free(&conn);
4866 return NULL;
4867}
4868
4880int n_ws_send(N_WS_CONN* conn, const char* payload, size_t len, int opcode) {
4881 __n_assert(conn, return -1);
4882 __n_assert(conn->netw, return -1);
4883
4884 /* max frame overhead: 2 (header) + 8 (ext len) + 4 (mask) = 14 */
4885 size_t frame_max = 14 + len;
4886 unsigned char* frame = NULL;
4887 Malloc(frame, unsigned char, frame_max);
4888 __n_assert(frame, return -1);
4889
4890 size_t pos = 0;
4891
4892 /* byte 0: FIN + opcode */
4893 frame[pos++] = (unsigned char)(0x80 | (opcode & 0x0F));
4894
4895 /* byte 1+: mask bit set + payload length */
4896 if (len <= 125) {
4897 frame[pos++] = (unsigned char)(0x80 | len);
4898 } else if (len <= 65535) {
4899 frame[pos++] = (unsigned char)(0x80 | 126);
4900 frame[pos++] = (unsigned char)((len >> 8) & 0xFF);
4901 frame[pos++] = (unsigned char)(len & 0xFF);
4902 } else {
4903 frame[pos++] = (unsigned char)(0x80 | 127);
4904 for (int i = 7; i >= 0; i--) {
4905 frame[pos++] = (unsigned char)((len >> (8 * i)) & 0xFF);
4906 }
4907 }
4908
4909 /* masking key: 4 random bytes */
4910 unsigned char mask_key[4];
4911 if (RAND_bytes(mask_key, 4) != 1) {
4912 _netw_capture_error(conn->netw, "n_ws_send: RAND_bytes failed for mask key");
4913 n_log(LOG_ERR, "n_ws_send: RAND_bytes failed for mask key");
4914 FreeNoLog(frame);
4915 return -1;
4916 }
4917 memcpy(frame + pos, mask_key, 4);
4918 pos += 4;
4919
4920 /* masked payload */
4921 if (payload && len > 0) {
4922 for (size_t i = 0; i < len; i++) {
4923 frame[pos + i] = (unsigned char)((unsigned char)payload[i] ^ mask_key[i % 4]);
4924 }
4925 pos += len;
4926 }
4927
4928 ssize_t written = _ws_write(conn, frame, pos);
4929 FreeNoLog(frame);
4930 if (written < 0 || (size_t)written != pos) {
4931 _netw_capture_error(conn->netw, "n_ws_send: write failed (wrote %zd of %zu)", written, pos);
4932 n_log(LOG_ERR, "n_ws_send: write failed (wrote %zd of %zu)", written, pos);
4933 return -1;
4934 }
4935 return 0;
4936}
4937
4947int n_ws_recv(N_WS_CONN* conn, N_WS_MESSAGE* msg_out) {
4948 __n_assert(conn, return -1);
4949 __n_assert(conn->netw, return -1);
4950 __n_assert(msg_out, return -1);
4951
4952 memset(msg_out, 0, sizeof(*msg_out));
4953
4954 /* read 2-byte header */
4955 unsigned char hdr[2];
4956 if (_ws_read(conn, hdr, 2) < 0) {
4957 return -1;
4958 }
4959
4960 /* int fin = (hdr[0] >> 7) & 1; */
4961 int opcode = hdr[0] & 0x0F;
4962 int mask_bit = (hdr[1] >> 7) & 1;
4963 uint64_t payload_len = hdr[1] & 0x7F;
4964
4965 /* extended payload length */
4966 if (payload_len == 126) {
4967 unsigned char ext[2];
4968 if (_ws_read(conn, ext, 2) < 0) return -1;
4969 payload_len = ((uint64_t)ext[0] << 8) | (uint64_t)ext[1];
4970 } else if (payload_len == 127) {
4971 unsigned char ext[8];
4972 if (_ws_read(conn, ext, 8) < 0) return -1;
4973 payload_len = 0;
4974 for (int i = 0; i < 8; i++) {
4975 payload_len = (payload_len << 8) | (uint64_t)ext[i];
4976 }
4977 }
4978
4979 /* read masking key if present */
4980 unsigned char mask_key[4] = {0};
4981 if (mask_bit) {
4982 if (_ws_read(conn, mask_key, 4) < 0) return -1;
4983 }
4984
4985 /* read payload */
4986 N_STR* payload = new_nstr((size_t)payload_len + 1);
4987 __n_assert(payload, return -1);
4988
4989 if (payload_len > 0) {
4990 if (_ws_read(conn, payload->data, (size_t)payload_len) < 0) {
4991 free_nstr(&payload);
4992 return -1;
4993 }
4994 /* unmask if needed */
4995 if (mask_bit) {
4996 for (uint64_t i = 0; i < payload_len; i++) {
4997 payload->data[i] = (char)((unsigned char)payload->data[i] ^ mask_key[i % 4]);
4998 }
4999 }
5000 }
5001 payload->written = (size_t)payload_len;
5002 payload->data[payload_len] = '\0';
5003
5004 msg_out->opcode = opcode;
5005 msg_out->payload = payload;
5006 msg_out->masked = mask_bit;
5007
5008 return 0;
5009}
5010
5020 __n_assert(conn, return);
5021
5022 if (conn->connected && conn->netw) {
5023 /* send close frame with empty payload */
5024 n_ws_send(conn, NULL, 0, N_WS_OP_CLOSE);
5025
5026 /* try to read close response with a short timeout */
5027 /* set non-blocking or just try one read */
5028 N_WS_MESSAGE msg;
5029 memset(&msg, 0, sizeof(msg));
5030 /* best effort: read one frame, ignore errors */
5031 if (n_ws_recv(conn, &msg) == 0) {
5032 free_nstr(&msg.payload);
5033 }
5034 conn->connected = 0;
5035 }
5036
5037 if (conn->netw) {
5038 netw_close(&conn->netw);
5039 }
5040}
5041
5050 __n_assert(conn, return);
5051 __n_assert(*conn, return);
5052
5053 N_WS_CONN* c = *conn;
5054
5055 if (c->connected) {
5056 n_ws_close(c);
5057 }
5058
5059 if (c->netw) {
5060 netw_close(&c->netw);
5061 }
5062
5063 FreeNoLog(c->host);
5064 FreeNoLog(c->path);
5065 FreeNoLog(c);
5066 *conn = NULL;
5067}
5068
5074 if (!event) return;
5075 if (event->event) free_nstr(&event->event);
5076 if (event->data) free_nstr(&event->data);
5077 if (event->id) free_nstr(&event->id);
5078 event->retry = 0;
5079}
5080
5086 __n_assert(conn, return);
5087 __atomic_store_n(&conn->stop_flag, 1, __ATOMIC_RELEASE);
5088}
5089
5095 __n_assert(conn, return);
5096 __n_assert(*conn, return);
5097
5098 N_SSE_CONN* c = *conn;
5099 if (c->netw) {
5100 netw_close(&c->netw);
5101 }
5102 FreeNoLog(c);
5103 *conn = NULL;
5104}
5105
5112static ssize_t _sse_read_byte(NETWORK* netw, char* ch, volatile int* stop_flag) {
5113 __n_assert(netw, return -1);
5114
5115 /* poll with 500ms timeout so we can check stop_flag periodically */
5116 for (;;) {
5117 if (stop_flag && __atomic_load_n(stop_flag, __ATOMIC_ACQUIRE)) {
5118 return -1;
5119 }
5120 struct timeval tv;
5121 tv.tv_sec = 0;
5122 tv.tv_usec = 500000; /* 500ms */
5123 fd_set fds;
5124 FD_ZERO(&fds);
5125 FD_SET(netw->link.sock, &fds);
5126
5127 /* For SSL: check if there's already buffered data */
5128 int ready = 0;
5130 if (SSL_pending(netw->ssl) > 0) {
5131 ready = 1;
5132 }
5133 }
5134 if (!ready) {
5135 int sel = select((int)netw->link.sock + 1, &fds, NULL, NULL, &tv);
5136 if (sel < 0) return -1; /* error */
5137 if (sel == 0) continue; /* timeout, loop back to check stop_flag */
5138 }
5139
5140 ssize_t ret = 0;
5142 ret = SSL_read(netw->ssl, ch, 1);
5143 } else {
5144 ret = recv(netw->link.sock, ch, 1, 0);
5145 }
5146 return (ret == 1) ? 1 : -1;
5147 }
5148}
5149
5155static ssize_t _sse_write(NETWORK* netw, const void* buf, size_t len) {
5156 __n_assert(netw, return -1);
5158 int ret = SSL_write(netw->ssl, buf, (int)len);
5159 return (ret > 0) ? (ssize_t)ret : -1;
5160 }
5161 ssize_t ret = send(netw->link.sock, buf, len, NETFLAGS);
5162 return ret;
5163}
5164
5179N_SSE_CONN* n_sse_connect(const char* host, const char* port, const char* path, int use_ssl, void (*on_event)(N_SSE_EVENT*, N_SSE_CONN*, void*), void* user_data) {
5180 __n_assert(host, return NULL);
5181 __n_assert(port, return NULL);
5182 __n_assert(path, return NULL);
5183 __n_assert(on_event, return NULL);
5184
5185 N_SSE_CONN* conn = NULL;
5186 Malloc(conn, N_SSE_CONN, 1);
5187 __n_assert(conn, return NULL);
5188
5189 conn->netw = NULL;
5190 conn->stop_flag = 0;
5191 conn->on_event = on_event;
5192 conn->user_data = user_data;
5193
5194 /* establish TCP + optional SSL */
5195 if (use_ssl) {
5196 if (netw_ssl_connect_client(&conn->netw, (char*)host, (char*)port, NETWORK_IPALL) == FALSE) {
5197 n_log(LOG_ERR, "n_sse_connect: TCP+SSL context setup failed for %s:%s", host, port);
5198 goto sse_connect_fail;
5199 }
5200 if (netw_ssl_do_handshake(conn->netw, host) == FALSE) {
5201 _netw_capture_error(conn->netw, "n_sse_connect: SSL handshake failed for %s:%s", host, port);
5202 n_log(LOG_ERR, "n_sse_connect: SSL handshake failed for %s:%s", host, port);
5203 goto sse_connect_fail;
5204 }
5205 } else {
5206 if (netw_connect(&conn->netw, (char*)host, (char*)port, NETWORK_IPALL) == FALSE) {
5207 n_log(LOG_ERR, "n_sse_connect: TCP connect failed for %s:%s", host, port);
5208 goto sse_connect_fail;
5209 }
5210 }
5211
5212 /* send HTTP GET with SSE headers */
5213 char request[4096];
5214 int req_len = snprintf(request, sizeof(request),
5215 "GET %s HTTP/1.1\r\n"
5216 "Host: %s\r\n"
5217 "Accept: text/event-stream\r\n"
5218 "Cache-Control: no-cache\r\n"
5219 "Connection: keep-alive\r\n"
5220 "\r\n",
5221 path, host);
5222
5223 if (req_len < 0 || (size_t)req_len >= sizeof(request)) {
5224 _netw_capture_error(conn->netw, "n_sse_connect: request too large");
5225 n_log(LOG_ERR, "n_sse_connect: request too large");
5226 goto sse_connect_fail;
5227 }
5228
5229 if (_sse_write(conn->netw, request, (size_t)req_len) < 0) {
5230 _netw_capture_error(conn->netw, "n_sse_connect: failed to send HTTP request");
5231 n_log(LOG_ERR, "n_sse_connect: failed to send HTTP request");
5232 goto sse_connect_fail;
5233 }
5234
5235 /* read HTTP response headers (byte by byte until \r\n\r\n) */
5236 char resp_buf[8192];
5237 size_t resp_len = 0;
5238 while (resp_len < sizeof(resp_buf) - 1) {
5239 char ch = 0;
5240 if (_sse_read_byte(conn->netw, &ch, NULL) < 0) {
5241 _netw_capture_error(conn->netw, "n_sse_connect: failed reading HTTP response");
5242 n_log(LOG_ERR, "n_sse_connect: failed reading HTTP response");
5243 goto sse_connect_fail;
5244 }
5245 resp_buf[resp_len++] = ch;
5246 if (resp_len >= 4 &&
5247 resp_buf[resp_len - 4] == '\r' && resp_buf[resp_len - 3] == '\n' &&
5248 resp_buf[resp_len - 2] == '\r' && resp_buf[resp_len - 1] == '\n') {
5249 break;
5250 }
5251 }
5252 resp_buf[resp_len] = '\0';
5253
5254 /* verify HTTP 200 status */
5255 if (strstr(resp_buf, "200") == NULL) {
5256 _netw_capture_error(conn->netw, "n_sse_connect: server did not return 200: %.128s", resp_buf);
5257 n_log(LOG_ERR, "n_sse_connect: server did not return 200: %.128s", resp_buf);
5258 goto sse_connect_fail;
5259 }
5260
5261 n_log(LOG_INFO, "n_sse_connect: SSE connected to %s:%s%s", host, port, path);
5262
5263 /* enter SSE read loop */
5264 {
5265 N_SSE_EVENT current;
5266 memset(&current, 0, sizeof(current));
5267
5268 /* line buffer for SSE parsing */
5269 char line[8192];
5270 size_t line_len = 0;
5271
5272 while (!__atomic_load_n(&conn->stop_flag, __ATOMIC_ACQUIRE)) {
5273 char ch = 0;
5274 if (_sse_read_byte(conn->netw, &ch, &conn->stop_flag) < 0) {
5275 n_log(LOG_DEBUG, "n_sse_connect: connection closed or read error");
5276 break;
5277 }
5278
5279 if (ch == '\n') {
5280 /* terminate line (strip trailing \r) */
5281 if (line_len > 0 && line[line_len - 1] == '\r') {
5282 line_len--;
5283 }
5284 line[line_len] = '\0';
5285
5286 if (line_len == 0) {
5287 /* empty line = dispatch event if we have data */
5288 if (current.data) {
5289 conn->on_event(&current, conn, conn->user_data);
5290 n_sse_event_clean(&current);
5291 memset(&current, 0, sizeof(current));
5292 }
5293 } else if (line[0] == ':') {
5294 /* comment line, ignore */
5295 n_log(LOG_DEBUG, "n_sse_connect: comment: %s", line + 1);
5296 } else {
5297 /* parse field:value */
5298 char* colon = strchr(line, ':');
5299 const char* field = line;
5300 const char* value = "";
5301 if (colon) {
5302 *colon = '\0';
5303 value = colon + 1;
5304 /* skip single leading space after colon */
5305 if (*value == ' ') value++;
5306 }
5307
5308 if (strcmp(field, "data") == 0) {
5309 if (current.data) {
5310 /* append newline + value to existing data */
5311 nstrprintf_cat(current.data, "\n%s", value);
5312 } else {
5313 current.data = char_to_nstr((char*)value);
5314 }
5315 } else if (strcmp(field, "event") == 0) {
5316 if (current.event) free_nstr(&current.event);
5317 current.event = char_to_nstr((char*)value);
5318 } else if (strcmp(field, "id") == 0) {
5319 if (current.id) free_nstr(&current.id);
5320 current.id = char_to_nstr((char*)value);
5321 } else if (strcmp(field, "retry") == 0) {
5322 current.retry = atoi(value);
5323 }
5324 }
5325
5326 line_len = 0;
5327 } else {
5328 if (line_len < sizeof(line) - 1) {
5329 line[line_len++] = ch;
5330 }
5331 }
5332 }
5333
5334 /* clean up any partial event */
5335 n_sse_event_clean(&current);
5336 }
5337
5338 return conn;
5339
5340sse_connect_fail:
5341 n_sse_conn_free(&conn);
5342 return NULL;
5343}
5344
5345#endif /* HAVE_OPENSSL */
5346
5352static void _n_mock_parse_request(const char* buf, N_HTTP_REQUEST* req) {
5353 if (!buf || !req) return;
5354
5355 /* parse request line: METHOD PATH?QUERY HTTP/1.x */
5356 const char* line_end = strstr(buf, "\r\n");
5357 if (!line_end) line_end = strchr(buf, '\n');
5358 if (!line_end) return;
5359
5360 size_t line_len = (size_t)(line_end - buf);
5361 char line[4096];
5362 if (line_len >= sizeof(line)) line_len = sizeof(line) - 1;
5363 memcpy(line, buf, line_len);
5364 line[line_len] = '\0';
5365
5366 /* method */
5367 char* sp1 = strchr(line, ' ');
5368 if (!sp1) return;
5369 size_t mlen = (size_t)(sp1 - line);
5370 if (mlen >= sizeof(req->method)) mlen = sizeof(req->method) - 1;
5371 memcpy(req->method, line, mlen);
5372 req->method[mlen] = '\0';
5373
5374 /* path and query */
5375 sp1++;
5376 char* sp2 = strchr(sp1, ' ');
5377 if (sp2) *sp2 = '\0';
5378 char* qmark = strchr(sp1, '?');
5379 if (qmark) {
5380 *qmark = '\0';
5381 strncpy(req->query, qmark + 1, sizeof(req->query) - 1);
5382 req->query[sizeof(req->query) - 1] = '\0';
5383 }
5384 strncpy(req->path, sp1, sizeof(req->path) - 1);
5385 req->path[sizeof(req->path) - 1] = '\0';
5386
5387 /* headers: skip past request line */
5388 const char* hdr_start = line_end;
5389 if (*hdr_start == '\r') hdr_start++;
5390 if (*hdr_start == '\n') hdr_start++;
5391
5392 req->headers = new_generic_list(0);
5393
5394 const char* body_start = NULL;
5395 while (hdr_start && *hdr_start) {
5396 const char* next = strstr(hdr_start, "\r\n");
5397 if (!next) next = strchr(hdr_start, '\n');
5398 if (!next) break;
5399
5400 size_t hlen = (size_t)(next - hdr_start);
5401 if (hlen == 0) {
5402 /* blank line = end of headers */
5403 body_start = next;
5404 if (*body_start == '\r') body_start++;
5405 if (*body_start == '\n') body_start++;
5406 break;
5407 }
5408
5409 char* header_line = strndup(hdr_start, hlen);
5410 if (header_line) {
5411 list_push(req->headers, header_line, free);
5412 }
5413
5414 hdr_start = next;
5415 if (*hdr_start == '\r') hdr_start++;
5416 if (*hdr_start == '\n') hdr_start++;
5417 }
5418
5419 /* body */
5420 if (body_start && *body_start) {
5421 req->body = char_to_nstr(body_start);
5422 }
5423}
5424
5430 if (!req) return;
5431 if (req->headers) list_destroy(&req->headers);
5432 if (req->body) free_nstr(&req->body);
5433}
5434
5443 void (*on_request)(N_HTTP_REQUEST*, N_HTTP_RESPONSE*, void*),
5444 void* user_data) {
5445 __n_assert(on_request, return NULL);
5446
5447 N_MOCK_SERVER* server = NULL;
5449 __n_assert(server, return NULL);
5450 memset(server, 0, sizeof(*server));
5451
5452 server->on_request = on_request;
5453 server->user_data = user_data;
5454 server->port = port;
5455 server->stop_flag = 0;
5456
5457 char port_str[16];
5458 snprintf(port_str, sizeof(port_str), "%d", port);
5459
5460 NETWORK* listener = NULL;
5461 if (netw_make_listening(&listener, NULL, port_str, 5, NETWORK_IPALL) != TRUE) {
5462 n_log(LOG_ERR, "n_mock_server_start: failed to listen on port %d", port);
5464 return NULL;
5465 }
5466 server->listener = listener;
5467
5468 n_log(LOG_INFO, "mock server listening on port %d", port);
5469 return server;
5470}
5471
5477 __n_assert(server, return);
5478 __n_assert(server->listener, return);
5479
5480 while (!__atomic_load_n(&server->stop_flag, __ATOMIC_ACQUIRE)) {
5481 /* Use select with timeout to avoid blocking forever */
5482 fd_set readfds;
5483 FD_ZERO(&readfds);
5484 FD_SET(server->listener->link.sock, &readfds);
5485 struct timeval tv;
5486 tv.tv_sec = 0;
5487 tv.tv_usec = 200000; /* 200ms */
5488 int sel = select((int)(server->listener->link.sock + 1), &readfds, NULL, NULL, &tv);
5489 if (sel <= 0) continue;
5490
5491 /* Accept connection using netw_accept_from_ex with 1000ms timeout */
5492 int ret = 0;
5493 NETWORK* client = netw_accept_from_ex(server->listener, 0, 0, 1000, &ret);
5494 if (!client) continue;
5495
5496 /* Read HTTP request */
5497 char buf[8192];
5498 ssize_t n = recv(client->link.sock, buf, sizeof(buf) - 1, 0);
5499 if (n <= 0) {
5500 netw_close(&client);
5501 continue;
5502 }
5503 buf[n] = '\0';
5504
5505 /* Parse request */
5506 N_HTTP_REQUEST req;
5507 memset(&req, 0, sizeof(req));
5508 _n_mock_parse_request(buf, &req);
5509
5510 /* Prepare default response */
5511 N_HTTP_RESPONSE resp;
5512 memset(&resp, 0, sizeof(resp));
5513 resp.status_code = 404;
5514 strncpy(resp.content_type, "text/plain", sizeof(resp.content_type) - 1);
5515
5516 /* Call handler */
5517 server->on_request(&req, &resp, server->user_data);
5518
5519 /* Build HTTP response */
5520 const char* status_msg = netw_get_http_status_message(resp.status_code);
5521 if (!status_msg) status_msg = "Unknown";
5522
5523 size_t body_len = 0;
5524 const char* body_data = "";
5525 if (resp.body && resp.body->data) {
5526 body_data = resp.body->data;
5527 body_len = resp.body->written;
5528 }
5529
5530 char header_buf[1024];
5531 int hlen = snprintf(header_buf, sizeof(header_buf),
5532 "HTTP/1.1 %d %s\r\n"
5533 "Content-Type: %s\r\n"
5534 "Content-Length: %zu\r\n"
5535 "Connection: close\r\n"
5536 "\r\n",
5537 resp.status_code, status_msg,
5538 resp.content_type,
5539 body_len);
5540
5541 if (hlen > 0) {
5542 send(client->link.sock, header_buf, (size_t)hlen, NETFLAGS);
5543 }
5544 if (body_len > 0) {
5545 send(client->link.sock, body_data, body_len, NETFLAGS);
5546 }
5547
5548 /* Cleanup */
5549 if (resp.body) free_nstr(&resp.body);
5551 netw_close(&client);
5552 }
5553
5554 n_log(LOG_INFO, "mock server stopped");
5555}
5556
5562 __n_assert(server, return);
5563 __atomic_store_n(&server->stop_flag, 1, __ATOMIC_RELEASE);
5564}
5565
5571 __n_assert(server && *server, return);
5572 if ((*server)->listener) {
5573 netw_close(&(*server)->listener);
5574 }
5575 FreeNoLog(*server);
5576 *server = NULL;
5577}
5578
5579static void _n_parse_query_params(N_URL* u, const char* qs) {
5580 if (!u || !qs || !qs[0]) return;
5581 char* buf = strdup(qs);
5582 if (!buf) return;
5583 char* saveptr = NULL;
5584 char* tok = strtok_r(buf, "&", &saveptr);
5585 while (tok && u->nb_params < N_URL_MAX_PARAMS) {
5586 char* eq = strchr(tok, '=');
5587 if (eq) {
5588 *eq = '\0';
5589 u->params[u->nb_params].key = strdup(tok);
5590 u->params[u->nb_params].value = strdup(eq + 1);
5591 } else {
5592 u->params[u->nb_params].key = strdup(tok);
5593 u->params[u->nb_params].value = strdup("");
5594 }
5595 u->nb_params++;
5596 tok = strtok_r(NULL, "&", &saveptr);
5597 }
5598 free(buf);
5599}
5600
5601N_URL* n_url_parse(const char* url) {
5602 if (!url) return NULL;
5603 N_URL* u = NULL;
5604 Malloc(u, N_URL, 1);
5605 if (!u) return NULL;
5606 memset(u, 0, sizeof(*u));
5607 const char* p = url;
5608 const char* scheme_end = strstr(p, "://");
5609 if (scheme_end) {
5610 u->scheme = strndup(p, (size_t)(scheme_end - p));
5611 p = scheme_end + 3;
5612 } else {
5613 u->scheme = strdup("http");
5614 }
5615 const char* host_end = p;
5616 while (*host_end && *host_end != '/' && *host_end != '?' && *host_end != ':') host_end++;
5617 u->host = strndup(p, (size_t)(host_end - p));
5618 p = host_end;
5619 if (*p == ':') {
5620 p++;
5621 u->port = atoi(p);
5622 while (*p && *p != '/' && *p != '?') p++;
5623 }
5624 if (*p == '/') {
5625 const char* pe = p;
5626 while (*pe && *pe != '?') pe++;
5627 u->path = strndup(p, (size_t)(pe - p));
5628 p = pe;
5629 } else {
5630 u->path = strdup("/");
5631 }
5632 if (*p == '?') {
5633 p++;
5634 u->query = strdup(p);
5636 }
5637 return u;
5638}
5639
5641 if (!u) return NULL;
5642 N_STR* result = new_nstr(512);
5643 if (!result) return NULL;
5644 nstrprintf(result, "%s://%s", u->scheme ? u->scheme : "http", u->host ? u->host : "localhost");
5645 if (u->port > 0) {
5646 int dp = (u->scheme && strcmp(u->scheme, "https") == 0) ? 443 : 80;
5647 if (u->port != dp) nstrprintf_cat(result, ":%d", u->port);
5648 }
5649 nstrprintf_cat(result, "%s", u->path ? u->path : "/");
5650 if (u->query && u->query[0]) nstrprintf_cat(result, "?%s", u->query);
5651 return result;
5652}
5653
5654N_STR* n_url_encode(const char* str) {
5655 if (!str) return NULL;
5656 size_t len = strlen(str);
5657 N_STR* result = new_nstr(len * 3 + 1);
5658 if (!result) return NULL;
5659 for (size_t i = 0; i < len; i++) {
5660 unsigned char c = (unsigned char)str[i];
5661 if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
5662 nstrprintf_cat(result, "%c", c);
5663 else
5664 nstrprintf_cat(result, "%%%02X", c);
5665 }
5666 return result;
5667}
5668
5669N_STR* n_url_decode(const char* str) {
5670 if (!str) return NULL;
5671 size_t len = strlen(str);
5672 N_STR* result = new_nstr(len + 1);
5673 if (!result) return NULL;
5674 for (size_t i = 0; i < len; i++) {
5675 if (str[i] == '%' && i + 2 < len && isxdigit((unsigned char)str[i + 1]) && isxdigit((unsigned char)str[i + 2])) {
5676 const char hex[3] = {str[i + 1], str[i + 2], '\0'};
5677 unsigned int val = 0;
5678 sscanf(hex, "%x", &val);
5679 nstrprintf_cat(result, "%c", (char)val);
5680 i += 2;
5681 } else if (str[i] == '+') {
5682 nstrprintf_cat(result, " ");
5683 } else {
5684 nstrprintf_cat(result, "%c", str[i]);
5685 }
5686 }
5687 return result;
5688}
5689
5691 if (!u || !*u) return;
5692 N_URL* p = *u;
5693 FreeNoLog(p->scheme);
5694 FreeNoLog(p->host);
5695 FreeNoLog(p->path);
5696 FreeNoLog(p->query);
5697 for (int i = 0; i < p->nb_params; i++) {
5698 FreeNoLog(p->params[i].key);
5699 FreeNoLog(p->params[i].value);
5700 }
5701 FreeNoLog(p);
5702 *u = NULL;
5703}
5704
5711static int _proxy_tcp_connect(const char* host, int port) {
5712 char port_str[16];
5713 snprintf(port_str, sizeof(port_str), "%d", port);
5714
5715 struct addrinfo hints;
5716 memset(&hints, 0, sizeof(hints));
5717 hints.ai_family = AF_UNSPEC;
5718 hints.ai_socktype = SOCK_STREAM;
5719
5720 struct addrinfo* res = NULL;
5721 int gai_rc = getaddrinfo(host, port_str, &hints, &res);
5722 if (gai_rc != 0 || !res) {
5723 n_log(LOG_ERR, "n_proxy: DNS lookup failed for %s:%d: %s",
5724 host, port, gai_strerror(gai_rc));
5725 return -1;
5726 }
5727
5728 int fd = -1;
5729 struct addrinfo* rp = NULL;
5730 for (rp = res; rp; rp = rp->ai_next) {
5731 fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
5732 if (fd < 0) continue;
5733 if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) break;
5734 close(fd);
5735 fd = -1;
5736 }
5737 freeaddrinfo(res);
5738
5739 if (fd < 0) {
5740 n_log(LOG_ERR, "n_proxy: TCP connect to %s:%d failed", host, port);
5741 }
5742 return fd;
5743}
5744
5746 if (!url) return NULL;
5747
5748 /* Expect scheme://... */
5749 const char* sep = strstr(url, "://");
5750 if (!sep) {
5751 n_log(LOG_ERR, "n_proxy_cfg_parse: no scheme in '%s'", url);
5752 return NULL;
5753 }
5754
5755 size_t scheme_len = (size_t)(sep - url);
5756 if (scheme_len == 0 || scheme_len > 16) return NULL;
5757
5758 char scheme[17];
5759 memcpy(scheme, url, scheme_len);
5760 scheme[scheme_len] = '\0';
5761
5762 /* Only http, https, and socks5 */
5763 if (strcmp(scheme, "http") != 0 && strcmp(scheme, "https") != 0 && strcmp(scheme, "socks5") != 0) {
5764 n_log(LOG_ERR, "n_proxy_cfg_parse: unsupported scheme '%s'", scheme);
5765 return NULL;
5766 }
5767
5768 const char* after = sep + 3; /* after :// */
5769 if (!*after) return NULL;
5770
5771 /* Check for user:pass@host:port */
5772 const char* at = strchr(after, '@');
5773 const char* host_start = NULL;
5774 char* username = NULL;
5775 char* password = NULL;
5776
5777 if (at) {
5778 /* Parse user:pass */
5779 const char* colon = strchr(after, ':');
5780 if (colon && colon < at) {
5781 username = strndup(after, (size_t)(colon - after));
5782 password = strndup(colon + 1, (size_t)(at - colon - 1));
5783 } else {
5784 username = strndup(after, (size_t)(at - after));
5785 }
5786 host_start = at + 1;
5787 } else {
5788 host_start = after;
5789 }
5790
5791 /* Parse host:port */
5792 /* Handle IPv6 [host]:port */
5793 char* hostname = NULL;
5794 int port = 0;
5795
5796 if (host_start[0] == '[') {
5797 const char* bracket = strchr(host_start, ']');
5798 if (!bracket) goto fail;
5799 hostname = strndup(host_start + 1, (size_t)(bracket - host_start - 1));
5800 if (bracket[1] == ':') {
5801 port = atoi(bracket + 2);
5802 }
5803 } else {
5804 const char* colon = strchr(host_start, ':');
5805 if (colon) {
5806 hostname = strndup(host_start, (size_t)(colon - host_start));
5807 port = atoi(colon + 1);
5808 } else {
5809 hostname = strdup(host_start);
5810 }
5811 }
5812
5813 if (!hostname || !hostname[0]) goto fail;
5814 if (port <= 0 || port > 65535) {
5815 /* Default ports */
5816 if (strcmp(scheme, "http") == 0 || strcmp(scheme, "https") == 0)
5817 port = 3128;
5818 else if (strcmp(scheme, "socks5") == 0)
5819 port = 1080;
5820 }
5821
5822 N_PROXY_CFG* cfg = NULL;
5823 Malloc(cfg, N_PROXY_CFG, 1);
5824 if (!cfg) goto fail;
5825 memset(cfg, 0, sizeof(*cfg));
5826 cfg->scheme = strdup(scheme);
5827 cfg->host = hostname;
5828 cfg->port = port;
5829 cfg->username = username;
5830 cfg->password = password;
5831 return cfg;
5832
5833fail:
5834 FreeNoLog(hostname);
5835 FreeNoLog(username);
5836 FreeNoLog(password);
5837 return NULL;
5838}
5839
5841 if (!cfg || !*cfg) return;
5842 N_PROXY_CFG* p = *cfg;
5843 FreeNoLog(p->scheme);
5844 FreeNoLog(p->host);
5845 FreeNoLog(p->username);
5846 FreeNoLog(p->password);
5847 FreeNoLog(p);
5848 *cfg = NULL;
5849}
5850
5852 const char* target_host,
5853 int target_port) {
5854 if (!proxy || !target_host) return -1;
5855
5856 int fd = _proxy_tcp_connect(proxy->host, proxy->port);
5857 if (fd < 0) return -1;
5858
5859 /* Build CONNECT request */
5860 char connect_req[2048];
5861 int len = 0;
5862
5863 len = snprintf(connect_req, sizeof(connect_req),
5864 "CONNECT %s:%d HTTP/1.1\r\n"
5865 "Host: %s:%d\r\n",
5866 target_host, target_port,
5867 target_host, target_port);
5868
5869 /* Proxy-Authorization if credentials present */
5870 if (proxy->username && proxy->username[0]) {
5871 char cred[512];
5872 snprintf(cred, sizeof(cred), "%s:%s",
5873 proxy->username, proxy->password ? proxy->password : "");
5874 N_STR* cred_nstr = char_to_nstr(cred);
5875 if (cred_nstr) {
5876 N_STR* b64 = n_base64_encode(cred_nstr);
5877 if (b64 && b64->data) {
5878 len += snprintf(connect_req + len,
5879 sizeof(connect_req) - (size_t)len,
5880 "Proxy-Authorization: Basic %s\r\n",
5881 b64->data);
5882 }
5883 if (b64) free_nstr(&b64);
5884 free_nstr(&cred_nstr);
5885 }
5886 }
5887
5888 len += snprintf(connect_req + len, sizeof(connect_req) - (size_t)len,
5889 "\r\n");
5890
5891 /* Send CONNECT */
5892 ssize_t sent = send(fd, connect_req, (size_t)len, 0);
5893 if (sent != (ssize_t)len) {
5894 n_log(LOG_ERR, "n_proxy_connect_tunnel: send CONNECT failed");
5895 close(fd);
5896 return -1;
5897 }
5898
5899 /* Read response */
5900 char resp_buf[4096];
5901 ssize_t nr = recv(fd, resp_buf, sizeof(resp_buf) - 1, 0);
5902 if (nr <= 0) {
5903 n_log(LOG_ERR, "n_proxy_connect_tunnel: no response from proxy");
5904 close(fd);
5905 return -1;
5906 }
5907 resp_buf[nr] = '\0';
5908
5909 /* Check for "HTTP/1.x 200" */
5910 if (strstr(resp_buf, " 200 ") == NULL) {
5911 n_log(LOG_ERR, "n_proxy_connect_tunnel: proxy rejected CONNECT: %.*s",
5912 (int)(strchr(resp_buf, '\r') ? strchr(resp_buf, '\r') - resp_buf : nr),
5913 resp_buf);
5914 close(fd);
5915 return -1;
5916 }
5917
5918 n_log(LOG_DEBUG, "n_proxy_connect_tunnel: tunnel established to %s:%d via %s:%d",
5919 target_host, target_port, proxy->host, proxy->port);
5920 return fd;
5921}
5922
5923#ifdef HAVE_OPENSSL
5925 const char* target_host,
5926 int target_port) {
5927 if (!proxy || !target_host) return -1;
5928
5929 int fd = _proxy_tcp_connect(proxy->host, proxy->port);
5930 if (fd < 0) return -1;
5931
5932 /* TLS handshake to the proxy itself */
5934
5935#if OPENSSL_VERSION_NUMBER >= 0x10100000L
5936 const SSL_METHOD* method = TLS_client_method();
5937#else
5938 const SSL_METHOD* method = TLSv1_2_client_method();
5939#endif
5940 SSL_CTX* ctx = SSL_CTX_new(method);
5941 if (!ctx) {
5942 n_log(LOG_ERR, "n_proxy_connect_tunnel_ssl: SSL_CTX_new failed");
5943 close(fd);
5944 return -1;
5945 }
5946 SSL_CTX_set_default_verify_paths(ctx);
5947
5948 SSL* ssl = SSL_new(ctx);
5949 if (!ssl) {
5950 n_log(LOG_ERR, "n_proxy_connect_tunnel_ssl: SSL_new failed");
5951 SSL_CTX_free(ctx);
5952 close(fd);
5953 return -1;
5954 }
5955 SSL_set_fd(ssl, fd);
5956 SSL_set_tlsext_host_name(ssl, proxy->host);
5957
5958 if (SSL_connect(ssl) <= 0) {
5959 n_log(LOG_ERR, "n_proxy_connect_tunnel_ssl: TLS handshake to proxy %s:%d failed",
5960 proxy->host, proxy->port);
5961 SSL_free(ssl);
5962 SSL_CTX_free(ctx);
5963 close(fd);
5964 return -1;
5965 }
5966
5967 n_log(LOG_DEBUG, "n_proxy_connect_tunnel_ssl: TLS established to proxy %s:%d",
5968 proxy->host, proxy->port);
5969
5970 /* Build CONNECT request */
5971 char connect_req[2048];
5972 int len = 0;
5973
5974 len = snprintf(connect_req, sizeof(connect_req),
5975 "CONNECT %s:%d HTTP/1.1\r\n"
5976 "Host: %s:%d\r\n",
5977 target_host, target_port,
5978 target_host, target_port);
5979
5980 /* Proxy-Authorization if credentials present */
5981 if (proxy->username && proxy->username[0]) {
5982 char cred[512];
5983 snprintf(cred, sizeof(cred), "%s:%s",
5984 proxy->username, proxy->password ? proxy->password : "");
5985 N_STR* cred_nstr = char_to_nstr(cred);
5986 if (cred_nstr) {
5987 N_STR* b64 = n_base64_encode(cred_nstr);
5988 if (b64 && b64->data) {
5989 len += snprintf(connect_req + len,
5990 sizeof(connect_req) - (size_t)len,
5991 "Proxy-Authorization: Basic %s\r\n",
5992 b64->data);
5993 }
5994 if (b64) free_nstr(&b64);
5995 free_nstr(&cred_nstr);
5996 }
5997 }
5998
5999 len += snprintf(connect_req + len, sizeof(connect_req) - (size_t)len,
6000 "\r\n");
6001
6002 /* Send CONNECT over TLS */
6003 if (SSL_write(ssl, connect_req, len) != len) {
6004 n_log(LOG_ERR, "n_proxy_connect_tunnel_ssl: send CONNECT failed");
6005 SSL_free(ssl);
6006 SSL_CTX_free(ctx);
6007 close(fd);
6008 return -1;
6009 }
6010
6011 /* Read response over TLS */
6012 char resp_buf[4096];
6013 int nr = SSL_read(ssl, resp_buf, (int)(sizeof(resp_buf) - 1));
6014 if (nr <= 0) {
6015 n_log(LOG_ERR, "n_proxy_connect_tunnel_ssl: no response from proxy");
6016 SSL_free(ssl);
6017 SSL_CTX_free(ctx);
6018 close(fd);
6019 return -1;
6020 }
6021 resp_buf[nr] = '\0';
6022
6023 /* Check for "HTTP/1.x 200" */
6024 if (strstr(resp_buf, " 200 ") == NULL) {
6025 n_log(LOG_ERR, "n_proxy_connect_tunnel_ssl: proxy rejected CONNECT: %.*s",
6026 (int)(strchr(resp_buf, '\r') ? strchr(resp_buf, '\r') - resp_buf : nr),
6027 resp_buf);
6028 SSL_free(ssl);
6029 SSL_CTX_free(ctx);
6030 close(fd);
6031 return -1;
6032 }
6033
6034 /* Tunnel established — tear down the proxy TLS session but keep the fd.
6035 * SSL_shutdown sends close_notify; the proxy knows the CONNECT handshake
6036 * is done and the raw tunnel begins. We call SSL_set_quiet_shutdown to
6037 * avoid waiting for the peer's close_notify (the proxy won't send one
6038 * mid-tunnel). */
6039 SSL_set_quiet_shutdown(ssl, 1);
6040 SSL_shutdown(ssl);
6041 SSL_free(ssl);
6042 SSL_CTX_free(ctx);
6043
6044 n_log(LOG_DEBUG, "n_proxy_connect_tunnel_ssl: tunnel established to %s:%d via https://%s:%d",
6045 target_host, target_port, proxy->host, proxy->port);
6046 return fd;
6047}
6048#endif /* HAVE_OPENSSL */
6049
6051 const char* target_host,
6052 int target_port) {
6053 if (!proxy || !target_host) return -1;
6054
6055 int fd = _proxy_tcp_connect(proxy->host, proxy->port);
6056 if (fd < 0) return -1;
6057
6058 int use_auth = (proxy->username && proxy->username[0]) ? 1 : 0;
6059
6060 /* SOCKS5 greeting */
6061 char greeting[4];
6062 if (use_auth) {
6063 greeting[0] = 0x05; /* version */
6064 greeting[1] = 0x02; /* 2 methods */
6065 greeting[2] = 0x00; /* no auth */
6066 greeting[3] = 0x02; /* user/pass */
6067 if (send(fd, greeting, 4, 0) != 4) goto fail;
6068 } else {
6069 greeting[0] = 0x05;
6070 greeting[1] = 0x01;
6071 greeting[2] = 0x00;
6072 if (send(fd, greeting, 3, 0) != 3) goto fail;
6073 }
6074
6075 /* Read server method selection */
6076 char method_resp[2];
6077 if (recv(fd, method_resp, 2, 0) != 2) goto fail;
6078 if (method_resp[0] != 0x05) goto fail;
6079
6080 if (method_resp[1] == 0x02 && use_auth) {
6081 /* User/pass auth sub-negotiation (RFC 1929) */
6082 size_t ulen = strlen(proxy->username);
6083 size_t plen = proxy->password ? strlen(proxy->password) : 0;
6084 if (ulen > 255 || plen > 255) goto fail;
6085
6086 char auth_req[515]; /* 1+1+255+1+255 */
6087 size_t pos = 0;
6088 auth_req[pos++] = 0x01; /* version */
6089 auth_req[pos++] = (char)ulen;
6090 memcpy(auth_req + pos, proxy->username, ulen);
6091 pos += ulen;
6092 auth_req[pos++] = (char)plen;
6093 if (plen > 0) {
6094 memcpy(auth_req + pos, proxy->password, plen);
6095 pos += plen;
6096 }
6097 if (send(fd, auth_req, pos, 0) != (ssize_t)pos) goto fail;
6098
6099 char auth_resp[2];
6100 if (recv(fd, auth_resp, 2, 0) != 2) goto fail;
6101 if (auth_resp[1] != 0x00) {
6102 n_log(LOG_ERR, "n_proxy_connect_socks5: auth rejected");
6103 goto fail;
6104 }
6105 } else if (method_resp[1] != 0x00) {
6106 n_log(LOG_ERR, "n_proxy_connect_socks5: no acceptable method");
6107 goto fail;
6108 }
6109
6110 /* Send connect request (domain name addressing) */
6111 {
6112 size_t hlen = strlen(target_host);
6113 if (hlen > 255) goto fail;
6114
6115 char conn_req[263]; /* 4 + 1 + 255 + 2 */
6116 size_t pos = 0;
6117 conn_req[pos++] = 0x05; /* version */
6118 conn_req[pos++] = 0x01; /* connect */
6119 conn_req[pos++] = 0x00; /* reserved */
6120 conn_req[pos++] = 0x03; /* domain name */
6121 conn_req[pos++] = (char)hlen;
6122 memcpy(conn_req + pos, target_host, hlen);
6123 pos += hlen;
6124 conn_req[pos++] = (char)((target_port >> 8) & 0xFF);
6125 conn_req[pos++] = (char)(target_port & 0xFF);
6126
6127 if (send(fd, conn_req, pos, 0) != (ssize_t)pos) goto fail;
6128 }
6129
6130 /* Read connect response */
6131 {
6132 char conn_resp[10];
6133 /* Minimum response: 4 bytes header + address (varies) */
6134 ssize_t nr = recv(fd, conn_resp, sizeof(conn_resp), 0);
6135 if (nr < 4) goto fail;
6136 if (conn_resp[0] != 0x05 || conn_resp[1] != 0x00) {
6137 n_log(LOG_ERR, "n_proxy_connect_socks5: connect failed, reply=%02x",
6138 conn_resp[1]);
6139 goto fail;
6140 }
6141
6142 /* If address type is domain (0x03), we may need to read more */
6143 if (conn_resp[3] == 0x03 && nr < 5) {
6144 /* Read remaining bytes */
6145 char extra[256];
6146 recv(fd, extra, sizeof(extra), 0);
6147 } else if (conn_resp[3] == 0x04 && nr < 10) {
6148 /* IPv6: 16 + 2 bytes remaining */
6149 char extra[18];
6150 size_t need = 22 - (size_t)nr; /* total IPv6 response = 4+16+2=22 */
6151 if (need <= sizeof(extra)) {
6152 recv(fd, extra, need, 0);
6153 }
6154 }
6155 }
6156
6157 n_log(LOG_DEBUG, "n_proxy_connect_socks5: tunnel established to %s:%d via %s:%d",
6158 target_host, target_port, proxy->host, proxy->port);
6159 return fd;
6160
6161fail:
6162 n_log(LOG_ERR, "n_proxy_connect_socks5: handshake failed");
6163 close(fd);
6164 return -1;
6165}
int DONE
Definition ex_fluid.c:59
static int mode
static char * port_str
static NETWORK_POOL * pool
NETWORK * netw
Network for server mode, accepting incomming.
Definition ex_network.c:38
static void on_request(N_HTTP_REQUEST *req, N_HTTP_RESPONSE *resp, void *user_data)
Request handler: returns JSON for GET /api/test, 404 otherwise.
char * ca_file
NETWORK * server
char * key
int ip_version
char * addr
char * cert
char * port
#define init_lock(__rwlock_mutex)
Macro for initializing a rwlock.
Definition n_common.h:349
#define FreeNoLog(__ptr)
Free Handler without log.
Definition n_common.h:271
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:203
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:278
#define _str(__PTR)
define true
Definition n_common.h:192
#define rw_lock_destroy(__rwlock_mutex)
Macro to destroy rwlock mutex.
Definition n_common.h:408
#define unlock(__rwlock_mutex)
Macro for releasing read/write lock a rwlock mutex.
Definition n_common.h:395
#define endif
close a ifwhatever block
Definition n_common.h:324
#define write_lock(__rwlock_mutex)
Macro for acquiring a write lock on a rwlock mutex.
Definition n_common.h:381
#define Free(__ptr)
Free Handler to get errors.
Definition n_common.h:262
#define read_lock(__rwlock_mutex)
Macro for acquiring a read lock on a rwlock mutex.
Definition n_common.h:367
#define _nstr(__PTR)
N_STR or "NULL" string for logging purposes.
Definition n_common.h:198
N_STR * n_base64_encode(N_STR *input)
encode a N_STR *string
Definition n_base64.c:269
#define N_ENUM_DEFINE(MACRO_DEFINITION, enum_name)
Macro to define an N_ENUM.
Definition n_enum.h:165
#define N_ENUM_ENTRY(class, method)
helper to build an N_ENUM
Definition n_enum.h:45
size_t nb_keys
total number of used keys in the table
Definition n_hash.h:141
int ht_get_ptr(HASH_TABLE *table, const char *key, void **val)
get pointer at 'key' from 'table'
Definition n_hash.c:2100
#define ht_foreach(__ITEM_, __HASH_)
ForEach macro helper (classic / old)
Definition n_hash.h:191
int destroy_ht(HASH_TABLE **table)
empty a table and destroy it
Definition n_hash.c:2234
int ht_remove(HASH_TABLE *table, const char *key)
remove and delete node at key in table
Definition n_hash.c:2192
HASH_TABLE * new_ht(size_t size)
Create a hash table with the given size.
Definition n_hash.c:2001
int ht_put_ptr(HASH_TABLE *table, const char *key, void *ptr, void(*destructor)(void *ptr), void *(*duplicator)(void *ptr))
put an arbitrary pointer value with given key in the targeted hash table
Definition n_hash.c:2154
int ht_put_string(HASH_TABLE *table, const char *key, char *string)
put a string value (copy/dup) with given key in the targeted hash table
Definition n_hash.c:2167
#define hash_val(node, type)
Cast a HASH_NODE element.
Definition n_hash.h:187
structure of a hash table node
Definition n_hash.h:111
structure of a hash table
Definition n_hash.h:137
size_t nb_items
number of item currently in the list
Definition n_list.h:60
#define list_shift(__LIST_, __TYPE_)
Shift macro helper for void pointer casting.
Definition n_list.h:95
int list_empty(LIST *list)
Empty a LIST list of pointers.
Definition n_list.c:500
LIST_NODE * list_search(LIST *list, const void *ptr)
search ptr in list
Definition n_list.c:468
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
Definition n_list.c:227
#define list_foreach(__ITEM_, __LIST_)
ForEach macro helper, safe for node removal during iteration.
Definition n_list.h:88
#define remove_list_node(__LIST_, __NODE_, __TYPE_)
Remove macro helper for void pointer casting.
Definition n_list.h:97
int list_destroy(LIST **list)
Empty and Free a list container.
Definition n_list.c:547
LIST * new_generic_list(size_t max_items)
Initialiaze a generic list container to max_items pointers.
Definition n_list.c:36
#define MAX_LIST_ITEMS
flag to pass to new_generic_list for the maximum possible number of item in a list
Definition n_list.h:74
Structure of a generic list node.
Definition n_list.h:43
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:88
#define LOG_DEBUG
debug-level messages
Definition n_log.h:83
#define LOG_ERR
error conditions
Definition n_log.h:75
#define LOG_WARNING
warning conditions
Definition n_log.h:77
#define LOG_INFO
informational
Definition n_log.h:81
size_t written
size of the written data inside the string
Definition n_str.h:66
char * data
the string
Definition n_str.h:62
size_t length
length of string (in case we wanna keep information after the 0 end of string value)
Definition n_str.h:64
void free_nstr_ptr(void *ptr)
Free a N_STR pointer structure.
Definition n_str.c:69
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:201
#define nstrcat(__nstr_dst, __nstr_src)
Macro to quickly concatenate two N_STR.
Definition n_str.h:123
N_STR * nstrdup(N_STR *str)
Duplicate a N_STR.
Definition n_str.c:708
#define nstrprintf_cat(__nstr_var, __format,...)
Macro to quickly allocate and sprintf and cat to a N_STR.
Definition n_str.h:119
N_STR * char_to_nstr(const char *src)
Convert a char into a N_STR, short version.
Definition n_str.c:254
N_STR * new_nstr(NSTRBYTE size)
create a new N_STR string
Definition n_str.c:206
#define nstrprintf(__nstr_var, __format,...)
Macro to quickly allocate and sprintf to N_STR.
Definition n_str.h:115
A box including a string and his lenght.
Definition n_str.h:60
void u_sleep(unsigned int usec)
wrapper around usleep for API consistency
Definition n_time.c:53
N_STR * netmsg_make_position_msg(int id, double X, double Y, double vx, double vy, double acc_x, double acc_y, int time_stamp)
make a network NETMSG_POSITION message with given parameters
N_STR * netmsg_make_ident(int type, int id, N_STR *name, N_STR *passwd)
Add a formatted NETWMSG_IDENT message to the specified network.
N_STR * netmsg_make_quit_msg(void)
make a generic network NETMSG_QUIT message
N_STR * netmsg_make_ping(int type, int id_from, int id_to, int time)
Make a ping message to send to a network.
N_STR * netmsg_make_string_msg(int id_from, int id_to, N_STR *name, N_STR *chan, N_STR *txt, int color)
make a network NETMSG_STRING message with given parameters
volatile int stop_flag
atomic stop flag
Definition n_network.h:725
char query[2048]
query string (or empty)
Definition n_network.h:748
char * ip
ip of the connected socket
Definition n_network.h:244
N_SOCKET link
networking socket
Definition n_network.h:326
char * certificate
openssl certificate file
Definition n_network.h:320
char netw_errors[8][512]
per-connection error capture ring buffer (max 8 entries, 512 chars each)
Definition n_network.h:351
int threaded_engine_status
Threaded network engine state for this network.
Definition n_network.h:264
size_t content_length
Store content length.
Definition n_network.h:385
char * type
Type of request.
Definition n_network.h:389
pthread_t send_thr
sending thread
Definition n_network.h:334
int nb_pending
Nb pending connection,if listening.
Definition n_network.h:260
int so_reuseaddr
so reuseaddr state
Definition n_network.h:270
pthread_t recv_thr
receiving thread
Definition n_network.h:336
int port
port number (0 if not specified)
Definition n_network.h:655
char * host
proxy hostname
Definition n_network.h:556
pthread_rwlock_t rwlock
thread safety
Definition n_network.h:376
NETWORK * netw
underlying network connection
Definition n_network.h:724
struct sockaddr_storage raddr
connected remote addr
Definition n_network.h:254
char * host
hostname
Definition n_network.h:654
char * path
path starting with "/", or "/" if empty
Definition n_network.h:656
pthread_mutex_t eventbolt
mutex for threaded access of state event
Definition n_network.h:343
const SSL_METHOD * method
SSL method container.
Definition n_network.h:314
N_STR * body
response body
Definition n_network.h:757
int netw_err_next
next write slot in ring buffer
Definition n_network.h:355
int deplete_socket_timeout
deplete socket send buffer timeout ( 0 disabled, > 0 wait for timeout and check unset/unack datas)
Definition n_network.h:298
char * scheme
"http" or "https"
Definition n_network.h:653
int deplete_queues_timeout
deplete network queues timeout ( 0 disabled, > 0 wait for timeout and check unset/unack datas)
Definition n_network.h:296
int nb_running_threads
nb running threads, if > 0 thread engine is still running
Definition n_network.h:294
pthread_mutex_t recvbolt
mutex for threaded access of recv buf
Definition n_network.h:341
NETWORK * netw
underlying network connection
Definition n_network.h:689
void(* on_event)(N_SSE_EVENT *event, struct N_SSE_CONN *conn, void *user_data)
callback
Definition n_network.h:726
pthread_mutex_t sendbolt
mutex for threaded access of send_buf
Definition n_network.h:339
int send_queue_consecutive_wait
send queue consecutive pool interval, used when there are still items to send, in usec
Definition n_network.h:268
N_STR * event
event type (or NULL for default)
Definition n_network.h:714
int connected
1 if handshake completed
Definition n_network.h:690
N_STR * data
event data
Definition n_network.h:715
N_STR * body
request body (or NULL)
Definition n_network.h:750
char * key
parameter name
Definition n_network.h:647
int so_rcvtimeo
send timeout value
Definition n_network.h:284
char * password
NULL if no auth.
Definition n_network.h:559
char * query
raw query string without leading '?', or NULL
Definition n_network.h:657
int port
proxy port
Definition n_network.h:557
N_URL_PARAM params[64]
parsed key=value pairs
Definition n_network.h:658
int tcpnodelay
state of naggle algorythm, 0 untouched, 1 forcibly disabled
Definition n_network.h:276
char * body
Pointer to the body data.
Definition n_network.h:387
LIST * headers
list of char* "Name: Value" strings
Definition n_network.h:749
char * value
parameter value
Definition n_network.h:648
char * host
remote hostname
Definition n_network.h:691
int netw_err_count
number of captured errors
Definition n_network.h:353
SOCKET sock
a normal socket
Definition n_network.h:242
char path[2048]
request path
Definition n_network.h:747
char * scheme
"http", "https", or "socks5"
Definition n_network.h:555
char * port
port of socket
Definition n_network.h:240
LIST * recv_buf
reveicing buffer (for incomming usage)
Definition n_network.h:331
int transport_type
transport type: NETWORK_TCP (0) or NETWORK_UDP (1)
Definition n_network.h:302
int nb_params
number of parsed parameters
Definition n_network.h:659
int user_id
if part of a user property, id of the user
Definition n_network.h:292
sem_t send_blocker
block sending func
Definition n_network.h:345
SSL_CTX * ctx
SSL context holder.
Definition n_network.h:316
int so_sndbuf
size of the socket send buffer, 0 untouched, else size in bytes
Definition n_network.h:278
int so_sndtimeo
send timeout value
Definition n_network.h:282
int retry
retry interval in ms (0 if not set)
Definition n_network.h:717
char content_type[256]
Store content type.
Definition n_network.h:383
N_STR * id
last event ID (or NULL)
Definition n_network.h:716
struct addrinfo hints
address of local machine
Definition n_network.h:250
int so_keepalive
so keepalive state
Definition n_network.h:274
netw_func send_data
send func ptr
Definition n_network.h:308
int addr_infos_loaded
Internal flag to know if we have to free addr infos.
Definition n_network.h:266
char * path
resource path
Definition n_network.h:692
N_STR * payload
message payload
Definition n_network.h:683
char method[16]
HTTP method.
Definition n_network.h:746
int opcode
frame opcode
Definition n_network.h:682
int masked
1 if masked
Definition n_network.h:684
LIST * pools
pointers to network pools if members of any
Definition n_network.h:348
char * username
NULL if no auth.
Definition n_network.h:558
int status_code
HTTP status code.
Definition n_network.h:755
char * key
openssl key file
Definition n_network.h:322
SSL * ssl
SSL handle.
Definition n_network.h:318
int so_linger
close lingering value (-1 disabled, 0 force close, >0 linger )
Definition n_network.h:286
unsigned long int is_blocking
flag to quickly check socket mode
Definition n_network.h:247
int crypto_algo
if encryption is on, which one (flags NETW_ENCRYPT_*)
Definition n_network.h:290
char content_type[128]
Content-Type header value.
Definition n_network.h:756
HASH_TABLE * pool
table of clients
Definition n_network.h:373
LIST * send_buf
sending buffer (for outgoing queuing )
Definition n_network.h:329
int mode
NETWORK mode , 1 listening, 0 connecting.
Definition n_network.h:262
int so_rcvbuf
size of the socket recv buffer, 0 untouched, else size in bytes
Definition n_network.h:280
void * user_data
user data for callback
Definition n_network.h:727
netw_func recv_data
receive func ptr
Definition n_network.h:310
int wait_close_timeout
network wait close timeout value ( < 1 disabled, >= 1 timeout sec )
Definition n_network.h:300
#define NETW_SOCKET_ERROR
code for a socket error
Definition n_network.h:69
int netw_send_string_to_all(NETWORK *netw, N_STR *name, N_STR *chan, N_STR *txt, int color)
Add a string to the network, aiming all server-side users.
Definition n_network.c:4136
#define N_URL_MAX_PARAMS
maximum number of parsed query parameters
Definition n_network.h:643
N_STR * netw_get_msg(NETWORK *netw)
Get a message from aimed NETWORK.
Definition n_network.c:2977
int netw_add_msg(NETWORK *netw, N_STR *msg)
Add a message to send in aimed NETWORK.
Definition n_network.c:2914
const char * n_netw_get_connect_error(int index)
Get pre-connection error message by index.
Definition n_network.c:115
ssize_t send_ssl_data(void *netw, char *buf, uint32_t n)
send data onto the socket
Definition n_network.c:3512
char * netw_extract_http_request_type(const char *request)
function to extract the request method from an http request
Definition n_network.c:4225
int netw_get_queue_status(NETWORK *netw, size_t *nb_to_send, size_t *nb_to_read)
retrieve network send queue status
Definition n_network.c:3842
int netw_bind_udp(NETWORK **netw, char *addr, char *port, int ip_version)
Create a UDP bound socket for receiving datagrams.
Definition n_network.c:2360
int netw_set_crypto_pem(NETWORK *netw, const char *key_pem, const char *cert_pem)
activate SSL encryption using PEM-formatted key and certificate strings loaded from memory
Definition n_network.c:1395
int netw_init_wsa(int mode, int v1, int v2)
Do not directly use, internal api.
Definition n_network.c:824
int netw_ssl_set_verify(NETWORK *netw, int enable)
enable or disable SSL peer certificate verification
Definition n_network.c:1606
ssize_t send_php(SOCKET s, int _code, char *buf, int n)
send data onto the socket
Definition n_network.c:3653
int netw_stop_thr_engine(NETWORK *netw)
Stop a NETWORK connection sending and receing thread.
Definition n_network.c:3346
void n_ws_close(N_WS_CONN *conn)
Send close frame and close the connection.
Definition n_network.c:5019
char * netw_urlencode(const char *str, size_t len)
function to perform URL encoding
Definition n_network.c:4192
N_STR * n_url_encode(const char *str)
percent-encode a string for use in URLs (returns N_STR)
Definition n_network.c:5654
void * netw_send_func(void *NET)
Thread send function.
Definition n_network.c:3091
NETWORK * netw_accept_nonblock_from(NETWORK *from, int blocking)
make a normal blocking 'accept' .
Definition n_network.c:2904
int netw_get_url_from_http_request(const char *request, char *url, size_t size)
Helper function to extract the URL from the HTTP request line.
Definition n_network.c:4338
N_PROXY_CFG * n_proxy_cfg_parse(const char *url)
Parse a proxy URL string into an N_PROXY_CFG struct.
Definition n_network.c:5745
int netw_set_crypto(NETWORK *netw, char *key, char *certificate)
activate SSL encryption on selected network, using key and certificate
Definition n_network.c:1321
int netw_set_crypto_chain_pem(NETWORK *netw, const char *key_pem, const char *cert_pem, const char *ca_pem)
activate SSL encryption using PEM strings for key, certificate, and CA
Definition n_network.c:1508
int netw_ssl_set_ca(NETWORK *netw, const char *ca_file, const char *ca_path)
set custom CA verify location for SSL context
Definition n_network.c:1581
void n_sse_stop(N_SSE_CONN *conn)
Signal the SSE connection to stop reading.
Definition n_network.c:5085
#define NETWORK_IPV6
Flag to force IPV6
Definition n_network.h:51
#define NETWORK_UDP
Flag for UDP transport.
Definition n_network.h:55
int netw_get_http_date(char *buffer, size_t buffer_size)
helper function to generate the current date in HTTP format
Definition n_network.c:4546
NETWORK_POOL * netw_new_pool(size_t nb_min_element)
return a new network pool of nb_min_element
Definition n_network.c:3861
int netw_set_user_id(NETWORK *netw, int id)
associate an id and a network
Definition n_network.c:4036
void n_mock_server_free(N_MOCK_SERVER **server)
Free a mock server and close the listening socket.
Definition n_network.c:5570
ssize_t recv_data(void *netw, char *buf, uint32_t n)
recv data from the socket
Definition n_network.c:3447
int netw_init_openssl(void)
Do not directly use, internal api.
Definition n_network.c:1280
void n_netw_clear_errors(NETWORK *netw)
Clear captured errors on a NETWORK handle.
Definition n_network.c:105
ssize_t recv_ssl_data(void *netw, char *buf, uint32_t n)
recv data from the socket
Definition n_network.c:3582
int netw_make_listening(NETWORK **netw, char *addr, char *port, int nbpending, int ip_version)
Make a NETWORK be a Listening network.
Definition n_network.c:2240
int netw_ssl_do_handshake(NETWORK *netw, const char *sni_hostname)
Complete the SSL handshake on an already-connected NETWORK.
Definition n_network.c:1888
#define HEAD_SIZE
Size of a HEAD message.
Definition n_network.h:65
ssize_t send_udp_data(void *netw, char *buf, uint32_t n)
send data via UDP on a connected socket
Definition n_network.c:2573
#define netw_atomic_write_state(netw, val)
Lock-free atomic write of the network state field.
Definition n_network.h:368
const char * n_netw_get_error(NETWORK *netw, int index)
Get captured error message by index (0 = oldest).
Definition n_network.c:99
int netw_start_thr_engine(NETWORK *netw)
Start the NETWORK netw Threaded Engine.
Definition n_network.c:3050
int netw_destroy_pool(NETWORK_POOL **netw_pool)
free a NETWORK_POOL *pool
Definition n_network.c:3880
void n_url_free(N_URL **u)
free a N_URL and all its members
Definition n_network.c:5690
#define NETWORK_IPV4
Flag to force IPV4
Definition n_network.h:49
int netw_build_http_response(N_STR **http_response, int status_code, const char *server_name, const char *content_type, char *additional_headers, N_STR *body)
function to dynamically generate an HTTP response
Definition n_network.c:4578
void * netw_recv_func(void *NET)
To Thread Receiving function.
Definition n_network.c:3219
void n_sse_conn_free(N_SSE_CONN **conn)
Free an SSE connection structure.
Definition n_network.c:5094
int n_proxy_connect_tunnel_ssl(const N_PROXY_CFG *proxy, const char *target_host, int target_port)
Open a TCP connection through an HTTPS proxy using CONNECT tunneling.
Definition n_network.c:5924
N_STR * n_url_decode(const char *str)
decode a percent-encoded string (returns N_STR)
Definition n_network.c:5669
N_WS_CONN * n_ws_connect(const char *host, const char *port, const char *path, int use_ssl)
Connect to a WebSocket server (ws:// or wss://).
Definition n_network.c:4676
#define SOCKET_SIZE_FORMAT
socket associated printf style
Definition n_network.h:90
__netw_code_type size_t htonst(size_t value)
host to network size_t
Definition n_network.c:134
int netw_unload_openssl(void)
Do not directly use, internal api.
Definition n_network.c:1302
#define HEAD_CODE
Code of a HEAD message.
Definition n_network.h:67
int n_proxy_connect_socks5(const N_PROXY_CFG *proxy, const char *target_host, int target_port)
Open a TCP connection through a SOCKS5 proxy.
Definition n_network.c:6050
size_t ntohst(size_t value)
network to host size_t
Definition n_network.c:151
ssize_t netw_udp_sendto(NETWORK *netw, char *buf, uint32_t n, struct sockaddr *dest_addr, socklen_t dest_len)
send data via UDP to a specific destination address
Definition n_network.c:2650
#define NETWORK_CONSECUTIVE_SEND_WAIT
Flag to set consecutive send waiting timeout
Definition n_network.h:61
size_t netw_pool_nbclients(NETWORK_POOL *netw_pool)
return the number of networks in netw_pool
Definition n_network.c:4019
int n_proxy_connect_tunnel(const N_PROXY_CFG *proxy, const char *target_host, int target_port)
Open a TCP connection through an HTTP proxy using CONNECT tunneling.
Definition n_network.c:5851
NETWORK * netw_accept_from_ex(NETWORK *from, size_t send_list_limit, size_t recv_list_limit, int blocking, int *retval)
make a normal 'accept' .
Definition n_network.c:2713
void n_netw_clear_connect_errors(void)
Clear pre-connection errors on this thread.
Definition n_network.c:121
void n_mock_server_stop(N_MOCK_SERVER *server)
Signal the mock server to stop accepting connections.
Definition n_network.c:5561
int netw_connect_ex(NETWORK **netw, char *host, char *port, size_t send_list_limit, size_t recv_list_limit, int ip_version, char *ssl_key_file, char *ssl_cert_file)
Use this to connect a NETWORK to any listening one.
Definition n_network.c:1670
#define NETWORK_IPALL
Flag for auto detection by OS of ip version to use.
Definition n_network.h:47
int SOCKET
default socket declaration
Definition n_network.h:88
#define NETW_SOCKET_DISCONNECTED
Code for a disconnected recv.
Definition n_network.h:71
int n_netw_get_error_count(NETWORK *netw)
Get number of captured errors on a NETWORK handle.
Definition n_network.c:95
int netw_pool_broadcast(NETWORK_POOL *netw_pool, const NETWORK *from, N_STR *net_msg)
add net_msg to all network in netork pool
Definition n_network.c:3995
void n_sse_event_clean(N_SSE_EVENT *event)
Free the contents of an SSE event (does not free the struct itself).
Definition n_network.c:5073
#define NETWORK_WAIT_CLOSE_TIMEOUT
Flag to set network closing wait timeout.
Definition n_network.h:63
int netw_setsockopt(NETWORK *netw, int optname, int value)
Modify common socket options on the given netw.
Definition n_network.c:929
int netw_set(NETWORK *netw, int flag)
Restart or reset the specified network ability.
Definition n_network.c:1946
ssize_t send_data(void *netw, char *buf, uint32_t n)
send data onto the socket
Definition n_network.c:3386
ssize_t recv_udp_data(void *netw, char *buf, uint32_t n)
recv data via UDP from a connected socket
Definition n_network.c:2614
int netw_get_state(NETWORK *netw, uint32_t *state, int *thr_engine_status)
Get the state of a network.
Definition n_network.c:1924
N_URL * n_url_parse(const char *url)
parse a URL string into components
Definition n_network.c:5601
void netw_pool_netw_close(void *netw_ptr)
close a network from a network pool
Definition n_network.c:3899
int n_netw_get_connect_error_count(void)
Get number of pre-connection errors captured on this thread.
Definition n_network.c:111
size_t netw_calculate_urlencoded_size(const char *str, size_t len)
function to calculate the required size for the URL-encoded string
Definition n_network.c:4169
int deplete_send_buffer(int fd, int timeout)
wait until the socket is empty or timeout, checking each 100 msec.
Definition n_network.c:2010
NETWORK * netw_accept_from(NETWORK *from)
make a normal blocking 'accept' .
Definition n_network.c:2894
#define NETW_MAX_RETRIES
Send or recv max number of retries.
Definition n_network.h:73
void n_proxy_cfg_free(N_PROXY_CFG **cfg)
Free an N_PROXY_CFG created by n_proxy_cfg_parse().
Definition n_network.c:5840
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
Definition n_network.c:2041
N_STR * n_url_build(const N_URL *u)
build a URL string from parsed components
Definition n_network.c:5640
void n_mock_server_run(N_MOCK_SERVER *server)
Run the mock server accept loop.
Definition n_network.c:5476
int netw_send_quit(NETWORK *netw)
Add a formatted NETMSG_QUIT message to the specified network.
Definition n_network.c:4152
int netw_ssl_connect_client(NETWORK **netw, char *host, char *port, int ip_version)
Connect as an SSL client without providing a client certificate.
Definition n_network.c:1849
const char * netw_get_http_status_message(int status_code)
helper function to convert status code to a human-readable message
Definition n_network.c:4522
int netw_set_crypto_chain(NETWORK *netw, char *key, char *certificate, char *ca_file)
activate SSL encryption using key/certificate files and a CA file for chain verification
Definition n_network.c:1473
int n_ws_recv(N_WS_CONN *conn, N_WS_MESSAGE *msg_out)
Receive one WebSocket frame.
Definition n_network.c:4947
int netw_set_blocking(NETWORK *netw, unsigned long int is_blocking)
Modify blocking socket mode.
Definition n_network.c:871
N_STR * netw_wait_msg(NETWORK *netw, unsigned int refresh, size_t timeout)
Wait a message from aimed NETWORK.
Definition n_network.c:2998
int netw_send_string_to(NETWORK *netw, int id_to, N_STR *name, N_STR *chan, N_STR *txt, int color)
Add a string to the network, aiming a specific user.
Definition n_network.c:4116
int netw_send_ping(NETWORK *netw, int type, int id_from, int id_to, int time)
Add a ping reply to the network.
Definition n_network.c:4051
int netw_ssl_connect(NETWORK **netw, char *host, char *port, int ip_version, char *ssl_key_file, char *ssl_cert_file)
Use this to connect a NETWORK to any listening one, unrestricted send/recv lists.
Definition n_network.c:1830
NETWORK_HTTP_INFO netw_extract_http_info(char *request)
extract a lot of informations, mostly as pointers, and populate a NETWORK_HTTP_INFO structure
Definition n_network.c:4258
#define netw_atomic_read_state(netw)
Lock-free atomic read of the network state field.
Definition n_network.h:363
int netw_connect(NETWORK **netw, char *host, char *port, int ip_version)
Use this to connect a NETWORK to any listening one, unrestricted send/recv lists.
Definition n_network.c:1814
N_MOCK_SERVER * n_mock_server_start(int port, void(*on_request)(N_HTTP_REQUEST *, N_HTTP_RESPONSE *, void *), void *user_data)
Start a mock HTTP server: set up listener and return immediately.
Definition n_network.c:5442
__netw_code_type
Network codes declaration.
Definition n_network.h:232
#define NETWORK_DEPLETE_SOCKET_TIMEOUT
Flag to set send buffer depletion timeout
Definition n_network.h:57
int netw_send_ident(NETWORK *netw, int type, int id, N_STR *name, N_STR *passwd)
Add a formatted NETWMSG_IDENT message to the specified network.
Definition n_network.c:4070
char * netw_urldecode(const char *str)
Function to decode URL-encoded data.
Definition n_network.c:4375
int netw_ssl_set_client_cert(NETWORK *netw, const char *cert_file, const char *key_file)
load a client certificate and private key for mTLS
Definition n_network.c:1626
void n_ws_conn_free(N_WS_CONN **conn)
Free a WebSocket connection structure.
Definition n_network.c:5049
N_SSE_CONN * n_sse_connect(const char *host, const char *port, const char *path, int use_ssl, void(*on_event)(N_SSE_EVENT *, N_SSE_CONN *, void *), void *user_data)
Connect to an SSE endpoint and start reading events.
Definition n_network.c:5179
#define NETWORK_DEPLETE_QUEUES_TIMEOUT
Flag to set network queues depletion timeout
Definition n_network.h:59
int netw_pool_add(NETWORK_POOL *netw_pool, NETWORK *netw)
add a NETWORK *netw to a NETWORK_POOL *pool
Definition n_network.c:3912
int netw_connect_udp(NETWORK **netw, char *host, char *port, int ip_version)
Connect a UDP socket to a remote host.
Definition n_network.c:2468
int netw_info_destroy(NETWORK_HTTP_INFO http_request)
destroy a NETWORK_HTTP_INFO loaded informations
Definition n_network.c:4325
HASH_TABLE * netw_parse_post_data(const char *post_data)
Function to parse POST data.
Definition n_network.c:4412
const char * netw_guess_http_content_type(const char *url)
function to guess the content type based on URL extension
Definition n_network.c:4458
int n_ws_send(N_WS_CONN *conn, const char *payload, size_t len, int opcode)
Send a WebSocket frame (client always masks).
Definition n_network.c:4880
ssize_t recv_php(SOCKET s, int *_code, char **buf)
recv data from the socket
Definition n_network.c:3736
int netw_send_position(NETWORK *netw, int id, double X, double Y, double vx, double vy, double acc_x, double acc_y, int time_stamp)
Add a formatted NETWMSG_IDENT message to the specified network.
Definition n_network.c:4094
int netw_pool_remove(NETWORK_POOL *netw_pool, NETWORK *netw)
remove a NETWORK *netw to a NETWORK_POOL *pool
Definition n_network.c:3958
int netw_add_msg_ex(NETWORK *netw, char *str, unsigned int length)
Add a message to send in aimed NETWORK.
Definition n_network.c:2946
#define NETWORK_TCP
Flag for TCP transport (default)
Definition n_network.h:53
ssize_t netw_udp_recvfrom(NETWORK *netw, char *buf, uint32_t n, struct sockaddr *src_addr, socklen_t *src_len)
recv data via UDP and capture the source address
Definition n_network.c:2682
#define N_WS_OP_CLOSE
Definition n_network.h:676
@ NETW_DESTROY_RECVBUF
Definition n_network.h:232
@ NETW_ENCRYPT_OPENSSL
Definition n_network.h:232
@ NETW_THR_ENGINE_STARTED
Definition n_network.h:232
@ NETW_SERVER
Definition n_network.h:232
@ NETW_EMPTY_SENDBUF
Definition n_network.h:232
@ NETW_DESTROY_SENDBUF
Definition n_network.h:232
@ NETW_THR_ENGINE_STOPPED
Definition n_network.h:232
@ NETW_RUN
Definition n_network.h:232
@ NETW_CLIENT
Definition n_network.h:232
@ NETW_EMPTY_RECVBUF
Definition n_network.h:232
@ NETW_EXITED
Definition n_network.h:232
@ NETW_ERROR
Definition n_network.h:232
@ NETW_EXIT_ASKED
Definition n_network.h:232
@ NETW_ENCRYPT_NONE
Definition n_network.h:232
parsed HTTP request for mock server callback
Definition n_network.h:745
HTTP response to send from mock server callback.
Definition n_network.h:754
mock HTTP server handle
Definition n_network.h:761
Parsed proxy URL components.
Definition n_network.h:554
SSE connection handle.
Definition n_network.h:723
SSE event received from server.
Definition n_network.h:713
parsed URL components
Definition n_network.h:652
WebSocket connection.
Definition n_network.h:688
WebSocket message.
Definition n_network.h:681
Structure of a NETWORK.
Definition n_network.h:258
structure for splitting HTTP requests
Definition n_network.h:381
structure of a network pool
Definition n_network.h:371
Base64 encoding and decoding functions using N_STR.
Hash functions and table.
Generic log system.
#define NETW_CALL_RETRY(__retvar, __expression, __max_tries)
network-aware retry macro: retries on EINTR and EAGAIN/EWOULDBLOCK
Definition n_network.c:659
static __thread int s_connect_err_next
Definition n_network.c:70
static ssize_t _ws_read(N_WS_CONN *conn, void *buf, size_t len)
read exactly len bytes from a WebSocket connection
Definition n_network.c:4645
__attribute__((unused))
Definition n_network.c:1233
static void netw_init_locks(void)
Definition n_network.c:1250
void netw_ssl_print_errors(SOCKET socket)
print OpenSSL errors for a given socket
Definition n_network.c:1187
static ssize_t _sse_write(NETWORK *netw, const void *buf, size_t len)
write bytes to an SSE connection (SSL or plain)
Definition n_network.c:5155
#define _Thread_local
thread-local pre-connection error buffer (DNS, socket creation)
Definition n_network.c:66
static void _n_parse_query_params(N_URL *u, const char *qs)
Definition n_network.c:5579
static void _n_mock_parse_request(const char *buf, N_HTTP_REQUEST *req)
Parse a raw HTTP request buffer into an N_HTTP_REQUEST.
Definition n_network.c:5352
static ssize_t _ws_write(N_WS_CONN *conn, const void *buf, size_t len)
write bytes to a WebSocket connection (SSL or plain)
Definition n_network.c:4629
#define NETW_BUFLEN_CAST(x)
cast for send/recv buffer length: int on Windows, size_t on POSIX
Definition n_network.c:656
NETWORK * netw_new(size_t send_list_limit, size_t recv_list_limit)
Return an empty allocated network ready to be netw_closed.
Definition n_network.c:703
static ssize_t _sse_read_byte(NETWORK *netw, char *ch, volatile int *stop_flag)
read one byte from an SSE connection (SSL or plain).
Definition n_network.c:5112
N_ENUM_netw_code_type
network error code
Definition n_network.c:127
#define netstrerror(code)
BSD style errno string NO WORKING ON REDHAT.
Definition n_network.c:685
static int OPENSSL_IS_INITIALIZED
Definition n_network.c:1274
static __thread int s_connect_err_count
Definition n_network.c:69
#define neterrno
get last socket error code, linux version
Definition n_network.c:670
static pthread_mutex_t * netw_ssl_lockarray
Definition n_network.c:1231
static void _netw_capture_connect_error(const char *fmt,...)
capture a pre-connection error (thread-local)
Definition n_network.c:84
char * get_in_addr(struct sockaddr *sa)
get sockaddr, IPv4 or IPv6
Definition n_network.c:811
static void _n_mock_request_clean(N_HTTP_REQUEST *req)
Free the contents of an N_HTTP_REQUEST (does not free the struct itself).
Definition n_network.c:5429
char * netw_get_openssl_error_string()
get the OpenSSL error string
Definition n_network.c:1160
static __thread char s_connect_errors[8][512]
Definition n_network.c:68
static void _netw_capture_error(NETWORK *netw, const char *fmt,...)
capture an error into a NETWORK handle's ring buffer
Definition n_network.c:73
static void netw_kill_locks(void)
Definition n_network.c:1264
static int _proxy_tcp_connect(const char *host, int port)
Helper: connect a plain TCP socket to host:port.
Definition n_network.c:5711
Network Engine.
Network messages , serialization tools.