44static char* _n_strndup(
const char* s,
size_t n) {
45 size_t len = strlen(s);
47 char* p = (
char*)malloc(len + 1);
54#define strndup _n_strndup
58#include <openssl/sha.h>
59#include <openssl/rand.h>
66#define _Thread_local __thread
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));
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));
170char* wchar_to_char(
const wchar_t* pwchar) {
172 int currentCharIndex = 0;
173 char currentChar = (char)pwchar[currentCharIndex];
174 char* filePathC = NULL;
176 while (currentChar !=
'\0') {
178 currentChar = (char)pwchar[currentCharIndex];
181 const int charCount = currentCharIndex + 1;
184 Malloc(filePathC,
char, (
size_t)charCount);
187 for (
int i = 0; i < charCount; i++) {
189 char character = (char)pwchar[i];
191 *filePathC = character;
193 filePathC +=
sizeof(char);
197 filePathC -= (
sizeof(char) * (
size_t)charCount);
203#define NETW_BUFLEN_CAST(x) ((int)(x))
206#define NETW_CALL_RETRY(__retvar, __expression, __max_tries) \
208 int __nb_retries = 0; \
210 __retvar = (__expression); \
212 } while (__retvar == -1 && (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK) && __nb_retries < (__max_tries)); \
213 if (__retvar == -1 && __nb_retries >= (__max_tries)) __retvar = -2; \
217#define neterrno WSAGetLastError()
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); \
231#if __GNUC__ <= 6 && __GNUC_MINOR__ <= 3
267size_t strlcpy(
char* dst,
const char* src,
size_t siz) {
275 if ((*d++ = *s++) ==
'\0')
287 return (s - src - 1);
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);
323char* inet_ntop(
int af,
const void* src,
char* dst, socklen_t size) {
326 return (inet_ntop4((
const unsigned char*)src, dst, size));
328 return (inet_ntop6((
const unsigned char*)src, dst, size));
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"];
351 l = snprintf(tmp,
sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
352 if (l <= 0 || (socklen_t)l >= size) {
355 strlcpy(dst, tmp, size);
365static char* inet_ntop6(
const unsigned char* src,
char* dst, socklen_t size) {
373 char tmp[
sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
378#define NS_IN6ADDRSZ 16
380 u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
388 memset(words,
'\0',
sizeof words);
389 for (i = 0; i < NS_IN6ADDRSZ; i++)
390 words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
395 for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
398 cur.base = i, cur.len = 1;
402 if (cur.base != -1) {
403 if (best.base == -1 || cur.len > best.len)
409 if (cur.base != -1) {
410 if (best.base == -1 || cur.len > best.len)
413 if (best.base != -1 && best.len < 2)
420 for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
422 if (best.base != -1 && i >= best.base &&
423 i < (best.base + best.len)) {
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)))
438 tp += sprintf(tp,
"%x", words[i]);
441 if (best.base != -1 && (best.base + best.len) ==
442 (NS_IN6ADDRSZ / NS_INT16SZ))
449 if ((socklen_t)(tp - tmp) > size) {
478static int inet_pton4(
const char* src, u_char* dst);
479static int inet_pton6(
const char* src, u_char* dst);
488int inet_pton(
int af,
const char* src,
void* dst) {
491 return (inet_pton4(src, (
unsigned char*)dst));
493 return (inet_pton6(src, (
unsigned char*)dst));
510static int inet_pton4(
const char* src, u_char* dst) {
511 static const char digits[] =
"0123456789";
512 int saw_digit, octets, ch;
514 u_char tmp[NS_INADDRSZ], *tp;
519 while ((ch = *src++) !=
'\0') {
522 if ((pch = strchr(digits, ch)) != NULL) {
523 u_int uiNew = *tp * 10 + (pch - digits);
525 if (saw_digit && *tp == 0)
535 }
else if (ch ==
'.' && saw_digit) {
545 memcpy(dst, tmp, NS_INADDRSZ);
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
567 u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
569 int ch, seen_xdigits;
572 memset((tp = tmp),
'\0', NS_IN6ADDRSZ);
573 endp = tp + NS_IN6ADDRSZ;
582 while ((ch = *src++) !=
'\0') {
583 const char* xdigits :
const char* pch;
585 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
586 pch = strchr((xdigits = xdigits_u), ch);
589 val |= (pch - xdigits);
590 if (++seen_xdigits > 4)
601 }
else if (*src ==
'\0') {
604 if (tp + NS_INT16SZ > endp)
606 *tp++ = (u_char)(val >> 8) & 0xff;
607 *tp++ = (u_char)val & 0xff;
612 if (ch ==
'.' && ((tp + NS_INADDRSZ) <= endp) &&
613 inet_pton4(curtok, tp) > 0) {
621 if (tp + NS_INT16SZ > endp)
623 *tp++ = (u_char)(val >> 8) & 0xff;
624 *tp++ = (u_char)val & 0xff;
626 if (colonp != NULL) {
631 const int n = tp - colonp;
636 for (i = 1; i <= n; i++) {
637 endp[-i] = colonp[n - i];
644 memcpy(dst, tmp, NS_IN6ADDRSZ);
652#include <sys/types.h>
656#define NETW_BUFLEN_CAST(x) ((size_t)(x))
659#define NETW_CALL_RETRY(__retvar, __expression, __max_tries) \
661 int __nb_retries = 0; \
663 __retvar = (__expression); \
665 } while (__retvar == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) && __nb_retries < (__max_tries)); \
666 if (__retvar == -1 && __nb_retries >= (__max_tries)) __retvar = -2; \
670#define neterrno errno
685#define netstrerror(code) ({ \
686 char* __errmsg = NULL; \
688 __errmsg = strdup(strerror(code)); \
689 if (errno == ENOMEM) { \
722 memset(&
netw->
link.
raddr, 0,
sizeof(
struct sockaddr_storage));
757 n_log(
LOG_ERR,
"Error when creating receive list with %d item limit", recv_list_limit);
763 n_log(
LOG_ERR,
"Error when creating send list with %d item limit", send_list_limit);
812 return sa->sa_family == AF_INET
813 ? (
char*)&(((
struct sockaddr_in*)sa)->sin_addr)
814 : (
char*)&(((
struct sockaddr_in6*)sa)->sin6_addr);
825 int compiler_warning_suppressor = 0;
826#if !defined(__linux__) && !defined(__sun) && !defined(_AIX)
827 static WSADATA WSAdata;
828 static int WSA_IS_INITIALIZED = 0;
834 return WSA_IS_INITIALIZED;
838 if (WSA_IS_INITIALIZED == 1)
840 if ((WSAStartup(MAKEWORD(v1, v2), &WSAdata)) != 0) {
841 WSA_IS_INITIALIZED = 0;
844 WSA_IS_INITIALIZED = 1;
850 if (WSA_IS_INITIALIZED == 0)
852 if (WSACleanup() == 0) {
853 WSA_IS_INITIALIZED = 0;
859 compiler_warning_suppressor =
mode + v1 + v2;
860 (void)compiler_warning_suppressor;
861 compiler_warning_suppressor = TRUE;
862 return compiler_warning_suppressor;
878#if defined(__linux__) || defined(__sun)
882 if (flags & O_NONBLOCK) {
889 if (!(flags & O_NONBLOCK)) {
896 if (fcntl(
netw->
link.
sock, F_SETFL, is_blocking ? flags & ~O_NONBLOCK : flags | O_NONBLOCK) == -1) {
905 unsigned long int blocking = 1 - is_blocking;
906 int res = ioctlsocket(
netw->
link.
sock, (
long)FIONBIO, &blocking);
908 if (res != NO_ERROR) {
911 n_log(
LOG_ERR,
"ioctlsocket failed with error: %ld , neterrno: %s", res,
_str(errmsg));
939 if (setsockopt(
netw->
link.
sock, IPPROTO_TCP, TCP_NODELAY, (
const char*)&value,
sizeof(value)) == -1) {
953 if (setsockopt(
netw->
link.
sock, SOL_SOCKET, SO_SNDBUF, (
const char*)&value,
sizeof(value)) == -1) {
967 if (setsockopt(
netw->
link.
sock, SOL_SOCKET, SO_RCVBUF, (
const char*)&value,
sizeof(value)) == -1) {
980 if (setsockopt(
netw->
link.
sock, SOL_SOCKET, SO_REUSEADDR, (
char*)&value,
sizeof(value)) == -1) {
1007 }
else if (value == 0) {
1013 ling.l_linger = (u_short)value;
1015 ling.l_linger = value;
1019 if (setsockopt(
netw->
link.
sock, SOL_SOCKET, SO_LINGER, &ling,
sizeof(ling)) == -1) {
1028 if (setsockopt(
netw->
link.
sock, SOL_SOCKET, SO_LINGER, (
const char*)&ling,
sizeof(ling)) == -1) {
1046 if (setsockopt(
netw->
link.
sock, SOL_SOCKET, SO_RCVTIMEO, (
const char*)&tv,
sizeof tv) == -1) {
1056 if (setsockopt(
netw->
link.
sock, SOL_SOCKET, SO_RCVTIMEO, (
const char*)&value,
sizeof value) == -1) {
1076 if (setsockopt(
netw->
link.
sock, SOL_SOCKET, SO_SNDTIMEO, (
const char*)&tv,
sizeof tv) == -1) {
1086 if (setsockopt(
netw->
link.
sock, SOL_SOCKET, SO_SNDTIMEO, (
const char*)&value,
sizeof value) == -1) {
1111 case TCP_USER_TIMEOUT:
1113 if (setsockopt(
netw->
link.
sock, IPPROTO_TCP, TCP_USER_TIMEOUT, (
const char*)&value,
sizeof value) == -1) {
1124 if (setsockopt(
netw->
link.
sock, IPPROTO_TCP, TCP_QUICKACK, &value,
sizeof(value)) < 0) {
1135 if (setsockopt(
netw->
link.
sock, SOL_SOCKET, SO_KEEPALIVE, (
const char*)&value,
sizeof value) == -1) {
1148 n_log(
LOG_ERR,
"%d is not a supported setsockopt", optname);
1161 BIO* bio = BIO_new(BIO_s_mem());
1166 ERR_print_errors(bio);
1169 size_t len = (size_t)BIO_get_mem_data(bio, &buf);
1172 char* error_str = malloc(len + 1);
1174 memcpy(error_str, buf, len);
1175 error_str[len] =
'\0';
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));
1233__attribute__((unused))
static void netw_ssl_lock_callback(
int mode,
int type,
char* file,
int line) {
1236 if (
mode & CRYPTO_LOCK) {
1243__attribute__((unused))
static unsigned long thread_id(
void) {
1246 ret = (
unsigned long)pthread_self();
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)));
1256 for (i = 0; i < CRYPTO_num_locks(); i++) {
1260 CRYPTO_set_id_callback((
unsigned long (*)())thread_id);
1261 CRYPTO_set_locking_callback((
void (*)())netw_ssl_lock_callback);
1267 CRYPTO_set_locking_callback(NULL);
1268 for (i = 0; i < CRYPTO_num_locks(); i++)
1285 SSL_load_error_strings();
1287#if OPENSSL_VERSION_NUMBER < 0x10100000L
1288 ERR_load_BIO_strings();
1290 OpenSSL_add_all_algorithms();
1324 char* tmp_key = NULL;
1325 char* tmp_cert = NULL;
1326 if (
key && strlen(
key) > 0) {
1327 tmp_key = strdup(
key);
1329 n_log(
LOG_ERR,
"strdup failed for key in netw_set_crypto");
1333 if (certificate && strlen(certificate) > 0) {
1334 tmp_cert = strdup(certificate);
1336 n_log(
LOG_ERR,
"strdup failed for certificate in netw_set_crypto");
1349 if (
key && certificate) {
1351#if OPENSSL_VERSION_NUMBER >= 0x10100000L
1365 if (SSL_CTX_load_verify_locations(
netw->
ctx, NULL,
"/etc/ssl/certs/") != 1) {
1370 if (SSL_CTX_use_certificate_file(
netw->
ctx, certificate, SSL_FILETYPE_PEM) <= 0) {
1374 if (SSL_CTX_use_PrivateKey_file(
netw->
ctx,
key, SSL_FILETYPE_PEM) <= 0) {
1401#if OPENSSL_VERSION_NUMBER >= 0x10100000L
1413 BIO* cert_bio = BIO_new_mem_buf(cert_pem, -1);
1415 n_log(
LOG_ERR,
"Failed to create BIO for certificate PEM");
1418 X509*
cert = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL);
1425 if (SSL_CTX_use_certificate(
netw->
ctx,
cert) <= 0) {
1433 BIO* key_bio = BIO_new_mem_buf(key_pem, -1);
1438 EVP_PKEY* pkey = PEM_read_bio_PrivateKey(key_bio, NULL, NULL, NULL);
1445 if (SSL_CTX_use_PrivateKey(
netw->
ctx, pkey) <= 0) {
1446 EVP_PKEY_free(pkey);
1450 EVP_PKEY_free(pkey);
1453 if (!SSL_CTX_check_private_key(
netw->
ctx)) {
1454 n_log(
LOG_ERR,
"Private key does not match the certificate");
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);
1491 if (SSL_CTX_load_verify_locations(
netw->
ctx,
ca_file, NULL) != 1) {
1519 BIO* ca_bio = BIO_new_mem_buf(ca_pem, -1);
1525 X509_STORE* store = SSL_CTX_get_cert_store(
netw->
ctx);
1528 n_log(
LOG_ERR,
"Failed to get certificate store from SSL context");
1532 X509* ca_cert = NULL;
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");
1546 if (ca_loaded == 0) {
1547 n_log(
LOG_ERR,
"No CA certificates were loaded from PEM string");
1552 BIO* chain_bio = BIO_new_mem_buf(cert_pem, -1);
1555 X509* skip_cert = PEM_read_bio_X509(chain_bio, NULL, NULL, NULL);
1557 X509_free(skip_cert);
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) {
1564 X509_free(chain_cert);
1568 BIO_free(chain_bio);
1586 n_log(
LOG_ERR,
"At least one of ca_file or ca_path must be specified");
1590 if (SSL_CTX_load_verify_locations(
netw->
ctx,
ca_file, ca_path) != 1) {
1611 SSL_CTX_set_verify(
netw->
ctx, SSL_VERIFY_PEER, NULL);
1613 SSL_CTX_set_verify(
netw->
ctx, SSL_VERIFY_NONE, NULL);
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);
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);
1646 if (SSL_CTX_check_private_key(
netw->
ctx) != 1) {
1647 n_log(
LOG_ERR,
"Client certificate and private key do not match");
1673 (void)ssl_cert_file;
1674 int error = 0, net_status = 0;
1675 char* errmsg = NULL;
1679 n_log(
LOG_ERR,
"Unable to allocate (*netw), already existing. You must use empty NETWORK *structs.");
1684 (*netw) =
netw_new(send_list_limit, recv_list_limit);
1696 (*netw)->link.hints.ai_family = AF_INET;
1698 (*netw)->link.hints.ai_family = AF_INET6;
1701 (*netw)->link.hints.ai_family = AF_UNSPEC;
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;
1713 error = getaddrinfo(host,
port, &(*netw)->link.hints, &(*netw)->link.rhost);
1716 n_log(
LOG_ERR,
"Error when resolving %s:%s getaddrinfo: %s", host,
port, gai_strerror(error));
1721 (*netw)->addr_infos_loaded = 1;
1722 Malloc((*netw)->link.ip,
char, 64);
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) {
1737 (*netw)->link.sock = sock;
1739 net_status = connect(sock, rp->ai_addr, (socklen_t)rp->ai_addrlen);
1740 if (net_status == -1) {
1746 (*netw)->link.sock = INVALID_SOCKET;
1750 if (!inet_ntop(rp->ai_family,
get_in_addr(rp->ai_addr), (*netw)->link.ip, 64)) {
1762 n_log(
LOG_ERR,
"Couldn't connect to %s:%s : no address succeeded", host,
port);
1768 (*netw)->link.port = strdup(
port);
1771 if (ssl_key_file && ssl_cert_file) {
1781 (*netw)->ssl = SSL_new((*netw)->ctx);
1782 SSL_set_fd((*netw)->ssl, (
int)(*netw)->link.sock);
1785 if (SSL_connect((*netw)->ssl) <= 0) {
1792 n_log(
LOG_DEBUG,
"SSL-Connected to %s:%s", (*netw)->link.ip, (*netw)->link.port);
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);
1798 n_log(
LOG_DEBUG,
"Connected to %s:%s", (*netw)->link.ip, (*netw)->link.port);
1857#if OPENSSL_VERSION_NUMBER >= 0x10100000L
1858 (*netw)->method = TLS_client_method();
1860 (*netw)->method = TLSv1_2_client_method();
1862 (*netw)->ctx = SSL_CTX_new((*netw)->method);
1863 if (!(*netw)->ctx) {
1869 SSL_CTX_set_default_verify_paths((*netw)->ctx);
1900 SSL_set_tlsext_host_name(
netw->
ssl, sni_hostname);
1902 if (SSL_connect(
netw->
ssl) <= 0) {
1904 unsigned long err = ERR_peek_error();
1907 err ? ERR_reason_error_string(err) :
"unknown error");
1930 if (thr_engine_status)
2011#if defined(__linux__)
2012 int outstanding = 0;
2016 for (
int it = 0; it < timeout; it += 100) {
2018 if (ioctl(fd, SIOCOUTQ, &outstanding) == -1) {
2020 n_log(
LOG_ERR,
"ioctl SIOCOUTQ returned -1: %s for socket %d", strerror(error), fd);
2044 int thr_engine_status = 0;
2047 if ((*netw)->deplete_queues_timeout > 0) {
2049 int max_iterations = (*netw)->deplete_queues_timeout * 10;
2054 pthread_mutex_lock(&(*netw)->eventbolt);
2055 nb_running = (*netw)->nb_running_threads;
2056 pthread_mutex_unlock(&(*netw)->eventbolt);
2059 }
while (nb_running > 0 && it < max_iterations);
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);
2067 if ((*netw)->link.sock != INVALID_SOCKET) {
2068 int remaining =
deplete_send_buffer((
int)(*netw)->link.sock, (*netw)->deplete_socket_timeout);
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);
2088 pthread_mutex_lock(&(*netw)->eventbolt);
2089 nb_running = (*netw)->nb_running_threads;
2090 pthread_mutex_unlock(&(*netw)->eventbolt);
2092 if ((*netw)->link.sock != INVALID_SOCKET) {
2098 if (nb_running == 0) {
2099 int shutdown_res = SSL_shutdown((*netw)->ssl);
2100 if (shutdown_res == 0) {
2104 shutdown_res = SSL_shutdown((*netw)->ssl);
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) {
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) {
2119 shutdown_res = SSL_shutdown((*netw)->ssl);
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);
2126 SSL_free((*netw)->ssl);
2129 n_log(
LOG_ERR,
"SSL handle of socket %d was already NULL", (*netw)->link.sock);
2131 n_log(
LOG_DEBUG,
"listening socket %d has no SSL handle (expected)", (*netw)->link.sock);
2138 shutdown((*netw)->link.sock, SHUT_WR);
2140 if ((*netw)->wait_close_timeout > 0) {
2143 char buffer[4096] =
"";
2144 int max_iters = (*netw)->wait_close_timeout * 10;
2145 for (
int it = 0; it < max_iters; it++) {
2149#pragma GCC diagnostic push
2150#pragma GCC diagnostic ignored "-Wsign-conversion"
2151 FD_SET((*netw)->link.sock, &rfds);
2152#pragma GCC diagnostic pop
2154 tv.tv_usec = 100000;
2155 int sel = select((
int)(*netw)->link.sock + 1, &rfds, NULL, NULL, &tv);
2157 ssize_t res = recv((*netw)->link.sock, buffer, 4096, NETFLAGS);
2162 if (error != ENOTCONN && error != EINTR && error != ECONNRESET
2164 && error != WSAENOTCONN && error != WSAECONNRESET && error != WSAESHUTDOWN && error != WSAEWOULDBLOCK && error != WSAEINTR
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));
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);
2175 }
else if (sel < 0) {
2180 && error != WSAEINTR
2184 n_log(
LOG_ERR,
"select() error on socket %d during close: %s", (*netw)->link.sock,
_str(errmsg));
2194 closesocket((*netw)->link.sock);
2200 SSL_CTX_free((*netw)->ctx);
2202 n_log(
LOG_ERR,
"SSL context of socket %d was already NULL", (*netw)->link.sock);
2214 if ((*netw)->link.rhost) {
2215 freeaddrinfo((*netw)->link.rhost);
2221 pthread_mutex_destroy(&(*netw)->recvbolt);
2222 pthread_mutex_destroy(&(*netw)->sendbolt);
2223 pthread_mutex_destroy(&(*netw)->eventbolt);
2224 sem_destroy(&(*netw)->send_blocker);
2244 char* errmsg = NULL;
2247 n_log(
LOG_ERR,
"Cannot use an allocated network. Please pass a NULL network to modify");
2259 (*netw)->link.port = strdup(
port);
2262 (*netw)->link.hints.ai_family = AF_INET;
2264 (*netw)->link.hints.ai_family = AF_INET6;
2267 (*netw)->link.hints.ai_family = AF_UNSPEC;
2270 (*netw)->link.hints.ai_flags = AI_PASSIVE;
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;
2277 error = getaddrinfo(
addr,
port, &(*netw)->link.hints, &(*netw)->link.rhost);
2284 (*netw)->addr_infos_loaded = 1;
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) {
2302 if (bind((*netw)->link.sock, rp->ai_addr, (socklen_t)rp->ai_addrlen) == 0) {
2310 if (!inet_ntop(rp->ai_family,
get_in_addr(rp->ai_addr), ip, 64)) {
2317 (*netw)->link.ip = ip;
2325 closesocket((*netw)->link.sock);
2336 (*netw)->nb_pending = nbpending;
2337 if (listen((*netw)->link.sock, (*netw)->nb_pending) != 0) {
2364 char* errmsg = NULL;
2367 n_log(
LOG_ERR,
"Cannot use an allocated network. Please pass a NULL network to modify");
2379 (*netw)->link.port = strdup(
port);
2384 (*netw)->link.hints.ai_family = AF_INET;
2386 (*netw)->link.hints.ai_family = AF_INET6;
2388 (*netw)->link.hints.ai_family = AF_UNSPEC;
2391 (*netw)->link.hints.ai_flags = AI_PASSIVE;
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;
2398 error = getaddrinfo(
addr,
port, &(*netw)->link.hints, &(*netw)->link.rhost);
2405 (*netw)->addr_infos_loaded = 1;
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) {
2413 n_log(
LOG_ERR,
"Error while trying to make a UDP socket: %s",
_str(errmsg));
2418 if (bind((*netw)->link.sock, rp->ai_addr, (socklen_t)rp->ai_addrlen) == 0) {
2426 if (!inet_ntop(rp->ai_family,
get_in_addr(rp->ai_addr), ip, 64)) {
2432 (*netw)->link.ip = ip;
2438 n_log(
LOG_ERR,
"Error from bind() on UDP port %s neterrno: %s",
port, errmsg);
2440 closesocket((*netw)->link.sock);
2470 char* errmsg = NULL;
2473 n_log(
LOG_ERR,
"Unable to allocate (*netw), already existing. You must use empty NETWORK *structs.");
2489 (*netw)->link.hints.ai_family = AF_INET;
2491 (*netw)->link.hints.ai_family = AF_INET6;
2493 (*netw)->link.hints.ai_family = AF_UNSPEC;
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;
2502 error = getaddrinfo(host,
port, &(*netw)->link.hints, &(*netw)->link.rhost);
2505 n_log(
LOG_ERR,
"Error when resolving %s:%s getaddrinfo: %s", host,
port, gai_strerror(error));
2509 (*netw)->addr_infos_loaded = 1;
2510 Malloc((*netw)->link.ip,
char, 64);
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) {
2519 n_log(
LOG_ERR,
"Error while trying to make a UDP socket: %s",
_str(errmsg));
2524 (*netw)->link.sock = sock;
2527 if (connect(sock, rp->ai_addr, (socklen_t)rp->ai_addrlen) == -1) {
2533 (*netw)->link.sock = INVALID_SOCKET;
2536 if (!inet_ntop(rp->ai_family,
get_in_addr(rp->ai_addr), (*netw)->link.ip, 64)) {
2547 n_log(
LOG_ERR,
"Couldn't connect UDP to %s:%s : no address succeeded", host,
port);
2552 (*netw)->link.port = strdup(
port);
2561 n_log(
LOG_DEBUG,
"UDP-Connected to %s:%s", (*netw)->link.ip, (*netw)->link.port);
2579 char* errmsg = NULL;
2590 if (error == ECONNRESET || error == ENOTCONN
2592 || error == WSAECONNRESET || error == WSAENOTCONN
2631 n_log(
LOG_ERR,
"UDP socket %d recv returned %zd, error: %s", s, br,
_str(errmsg));
2714 SOCKET tmp = INVALID_SOCKET;
2716 char* errmsg = NULL;
2718#if defined(__linux__) || defined(__sun) || defined(_AIX)
2719 socklen_t sin_size = 0;
2739 int secs = blocking / 1000;
2740 int usecs = (blocking % 1000) * 1000;
2741 struct timeval select_timeout = {secs, usecs};
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
2750 int ret = select((
int)(from->
link.
sock + 1), &accept_set, NULL, NULL, &select_timeout);
2756 n_log(
LOG_DEBUG,
"error on select with timeout %ds (%d.%ds), neterrno: %s", blocking, secs, usecs,
_str(errmsg));
2760 }
else if (ret == 0) {
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
2772 if (tmp == INVALID_SOCKET) {
2789 }
else if (blocking == -1) {
2797 if (tmp == INVALID_SOCKET) {
2798 if (error != EINTR && error != EAGAIN
2800 && error != WSAEWOULDBLOCK
2818 if (tmp == INVALID_SOCKET) {
2871 if (SSL_accept(
netw->
ssl) <= 0) {
2921 n_log(
LOG_ERR,
"Empty messages are not supported. msg(%p)->lenght=%d", msg, msg->
length);
3000 unsigned int secs = 0;
3001 unsigned int usecs = 0;
3006 if (refresh > 999999) {
3007 secs = refresh / 1000000;
3008 usecs = refresh % 1000000;
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);
3023 if (timed >= refresh)
3025 if (timed == 0 || timed < refresh) {
3094 ssize_t net_status = 0;
3109 int message_sent = 0;
3110 while (message_sent == 0 && !
DONE) {
3120 memcpy(nboct, &nboctet,
sizeof(uint32_t));
3131 if (ptr->
written <= UINT_MAX) {
3135 nboctet = htonl(state);
3136 memcpy(nboct, &nboctet,
sizeof(uint32_t));
3145 nboctet = htonl((uint32_t)ptr->
written);
3146 memcpy(nboct, &nboctet,
sizeof(uint32_t));
3168 n_log(
LOG_ERR,
"discarded packet of size %zu which is greater than %" PRIu32, ptr->
written, UINT_MAX);
3180 }
else if (
DONE == 2) {
3183 }
else if (
DONE == 3)
3185 else if (
DONE == 4) {
3188 }
else if (
DONE == 5) {
3209#if !defined(__linux__)
3221 ssize_t net_status = 0;
3229 N_STR* recvdmsg = NULL;
3247 if (net_status < 0) {
3250 memcpy(&nboctet, nboct,
sizeof(uint32_t));
3251 tmpstate = ntohl(nboctet);
3260 if (net_status < 0) {
3263 memcpy(&nboctet, nboct,
sizeof(uint32_t));
3264 tmpstate = ntohl(nboctet);
3273 if (!recvdmsg->
data) {
3277 recvdmsg->
length = nboctet + 1;
3282 if (net_status < 0) {
3303 }
else if (
DONE == 2) {
3306 }
else if (
DONE == 3) {
3309 }
else if (
DONE == 4) {
3312 }
else if (
DONE == 5) {
3315 }
else if (
DONE == 6) {
3336#if !defined(__linux__)
3349 int thr_engine_status = 0;
3357 n_log(
LOG_ERR,
"Thread engine status already stopped for network %p",
netw);
3364 for (
int it = 0; it < 10; it++) {
3393 char* errmsg = NULL;
3401 char* tmp_buf = buf;
3412 }
else if (error == ECONNRESET || error == ENOTCONN || error == EPIPE
3414 || error == WSAECONNRESET || error == WSAENOTCONN || error == WSAESHUTDOWN
3419 }
else if (bs == -1) {
3426 }
else if (bs == -2) {
3431 }
else if (bs == 0) {
3454 char* errmsg = NULL;
3456#if defined(NETWORK_DISABLE_ZERO_LENGTH_RECV) && (NETWORK_DISABLE_ZERO_LENGTH_RECV == TRUE)
3464 char* tmp_buf = buf;
3475 }
else if (br == 0) {
3479 }
else if (error == ECONNRESET || error == ENOTCONN
3481 || error == WSAECONNRESET || error == WSAENOTCONN || error == WSAESHUTDOWN
3487 }
else if (br == -1) {
3494 }
else if (br == -2) {
3523 char* errmsg = NULL;
3534#if OPENSSL_VERSION_NUMBER < 0x1010107fL
3535 int status = SSL_write(ssl, buf, (
int)(n - bcount));
3536 bs = (status > 0) ? (
size_t)status : 0;
3538 int status = SSL_write_ex(ssl, buf, (
size_t)(n - bcount), &bs);
3542 bcount += (ssize_t)bs;
3545 int ssl_error = SSL_get_error(ssl, status);
3546 if (ssl_error == SSL_ERROR_WANT_READ || ssl_error == SSL_ERROR_WANT_WRITE) {
3551 switch (ssl_error) {
3552 case SSL_ERROR_SYSCALL:
3555 n_log(
LOG_ERR,
"socket %d SSL_write syscall error: connection closed by peer", s);
3560 n_log(
LOG_ERR,
"socket %d SSL_write returned %d, error: %s", s, bs, ERR_reason_error_string(ERR_get_error()));
3565 n_log(
LOG_ERR,
"socket %d SSL_write returned %d, errno: %s", s, bs,
_str(errmsg));
3592 char* errmsg = NULL;
3600 while (bcount < n) {
3603#if OPENSSL_VERSION_NUMBER < 0x10101000L
3604 int status = SSL_read(ssl, buf, (
int)(n - bcount));
3605 br = (status > 0) ? (
size_t)status : 0;
3607 int status = SSL_read_ex(ssl, buf, (
size_t)(n - bcount), &br);
3611 bcount += (ssize_t)br;
3614 int ssl_error = SSL_get_error(ssl, status);
3615 if (ssl_error == SSL_ERROR_WANT_READ || ssl_error == SSL_ERROR_WANT_WRITE) {
3620 switch (ssl_error) {
3621 case SSL_ERROR_SYSCALL:
3624 n_log(
LOG_ERR,
"socket %d SSL_read syscall error: connection closed by peer", s);
3629 n_log(
LOG_ERR,
"socket %d SSL_read returned %d, error: %s", s, br, ERR_reason_error_string(ERR_get_error()));
3634 n_log(
LOG_ERR,
"socket %d SSL_read returned %d, errno: %s", s, br,
_str(errmsg));
3658 char* errmsg = NULL;
3682 n_log(
LOG_ERR,
"Socket %d sending Error %d when sending head size, neterrno: %s", s, bs,
_str(errmsg));
3700 n_log(
LOG_ERR,
"Socket %d sending Error %d when sending head code, neterrno: %s", s, bs,
_str(errmsg));
3719 n_log(
LOG_ERR,
"Socket %d sending Error %d when sending message of size %d, neterrno: %s", s, bs, n,
_str(errmsg));
3739 long int tmpnb = 0, size = 0;
3743 char* errmsg = NULL;
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);
3788 n_log(
LOG_ERR,
"Socket %d receive %d Error , neterrno: %s", s, br,
_str(errmsg));
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);
3800 (*_code) = (int)tmpnb;
3807 Malloc((*buf),
char, (
size_t)(size + 1));
3816 while (bcount < size) {
3826 n_log(
LOG_ERR,
"Socket %d receive %d Error neterrno: %s", s, br,
_str(errmsg));
3881 __n_assert(netw_pool && (*netw_pool),
return FALSE);
3884 if ((*netw_pool)->pool)
3886 unlock((*netw_pool)->rwlock);
4052 N_STR* tmpstr = NULL;
4071 N_STR* tmpstr = NULL;
4095 N_STR* tmpstr = NULL;
4117 N_STR* tmpstr = NULL;
4137 N_STR* tmpstr = NULL;
4155 N_STR* tmpstr = NULL;
4172 size_t encoded_size = 0;
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 ==
'~') {
4183 return encoded_size;
4195 static const char* hex =
"0123456789ABCDEF";
4197 char* encoded = (
char*)malloc(encoded_size + 1);
4203 char* pbuf = encoded;
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 ==
'~') {
4211 *pbuf++ = hex[c >> 4];
4212 *pbuf++ = hex[c & 0xF];
4228 const char* space = strchr(request,
' ');
4230 if (space == NULL) {
4236 size_t method_length = (size_t)(space - request);
4239 char* method = (
char*)malloc(method_length + 1);
4241 if (method == NULL) {
4247 strncpy(method, request, method_length);
4248 method[method_length] =
'\0';
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");
4272 size_t length = (size_t)(end - start);
4273 if (length > 255) length = 255;
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: ");
4287 unsigned long tmp_cl = strtoul(start, NULL, 10);
4291#if ULONG_MAX > SIZE_MAX
4292 if (error == ERANGE || tmp_cl > SIZE_MAX) {
4294 if (error == ERANGE) {
4296 n_log(
LOG_ERR,
"could not get content_length for request %p, returned %s", request, strerror(error));
4304 const char* body_start = strstr(request,
"\r\n\r\n");
4339 __n_assert(request && strlen(request) > 0,
return FALSE);
4343 strncpy(url,
"/", size - 1);
4344 url[size - 1] =
'\0';
4347 const char* first_space = strchr(request,
' ');
4353 const char* second_space = strchr(first_space + 1,
' ');
4354 if (!second_space) {
4359 size_t len = (size_t)(second_space - first_space - 1);
4364 strncpy(url, first_space + 1, len);
4377 char* decoded = malloc(strlen(str) + 1);
4383 if (isxdigit((
unsigned char)str[1]) && isxdigit((
unsigned char)str[2])) {
4385 if (sscanf(str + 1,
"%2x", &value) >= 1) {
4389 n_log(
LOG_ERR,
"sscanf could not parse char *str (%p) for a %%2x", str);
4396 }
else if (*str ==
'+') {
4416 char* data = strdup(post_data);
4423 while (pair != NULL) {
4425 char* ampersand_pos = strchr(pair,
'&');
4428 if (ampersand_pos != NULL) {
4429 *ampersand_pos =
'\0';
4433 char* equal_pos = strchr(pair,
'=');
4434 if (equal_pos != NULL) {
4436 const char*
key = pair;
4437 const char* value = equal_pos + 1;
4443 free(decoded_value);
4446 pair = (ampersand_pos != NULL) ? (ampersand_pos + 1) : NULL;
4450 return post_data_table;
4462 char url_copy[1024];
4463 strncpy(url_copy, url,
sizeof(url_copy) - 1);
4464 url_copy[
sizeof(url_copy) - 1] =
'\0';
4467 char* query_start = strchr(url_copy,
'?');
4469 *query_start =
'\0';
4473 const char* ext = strrchr(url_copy,
'.');
4481 if (strcmp(ext,
".html") == 0 || strcmp(ext,
".htm") == 0) {
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) {
4489 }
else if (strcmp(ext,
".gif") == 0) {
4491 }
else if (strcmp(ext,
".css") == 0) {
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) {
4505 }
else if (strcmp(ext,
".mp3") == 0) {
4506 return "audio/mpeg";
4507 }
else if (strcmp(ext,
".wav") == 0) {
4509 }
else if (strcmp(ext,
".ogg") == 0) {
4523 switch (status_code) {
4527 return "No Content";
4529 return "Not Modified";
4533 return "Internal Server Error";
4548 const time_t now = time(NULL);
4551 if (gmtime_s(&gmt, &now) != 0) {
4556 if (!gmtime_r(&now, &gmt)) {
4561 if (strftime(buffer, buffer_size,
"%a, %d %b %Y %H:%M:%S GMT", &gmt) == 0) {
4581 __n_assert(additional_headers,
return FALSE);
4584 const char* connection_type =
"close";
4587 char date_buffer[128] =
"";
4590 if ((*http_response)) {
4591 (*http_response)->written = 0;
4594 if (!body || body->
written == 0) {
4597 "HTTP/1.1 %d %s\r\n"
4600 "Content-Length: 0\r\n"
4602 "Connection: %s\r\n\r\n",
4603 status_code, status_message, date_buffer, server_name, additional_headers, connection_type);
4608 "HTTP/1.1 %d %s\r\n"
4611 "Content-Type: %s\r\n"
4612 "Content-Length: %zu\r\n"
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);
4633 int ret = SSL_write(conn->
netw->
ssl, buf, (
int)len);
4634 return (ret > 0) ? (ssize_t)ret : -1;
4636 ssize_t ret = send(conn->
netw->
link.
sock, buf, len, NETFLAGS);
4649 char* p = (
char*)buf;
4650 while (total < len) {
4653 ret = SSL_read(conn->
netw->
ssl, p + total, (
int)(len - total));
4655 ret = recv(conn->
netw->
link.
sock, p + total, len - total, 0);
4660 total += (size_t)ret;
4662 return (ssize_t)total;
4687 conn->
host = strdup(host);
4688 conn->
path = strdup(path);
4691 goto ws_connect_fail;
4697 n_log(
LOG_ERR,
"n_ws_connect: TCP+SSL context setup failed for %s:%s", host,
port);
4698 goto ws_connect_fail;
4702 n_log(
LOG_ERR,
"n_ws_connect: SSL handshake failed for %s:%s", host,
port);
4703 goto ws_connect_fail;
4708 goto ws_connect_fail;
4713 unsigned char rand_bytes[16];
4714 if (RAND_bytes(rand_bytes, 16) != 1) {
4717 goto ws_connect_fail;
4722 memcpy(rand_nstr->
data, rand_bytes, 16);
4727 __n_assert(ws_key_nstr,
goto ws_connect_fail);
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] ==
' ')) {
4737 char upgrade_req[2048];
4738 int req_len = snprintf(upgrade_req,
sizeof(upgrade_req),
4739 "GET %s HTTP/1.1\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"
4746 path, host, ws_key_nstr->
data);
4748 if (req_len < 0 || (
size_t)req_len >=
sizeof(upgrade_req)) {
4750 n_log(
LOG_ERR,
"n_ws_connect: upgrade request too large");
4752 goto ws_connect_fail;
4756 if (
_ws_write(conn, upgrade_req, (
size_t)req_len) < 0) {
4758 n_log(
LOG_ERR,
"n_ws_connect: failed to send upgrade request");
4760 goto ws_connect_fail;
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) {
4770 r = SSL_read(conn->
netw->
ssl, resp_buf + resp_len, 1);
4772 r = recv(conn->
netw->
link.
sock, resp_buf + resp_len, 1, 0);
4776 n_log(
LOG_ERR,
"n_ws_connect: failed reading handshake response");
4778 goto ws_connect_fail;
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') {
4787 resp_buf[resp_len] =
'\0';
4790 if (strstr(resp_buf,
"101") == NULL) {
4792 n_log(
LOG_ERR,
"n_ws_connect: server did not return 101: %.128s", resp_buf);
4794 goto ws_connect_fail;
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);
4805 unsigned char sha1_hash[SHA_DIGEST_LENGTH];
4806 SHA1((
const unsigned char*)concat_key, strlen(concat_key), sha1_hash);
4810 memcpy(sha1_nstr->
data, sha1_hash, SHA_DIGEST_LENGTH);
4811 sha1_nstr->
written = SHA_DIGEST_LENGTH;
4815 __n_assert(expected_accept,
goto ws_connect_fail);
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';
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;
4836 n_log(
LOG_ERR,
"n_ws_connect: no Sec-WebSocket-Accept header in response");
4838 goto ws_connect_fail;
4841 while (*accept_hdr ==
' ') accept_hdr++;
4843 char accept_val[128];
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];
4850 accept_val[ai] =
'\0';
4852 if (strcmp(accept_val, expected_accept->
data) != 0) {
4855 n_log(
LOG_DEBUG,
"n_ws_connect: Sec-WebSocket-Accept mismatch (proxy?): got [%s] expected [%s]",
4856 accept_val, expected_accept->
data);
4861 n_log(
LOG_INFO,
"n_ws_connect: WebSocket handshake completed with %s:%s%s", host,
port, path);
4885 size_t frame_max = 14 + len;
4886 unsigned char* frame = NULL;
4887 Malloc(frame,
unsigned char, frame_max);
4893 frame[pos++] = (
unsigned char)(0x80 | (opcode & 0x0F));
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);
4903 frame[pos++] = (
unsigned char)(0x80 | 127);
4904 for (
int i = 7; i >= 0; i--) {
4905 frame[pos++] = (
unsigned char)((len >> (8 * i)) & 0xFF);
4910 unsigned char mask_key[4];
4911 if (RAND_bytes(mask_key, 4) != 1) {
4913 n_log(
LOG_ERR,
"n_ws_send: RAND_bytes failed for mask key");
4917 memcpy(frame + pos, mask_key, 4);
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]);
4928 ssize_t written =
_ws_write(conn, frame, pos);
4930 if (written < 0 || (
size_t)written != pos) {
4932 n_log(
LOG_ERR,
"n_ws_send: write failed (wrote %zd of %zu)", written, pos);
4952 memset(msg_out, 0,
sizeof(*msg_out));
4955 unsigned char hdr[2];
4961 int opcode = hdr[0] & 0x0F;
4962 int mask_bit = (hdr[1] >> 7) & 1;
4963 uint64_t payload_len = hdr[1] & 0x7F;
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;
4974 for (
int i = 0; i < 8; i++) {
4975 payload_len = (payload_len << 8) | (uint64_t)ext[i];
4980 unsigned char mask_key[4] = {0};
4982 if (
_ws_read(conn, mask_key, 4) < 0)
return -1;
4989 if (payload_len > 0) {
4990 if (
_ws_read(conn, payload->
data, (
size_t)payload_len) < 0) {
4996 for (uint64_t i = 0; i < payload_len; i++) {
4997 payload->
data[i] = (char)((
unsigned char)payload->
data[i] ^ mask_key[i % 4]);
5001 payload->
written = (size_t)payload_len;
5002 payload->
data[payload_len] =
'\0';
5004 msg_out->
opcode = opcode;
5006 msg_out->
masked = mask_bit;
5029 memset(&msg, 0,
sizeof(msg));
5087 __atomic_store_n(&conn->
stop_flag, 1, __ATOMIC_RELEASE);
5117 if (stop_flag && __atomic_load_n(stop_flag, __ATOMIC_ACQUIRE)) {
5122 tv.tv_usec = 500000;
5130 if (SSL_pending(
netw->
ssl) > 0) {
5135 int sel = select((
int)
netw->
link.
sock + 1, &fds, NULL, NULL, &tv);
5136 if (sel < 0)
return -1;
5137 if (sel == 0)
continue;
5142 ret = SSL_read(
netw->
ssl, ch, 1);
5146 return (ret == 1) ? 1 : -1;
5158 int ret = SSL_write(
netw->
ssl, buf, (
int)len);
5159 return (ret > 0) ? (ssize_t)ret : -1;
5161 ssize_t ret = send(
netw->
link.
sock, buf, len, NETFLAGS);
5197 n_log(
LOG_ERR,
"n_sse_connect: TCP+SSL context setup failed for %s:%s", host,
port);
5198 goto sse_connect_fail;
5202 n_log(
LOG_ERR,
"n_sse_connect: SSL handshake failed for %s:%s", host,
port);
5203 goto sse_connect_fail;
5208 goto sse_connect_fail;
5214 int req_len = snprintf(request,
sizeof(request),
5215 "GET %s HTTP/1.1\r\n"
5217 "Accept: text/event-stream\r\n"
5218 "Cache-Control: no-cache\r\n"
5219 "Connection: keep-alive\r\n"
5223 if (req_len < 0 || (
size_t)req_len >=
sizeof(request)) {
5226 goto sse_connect_fail;
5231 n_log(
LOG_ERR,
"n_sse_connect: failed to send HTTP request");
5232 goto sse_connect_fail;
5236 char resp_buf[8192];
5237 size_t resp_len = 0;
5238 while (resp_len <
sizeof(resp_buf) - 1) {
5242 n_log(
LOG_ERR,
"n_sse_connect: failed reading HTTP response");
5243 goto sse_connect_fail;
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') {
5252 resp_buf[resp_len] =
'\0';
5255 if (strstr(resp_buf,
"200") == NULL) {
5257 n_log(
LOG_ERR,
"n_sse_connect: server did not return 200: %.128s", resp_buf);
5258 goto sse_connect_fail;
5266 memset(¤t, 0,
sizeof(current));
5270 size_t line_len = 0;
5272 while (!__atomic_load_n(&conn->
stop_flag, __ATOMIC_ACQUIRE)) {
5275 n_log(
LOG_DEBUG,
"n_sse_connect: connection closed or read error");
5281 if (line_len > 0 && line[line_len - 1] ==
'\r') {
5284 line[line_len] =
'\0';
5286 if (line_len == 0) {
5291 memset(¤t, 0,
sizeof(current));
5293 }
else if (line[0] ==
':') {
5298 char* colon = strchr(line,
':');
5299 const char* field = line;
5300 const char* value =
"";
5305 if (*value ==
' ') value++;
5308 if (strcmp(field,
"data") == 0) {
5315 }
else if (strcmp(field,
"event") == 0) {
5318 }
else if (strcmp(field,
"id") == 0) {
5321 }
else if (strcmp(field,
"retry") == 0) {
5322 current.
retry = atoi(value);
5328 if (line_len <
sizeof(line) - 1) {
5329 line[line_len++] = ch;
5353 if (!buf || !req)
return;
5356 const char* line_end = strstr(buf,
"\r\n");
5357 if (!line_end) line_end = strchr(buf,
'\n');
5358 if (!line_end)
return;
5360 size_t line_len = (size_t)(line_end - buf);
5362 if (line_len >=
sizeof(line)) line_len =
sizeof(line) - 1;
5363 memcpy(line, buf, line_len);
5364 line[line_len] =
'\0';
5367 char* sp1 = strchr(line,
' ');
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';
5376 char* sp2 = strchr(sp1,
' ');
5377 if (sp2) *sp2 =
'\0';
5378 char* qmark = strchr(sp1,
'?');
5381 strncpy(req->
query, qmark + 1,
sizeof(req->
query) - 1);
5384 strncpy(req->
path, sp1,
sizeof(req->
path) - 1);
5385 req->
path[
sizeof(req->
path) - 1] =
'\0';
5388 const char* hdr_start = line_end;
5389 if (*hdr_start ==
'\r') hdr_start++;
5390 if (*hdr_start ==
'\n') hdr_start++;
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');
5400 size_t hlen = (size_t)(next - hdr_start);
5404 if (*body_start ==
'\r') body_start++;
5405 if (*body_start ==
'\n') body_start++;
5409 char* header_line = strndup(hdr_start, hlen);
5415 if (*hdr_start ==
'\r') hdr_start++;
5416 if (*hdr_start ==
'\n') hdr_start++;
5420 if (body_start && *body_start) {
5453 server->user_data = user_data;
5466 server->listener = listener;
5480 while (!__atomic_load_n(&
server->stop_flag, __ATOMIC_ACQUIRE)) {
5487 tv.tv_usec = 200000;
5488 int sel = select((
int)(
server->listener->
link.
sock + 1), &readfds, NULL, NULL, &tv);
5489 if (sel <= 0)
continue;
5494 if (!client)
continue;
5498 ssize_t n = recv(client->
link.
sock, buf,
sizeof(buf) - 1, 0);
5507 memset(&req, 0,
sizeof(req));
5512 memset(&resp, 0,
sizeof(resp));
5521 if (!status_msg) status_msg =
"Unknown";
5523 size_t body_len = 0;
5524 const char* body_data =
"";
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"
5542 send(client->
link.
sock, header_buf, (
size_t)hlen, NETFLAGS);
5545 send(client->
link.
sock, body_data, body_len, NETFLAGS);
5563 __atomic_store_n(&
server->stop_flag, 1, __ATOMIC_RELEASE);
5572 if ((*server)->listener) {
5580 if (!u || !qs || !qs[0])
return;
5581 char* buf = strdup(qs);
5583 char* saveptr = NULL;
5584 char* tok = strtok_r(buf,
"&", &saveptr);
5586 char* eq = strchr(tok,
'=');
5596 tok = strtok_r(NULL,
"&", &saveptr);
5602 if (!url)
return NULL;
5605 if (!u)
return NULL;
5606 memset(u, 0,
sizeof(*u));
5607 const char* p = url;
5608 const char* scheme_end = strstr(p,
"://");
5610 u->
scheme = strndup(p, (
size_t)(scheme_end - p));
5613 u->
scheme = strdup(
"http");
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));
5622 while (*p && *p !=
'/' && *p !=
'?') p++;
5626 while (*pe && *pe !=
'?') pe++;
5627 u->
path = strndup(p, (
size_t)(pe - p));
5630 u->
path = strdup(
"/");
5634 u->
query = strdup(p);
5641 if (!u)
return NULL;
5643 if (!result)
return NULL;
5646 int dp = (u->
scheme && strcmp(u->
scheme,
"https") == 0) ? 443 : 80;
5655 if (!str)
return NULL;
5656 size_t len = strlen(str);
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 ==
'~')
5670 if (!str)
return NULL;
5671 size_t len = strlen(str);
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);
5681 }
else if (str[i] ==
'+') {
5691 if (!u || !*u)
return;
5697 for (
int i = 0; i < p->
nb_params; i++) {
5715 struct addrinfo hints;
5716 memset(&hints, 0,
sizeof(hints));
5717 hints.ai_family = AF_UNSPEC;
5718 hints.ai_socktype = SOCK_STREAM;
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));
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;
5746 if (!url)
return NULL;
5749 const char* sep = strstr(url,
"://");
5751 n_log(
LOG_ERR,
"n_proxy_cfg_parse: no scheme in '%s'", url);
5755 size_t scheme_len = (size_t)(sep - url);
5756 if (scheme_len == 0 || scheme_len > 16)
return NULL;
5759 memcpy(scheme, url, scheme_len);
5760 scheme[scheme_len] =
'\0';
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);
5768 const char* after = sep + 3;
5769 if (!*after)
return NULL;
5772 const char* at = strchr(after,
'@');
5773 const char* host_start = NULL;
5774 char* username = NULL;
5775 char* password = NULL;
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));
5784 username = strndup(after, (
size_t)(at - after));
5786 host_start = at + 1;
5793 char* hostname = NULL;
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);
5804 const char* colon = strchr(host_start,
':');
5806 hostname = strndup(host_start, (
size_t)(colon - host_start));
5807 port = atoi(colon + 1);
5809 hostname = strdup(host_start);
5813 if (!hostname || !hostname[0])
goto fail;
5814 if (port <= 0 || port > 65535) {
5816 if (strcmp(scheme,
"http") == 0 || strcmp(scheme,
"https") == 0)
5818 else if (strcmp(scheme,
"socks5") == 0)
5824 if (!cfg)
goto fail;
5825 memset(cfg, 0,
sizeof(*cfg));
5826 cfg->
scheme = strdup(scheme);
5827 cfg->
host = hostname;
5841 if (!cfg || !*cfg)
return;
5852 const char* target_host,
5854 if (!proxy || !target_host)
return -1;
5857 if (fd < 0)
return -1;
5860 char connect_req[2048];
5863 len = snprintf(connect_req,
sizeof(connect_req),
5864 "CONNECT %s:%d HTTP/1.1\r\n"
5866 target_host, target_port,
5867 target_host, target_port);
5872 snprintf(cred,
sizeof(cred),
"%s:%s",
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",
5888 len += snprintf(connect_req + len,
sizeof(connect_req) - (
size_t)len,
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");
5900 char resp_buf[4096];
5901 ssize_t nr = recv(fd, resp_buf,
sizeof(resp_buf) - 1, 0);
5903 n_log(
LOG_ERR,
"n_proxy_connect_tunnel: no response from proxy");
5907 resp_buf[nr] =
'\0';
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),
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);
5925 const char* target_host,
5927 if (!proxy || !target_host)
return -1;
5930 if (fd < 0)
return -1;
5935#if OPENSSL_VERSION_NUMBER >= 0x10100000L
5936 const SSL_METHOD* method = TLS_client_method();
5938 const SSL_METHOD* method = TLSv1_2_client_method();
5940 SSL_CTX* ctx = SSL_CTX_new(method);
5942 n_log(
LOG_ERR,
"n_proxy_connect_tunnel_ssl: SSL_CTX_new failed");
5946 SSL_CTX_set_default_verify_paths(ctx);
5948 SSL* ssl = SSL_new(ctx);
5950 n_log(
LOG_ERR,
"n_proxy_connect_tunnel_ssl: SSL_new failed");
5955 SSL_set_fd(ssl, fd);
5956 SSL_set_tlsext_host_name(ssl, proxy->
host);
5958 if (SSL_connect(ssl) <= 0) {
5959 n_log(
LOG_ERR,
"n_proxy_connect_tunnel_ssl: TLS handshake to proxy %s:%d failed",
5967 n_log(
LOG_DEBUG,
"n_proxy_connect_tunnel_ssl: TLS established to proxy %s:%d",
5971 char connect_req[2048];
5974 len = snprintf(connect_req,
sizeof(connect_req),
5975 "CONNECT %s:%d HTTP/1.1\r\n"
5977 target_host, target_port,
5978 target_host, target_port);
5983 snprintf(cred,
sizeof(cred),
"%s:%s",
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",
5999 len += snprintf(connect_req + len,
sizeof(connect_req) - (
size_t)len,
6003 if (SSL_write(ssl, connect_req, len) != len) {
6004 n_log(
LOG_ERR,
"n_proxy_connect_tunnel_ssl: send CONNECT failed");
6012 char resp_buf[4096];
6013 int nr = SSL_read(ssl, resp_buf, (
int)(
sizeof(resp_buf) - 1));
6015 n_log(
LOG_ERR,
"n_proxy_connect_tunnel_ssl: no response from proxy");
6021 resp_buf[nr] =
'\0';
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),
6039 SSL_set_quiet_shutdown(ssl, 1);
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);
6051 const char* target_host,
6053 if (!proxy || !target_host)
return -1;
6056 if (fd < 0)
return -1;
6067 if (send(fd, greeting, 4, 0) != 4)
goto fail;
6072 if (send(fd, greeting, 3, 0) != 3)
goto fail;
6076 char method_resp[2];
6077 if (recv(fd, method_resp, 2, 0) != 2)
goto fail;
6078 if (method_resp[0] != 0x05)
goto fail;
6080 if (method_resp[1] == 0x02 && use_auth) {
6082 size_t ulen = strlen(proxy->
username);
6084 if (ulen > 255 || plen > 255)
goto fail;
6088 auth_req[pos++] = 0x01;
6089 auth_req[pos++] = (char)ulen;
6090 memcpy(auth_req + pos, proxy->
username, ulen);
6092 auth_req[pos++] = (char)plen;
6094 memcpy(auth_req + pos, proxy->
password, plen);
6097 if (send(fd, auth_req, pos, 0) != (ssize_t)pos)
goto fail;
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");
6105 }
else if (method_resp[1] != 0x00) {
6106 n_log(
LOG_ERR,
"n_proxy_connect_socks5: no acceptable method");
6112 size_t hlen = strlen(target_host);
6113 if (hlen > 255)
goto fail;
6117 conn_req[pos++] = 0x05;
6118 conn_req[pos++] = 0x01;
6119 conn_req[pos++] = 0x00;
6120 conn_req[pos++] = 0x03;
6121 conn_req[pos++] = (char)hlen;
6122 memcpy(conn_req + pos, target_host, hlen);
6124 conn_req[pos++] = (char)((target_port >> 8) & 0xFF);
6125 conn_req[pos++] = (char)(target_port & 0xFF);
6127 if (send(fd, conn_req, pos, 0) != (ssize_t)pos)
goto fail;
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",
6143 if (conn_resp[3] == 0x03 && nr < 5) {
6146 recv(fd, extra,
sizeof(extra), 0);
6147 }
else if (conn_resp[3] == 0x04 && nr < 10) {
6150 size_t need = 22 - (size_t)nr;
6151 if (need <=
sizeof(extra)) {
6152 recv(fd, extra, need, 0);
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);
6162 n_log(
LOG_ERR,
"n_proxy_connect_socks5: handshake failed");
static NETWORK_POOL * pool
NETWORK * netw
Network for server mode, accepting incomming.
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.
#define init_lock(__rwlock_mutex)
Macro for initializing a rwlock.
#define FreeNoLog(__ptr)
Free Handler without log.
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
#define __n_assert(__ptr, __ret)
macro to assert things
#define _str(__PTR)
define true
#define rw_lock_destroy(__rwlock_mutex)
Macro to destroy rwlock mutex.
#define unlock(__rwlock_mutex)
Macro for releasing read/write lock a rwlock mutex.
#define endif
close a ifwhatever block
#define write_lock(__rwlock_mutex)
Macro for acquiring a write lock on a rwlock mutex.
#define Free(__ptr)
Free Handler to get errors.
#define read_lock(__rwlock_mutex)
Macro for acquiring a read lock on a rwlock mutex.
#define _nstr(__PTR)
N_STR or "NULL" string for logging purposes.
N_STR * n_base64_encode(N_STR *input)
encode a N_STR *string
#define N_ENUM_DEFINE(MACRO_DEFINITION, enum_name)
Macro to define an N_ENUM.
#define N_ENUM_ENTRY(class, method)
helper to build an N_ENUM
size_t nb_keys
total number of used keys in the table
int ht_get_ptr(HASH_TABLE *table, const char *key, void **val)
get pointer at 'key' from 'table'
#define ht_foreach(__ITEM_, __HASH_)
ForEach macro helper (classic / old)
int destroy_ht(HASH_TABLE **table)
empty a table and destroy it
int ht_remove(HASH_TABLE *table, const char *key)
remove and delete node at key in table
HASH_TABLE * new_ht(size_t size)
Create a hash table with the given size.
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
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
#define hash_val(node, type)
Cast a HASH_NODE element.
structure of a hash table node
structure of a hash table
size_t nb_items
number of item currently in the list
#define list_shift(__LIST_, __TYPE_)
Shift macro helper for void pointer casting.
int list_empty(LIST *list)
Empty a LIST list of pointers.
LIST_NODE * list_search(LIST *list, const void *ptr)
search ptr in list
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
#define list_foreach(__ITEM_, __LIST_)
ForEach macro helper, safe for node removal during iteration.
#define remove_list_node(__LIST_, __NODE_, __TYPE_)
Remove macro helper for void pointer casting.
int list_destroy(LIST **list)
Empty and Free a list container.
LIST * new_generic_list(size_t max_items)
Initialiaze a generic list container to max_items pointers.
#define MAX_LIST_ITEMS
flag to pass to new_generic_list for the maximum possible number of item in a list
Structure of a generic list node.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_DEBUG
debug-level messages
#define LOG_ERR
error conditions
#define LOG_WARNING
warning conditions
#define LOG_INFO
informational
size_t written
size of the written data inside the string
size_t length
length of string (in case we wanna keep information after the 0 end of string value)
void free_nstr_ptr(void *ptr)
Free a N_STR pointer structure.
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
#define nstrcat(__nstr_dst, __nstr_src)
Macro to quickly concatenate two N_STR.
N_STR * nstrdup(N_STR *str)
Duplicate a N_STR.
#define nstrprintf_cat(__nstr_var, __format,...)
Macro to quickly allocate and sprintf and cat to a N_STR.
N_STR * char_to_nstr(const char *src)
Convert a char into a N_STR, short version.
N_STR * new_nstr(NSTRBYTE size)
create a new N_STR string
#define nstrprintf(__nstr_var, __format,...)
Macro to quickly allocate and sprintf to N_STR.
A box including a string and his lenght.
void u_sleep(unsigned int usec)
wrapper around usleep for API consistency
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
char query[2048]
query string (or empty)
char * ip
ip of the connected socket
N_SOCKET link
networking socket
char * certificate
openssl certificate file
char netw_errors[8][512]
per-connection error capture ring buffer (max 8 entries, 512 chars each)
int threaded_engine_status
Threaded network engine state for this network.
size_t content_length
Store content length.
char * type
Type of request.
pthread_t send_thr
sending thread
int nb_pending
Nb pending connection,if listening.
int so_reuseaddr
so reuseaddr state
pthread_t recv_thr
receiving thread
int port
port number (0 if not specified)
char * host
proxy hostname
pthread_rwlock_t rwlock
thread safety
NETWORK * netw
underlying network connection
struct sockaddr_storage raddr
connected remote addr
char * path
path starting with "/", or "/" if empty
pthread_mutex_t eventbolt
mutex for threaded access of state event
const SSL_METHOD * method
SSL method container.
N_STR * body
response body
int netw_err_next
next write slot in ring buffer
int deplete_socket_timeout
deplete socket send buffer timeout ( 0 disabled, > 0 wait for timeout and check unset/unack datas)
char * scheme
"http" or "https"
int deplete_queues_timeout
deplete network queues timeout ( 0 disabled, > 0 wait for timeout and check unset/unack datas)
int nb_running_threads
nb running threads, if > 0 thread engine is still running
pthread_mutex_t recvbolt
mutex for threaded access of recv buf
NETWORK * netw
underlying network connection
void(* on_event)(N_SSE_EVENT *event, struct N_SSE_CONN *conn, void *user_data)
callback
pthread_mutex_t sendbolt
mutex for threaded access of send_buf
int send_queue_consecutive_wait
send queue consecutive pool interval, used when there are still items to send, in usec
N_STR * event
event type (or NULL for default)
int connected
1 if handshake completed
N_STR * body
request body (or NULL)
int so_rcvtimeo
send timeout value
char * password
NULL if no auth.
char * query
raw query string without leading '?', or NULL
N_URL_PARAM params[64]
parsed key=value pairs
int tcpnodelay
state of naggle algorythm, 0 untouched, 1 forcibly disabled
char * body
Pointer to the body data.
LIST * headers
list of char* "Name: Value" strings
char * value
parameter value
char * host
remote hostname
int netw_err_count
number of captured errors
SOCKET sock
a normal socket
char path[2048]
request path
char * scheme
"http", "https", or "socks5"
char * port
port of socket
LIST * recv_buf
reveicing buffer (for incomming usage)
int transport_type
transport type: NETWORK_TCP (0) or NETWORK_UDP (1)
int nb_params
number of parsed parameters
int user_id
if part of a user property, id of the user
sem_t send_blocker
block sending func
SSL_CTX * ctx
SSL context holder.
int so_sndbuf
size of the socket send buffer, 0 untouched, else size in bytes
int so_sndtimeo
send timeout value
int retry
retry interval in ms (0 if not set)
char content_type[256]
Store content type.
N_STR * id
last event ID (or NULL)
struct addrinfo hints
address of local machine
int so_keepalive
so keepalive state
netw_func send_data
send func ptr
int addr_infos_loaded
Internal flag to know if we have to free addr infos.
N_STR * payload
message payload
char method[16]
HTTP method.
LIST * pools
pointers to network pools if members of any
char * username
NULL if no auth.
int status_code
HTTP status code.
char * key
openssl key file
int so_linger
close lingering value (-1 disabled, 0 force close, >0 linger )
unsigned long int is_blocking
flag to quickly check socket mode
int crypto_algo
if encryption is on, which one (flags NETW_ENCRYPT_*)
char content_type[128]
Content-Type header value.
HASH_TABLE * pool
table of clients
LIST * send_buf
sending buffer (for outgoing queuing )
int mode
NETWORK mode , 1 listening, 0 connecting.
int so_rcvbuf
size of the socket recv buffer, 0 untouched, else size in bytes
void * user_data
user data for callback
netw_func recv_data
receive func ptr
int wait_close_timeout
network wait close timeout value ( < 1 disabled, >= 1 timeout sec )
#define NETW_SOCKET_ERROR
code for a socket error
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.
#define N_URL_MAX_PARAMS
maximum number of parsed query parameters
N_STR * netw_get_msg(NETWORK *netw)
Get a message from aimed NETWORK.
int netw_add_msg(NETWORK *netw, N_STR *msg)
Add a message to send in aimed NETWORK.
const char * n_netw_get_connect_error(int index)
Get pre-connection error message by index.
ssize_t send_ssl_data(void *netw, char *buf, uint32_t n)
send data onto the socket
char * netw_extract_http_request_type(const char *request)
function to extract the request method from an http request
int netw_get_queue_status(NETWORK *netw, size_t *nb_to_send, size_t *nb_to_read)
retrieve network send queue status
int netw_bind_udp(NETWORK **netw, char *addr, char *port, int ip_version)
Create a UDP bound socket for receiving datagrams.
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
int netw_init_wsa(int mode, int v1, int v2)
Do not directly use, internal api.
int netw_ssl_set_verify(NETWORK *netw, int enable)
enable or disable SSL peer certificate verification
ssize_t send_php(SOCKET s, int _code, char *buf, int n)
send data onto the socket
int netw_stop_thr_engine(NETWORK *netw)
Stop a NETWORK connection sending and receing thread.
void n_ws_close(N_WS_CONN *conn)
Send close frame and close the connection.
char * netw_urlencode(const char *str, size_t len)
function to perform URL encoding
N_STR * n_url_encode(const char *str)
percent-encode a string for use in URLs (returns N_STR)
void * netw_send_func(void *NET)
Thread send function.
NETWORK * netw_accept_nonblock_from(NETWORK *from, int blocking)
make a normal blocking 'accept' .
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.
N_PROXY_CFG * n_proxy_cfg_parse(const char *url)
Parse a proxy URL string into an N_PROXY_CFG struct.
int netw_set_crypto(NETWORK *netw, char *key, char *certificate)
activate SSL encryption on selected network, using key and certificate
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
int netw_ssl_set_ca(NETWORK *netw, const char *ca_file, const char *ca_path)
set custom CA verify location for SSL context
void n_sse_stop(N_SSE_CONN *conn)
Signal the SSE connection to stop reading.
#define NETWORK_IPV6
Flag to force IPV6
#define NETWORK_UDP
Flag for UDP transport.
int netw_get_http_date(char *buffer, size_t buffer_size)
helper function to generate the current date in HTTP format
NETWORK_POOL * netw_new_pool(size_t nb_min_element)
return a new network pool of nb_min_element
int netw_set_user_id(NETWORK *netw, int id)
associate an id and a network
void n_mock_server_free(N_MOCK_SERVER **server)
Free a mock server and close the listening socket.
ssize_t recv_data(void *netw, char *buf, uint32_t n)
recv data from the socket
int netw_init_openssl(void)
Do not directly use, internal api.
void n_netw_clear_errors(NETWORK *netw)
Clear captured errors on a NETWORK handle.
ssize_t recv_ssl_data(void *netw, char *buf, uint32_t n)
recv data from the socket
int netw_make_listening(NETWORK **netw, char *addr, char *port, int nbpending, int ip_version)
Make a NETWORK be a Listening network.
int netw_ssl_do_handshake(NETWORK *netw, const char *sni_hostname)
Complete the SSL handshake on an already-connected NETWORK.
#define HEAD_SIZE
Size of a HEAD message.
ssize_t send_udp_data(void *netw, char *buf, uint32_t n)
send data via UDP on a connected socket
#define netw_atomic_write_state(netw, val)
Lock-free atomic write of the network state field.
const char * n_netw_get_error(NETWORK *netw, int index)
Get captured error message by index (0 = oldest).
int netw_start_thr_engine(NETWORK *netw)
Start the NETWORK netw Threaded Engine.
int netw_destroy_pool(NETWORK_POOL **netw_pool)
free a NETWORK_POOL *pool
void n_url_free(N_URL **u)
free a N_URL and all its members
#define NETWORK_IPV4
Flag to force IPV4
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
void * netw_recv_func(void *NET)
To Thread Receiving function.
void n_sse_conn_free(N_SSE_CONN **conn)
Free an SSE connection structure.
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.
N_STR * n_url_decode(const char *str)
decode a percent-encoded string (returns N_STR)
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://).
#define SOCKET_SIZE_FORMAT
socket associated printf style
__netw_code_type size_t htonst(size_t value)
host to network size_t
int netw_unload_openssl(void)
Do not directly use, internal api.
#define HEAD_CODE
Code of a HEAD message.
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.
size_t ntohst(size_t value)
network to host size_t
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
#define NETWORK_CONSECUTIVE_SEND_WAIT
Flag to set consecutive send waiting timeout
size_t netw_pool_nbclients(NETWORK_POOL *netw_pool)
return the number of networks in netw_pool
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.
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' .
void n_netw_clear_connect_errors(void)
Clear pre-connection errors on this thread.
void n_mock_server_stop(N_MOCK_SERVER *server)
Signal the mock server to stop accepting connections.
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.
#define NETWORK_IPALL
Flag for auto detection by OS of ip version to use.
int SOCKET
default socket declaration
#define NETW_SOCKET_DISCONNECTED
Code for a disconnected recv.
int n_netw_get_error_count(NETWORK *netw)
Get number of captured errors on a NETWORK handle.
int netw_pool_broadcast(NETWORK_POOL *netw_pool, const NETWORK *from, N_STR *net_msg)
add net_msg to all network in netork pool
void n_sse_event_clean(N_SSE_EVENT *event)
Free the contents of an SSE event (does not free the struct itself).
#define NETWORK_WAIT_CLOSE_TIMEOUT
Flag to set network closing wait timeout.
int netw_setsockopt(NETWORK *netw, int optname, int value)
Modify common socket options on the given netw.
int netw_set(NETWORK *netw, int flag)
Restart or reset the specified network ability.
ssize_t send_data(void *netw, char *buf, uint32_t n)
send data onto the socket
ssize_t recv_udp_data(void *netw, char *buf, uint32_t n)
recv data via UDP from a connected socket
int netw_get_state(NETWORK *netw, uint32_t *state, int *thr_engine_status)
Get the state of a network.
N_URL * n_url_parse(const char *url)
parse a URL string into components
void netw_pool_netw_close(void *netw_ptr)
close a network from a network pool
int n_netw_get_connect_error_count(void)
Get number of pre-connection errors captured on this thread.
size_t netw_calculate_urlencoded_size(const char *str, size_t len)
function to calculate the required size for the URL-encoded string
int deplete_send_buffer(int fd, int timeout)
wait until the socket is empty or timeout, checking each 100 msec.
NETWORK * netw_accept_from(NETWORK *from)
make a normal blocking 'accept' .
#define NETW_MAX_RETRIES
Send or recv max number of retries.
void n_proxy_cfg_free(N_PROXY_CFG **cfg)
Free an N_PROXY_CFG created by n_proxy_cfg_parse().
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
N_STR * n_url_build(const N_URL *u)
build a URL string from parsed components
void n_mock_server_run(N_MOCK_SERVER *server)
Run the mock server accept loop.
int netw_send_quit(NETWORK *netw)
Add a formatted NETMSG_QUIT message to the specified network.
int netw_ssl_connect_client(NETWORK **netw, char *host, char *port, int ip_version)
Connect as an SSL client without providing a client certificate.
const char * netw_get_http_status_message(int status_code)
helper function to convert status code to a human-readable message
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
int n_ws_recv(N_WS_CONN *conn, N_WS_MESSAGE *msg_out)
Receive one WebSocket frame.
int netw_set_blocking(NETWORK *netw, unsigned long int is_blocking)
Modify blocking socket mode.
N_STR * netw_wait_msg(NETWORK *netw, unsigned int refresh, size_t timeout)
Wait a message from aimed NETWORK.
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.
int netw_send_ping(NETWORK *netw, int type, int id_from, int id_to, int time)
Add a ping reply to the network.
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.
NETWORK_HTTP_INFO netw_extract_http_info(char *request)
extract a lot of informations, mostly as pointers, and populate a NETWORK_HTTP_INFO structure
#define netw_atomic_read_state(netw)
Lock-free atomic read of the network state field.
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.
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.
__netw_code_type
Network codes declaration.
#define NETWORK_DEPLETE_SOCKET_TIMEOUT
Flag to set send buffer depletion timeout
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.
char * netw_urldecode(const char *str)
Function to decode URL-encoded data.
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
void n_ws_conn_free(N_WS_CONN **conn)
Free a WebSocket connection structure.
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.
#define NETWORK_DEPLETE_QUEUES_TIMEOUT
Flag to set network queues depletion timeout
int netw_pool_add(NETWORK_POOL *netw_pool, NETWORK *netw)
add a NETWORK *netw to a NETWORK_POOL *pool
int netw_connect_udp(NETWORK **netw, char *host, char *port, int ip_version)
Connect a UDP socket to a remote host.
int netw_info_destroy(NETWORK_HTTP_INFO http_request)
destroy a NETWORK_HTTP_INFO loaded informations
HASH_TABLE * netw_parse_post_data(const char *post_data)
Function to parse POST data.
const char * netw_guess_http_content_type(const char *url)
function to guess the content type based on URL extension
int n_ws_send(N_WS_CONN *conn, const char *payload, size_t len, int opcode)
Send a WebSocket frame (client always masks).
ssize_t recv_php(SOCKET s, int *_code, char **buf)
recv data from the socket
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.
int netw_pool_remove(NETWORK_POOL *netw_pool, NETWORK *netw)
remove a NETWORK *netw to a NETWORK_POOL *pool
int netw_add_msg_ex(NETWORK *netw, char *str, unsigned int length)
Add a message to send in aimed NETWORK.
#define NETWORK_TCP
Flag for TCP transport (default)
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
@ NETW_THR_ENGINE_STARTED
@ NETW_THR_ENGINE_STOPPED
parsed HTTP request for mock server callback
HTTP response to send from mock server callback.
Parsed proxy URL components.
SSE event received from server.
structure for splitting HTTP requests
structure of a network pool
Base64 encoding and decoding functions using N_STR.
Hash functions and table.
#define NETW_CALL_RETRY(__retvar, __expression, __max_tries)
network-aware retry macro: retries on EINTR and EAGAIN/EWOULDBLOCK
static __thread int s_connect_err_next
static ssize_t _ws_read(N_WS_CONN *conn, void *buf, size_t len)
read exactly len bytes from a WebSocket connection
static void netw_init_locks(void)
void netw_ssl_print_errors(SOCKET socket)
print OpenSSL errors for a given socket
static ssize_t _sse_write(NETWORK *netw, const void *buf, size_t len)
write bytes to an SSE connection (SSL or plain)
#define _Thread_local
thread-local pre-connection error buffer (DNS, socket creation)
static void _n_parse_query_params(N_URL *u, const char *qs)
static void _n_mock_parse_request(const char *buf, N_HTTP_REQUEST *req)
Parse a raw HTTP request buffer into an N_HTTP_REQUEST.
static ssize_t _ws_write(N_WS_CONN *conn, const void *buf, size_t len)
write bytes to a WebSocket connection (SSL or plain)
#define NETW_BUFLEN_CAST(x)
cast for send/recv buffer length: int on Windows, size_t on POSIX
NETWORK * netw_new(size_t send_list_limit, size_t recv_list_limit)
Return an empty allocated network ready to be netw_closed.
static ssize_t _sse_read_byte(NETWORK *netw, char *ch, volatile int *stop_flag)
read one byte from an SSE connection (SSL or plain).
N_ENUM_netw_code_type
network error code
#define netstrerror(code)
BSD style errno string NO WORKING ON REDHAT.
static int OPENSSL_IS_INITIALIZED
static __thread int s_connect_err_count
#define neterrno
get last socket error code, linux version
static pthread_mutex_t * netw_ssl_lockarray
static void _netw_capture_connect_error(const char *fmt,...)
capture a pre-connection error (thread-local)
char * get_in_addr(struct sockaddr *sa)
get sockaddr, IPv4 or IPv6
static void _n_mock_request_clean(N_HTTP_REQUEST *req)
Free the contents of an N_HTTP_REQUEST (does not free the struct itself).
char * netw_get_openssl_error_string()
get the OpenSSL error string
static __thread char s_connect_errors[8][512]
static void _netw_capture_error(NETWORK *netw, const char *fmt,...)
capture an error into a NETWORK handle's ring buffer
static void netw_kill_locks(void)
static int _proxy_tcp_connect(const char *host, int port)
Helper: connect a plain TCP socket to host:port.
Network messages , serialization tools.