52static char* _n_strndup(
const char* s,
size_t n) {
53 size_t len = strlen(s);
55 char* p = (
char*)malloc(len + 1);
62#define strndup _n_strndup
66#include <openssl/sha.h>
67#include <openssl/rand.h>
68#include <openssl/x509v3.h>
75#define _Thread_local __thread
87 vsnprintf(
netw->netw_errors[
netw->netw_err_next], 512, fmt, ap);
89 netw->netw_err_next = (
netw->netw_err_next + 1) % 8;
90 if (
netw->netw_err_count < 8)
netw->netw_err_count++;
107 return netw ?
netw->netw_err_count : 0;
111 if (!
netw || index < 0 || index >=
netw->netw_err_count)
return NULL;
112 int oldest = (
netw->netw_err_next -
netw->netw_err_count + 8) % 8;
113 return netw->netw_errors[(oldest + index) % 8];
118 netw->netw_err_count = 0;
119 netw->netw_err_next = 0;
146#if __BYTE_ORDER == __LITTLE_ENDIAN
147 if (
sizeof(
size_t) == 4) {
148 return (
size_t)htonl((uint32_t)value);
149 }
else if (
sizeof(
size_t) == 8) {
150 return ((
size_t)htonl((uint32_t)(value >> 32)) |
151 ((
size_t)htonl((uint32_t)value) << 32));
163#if __BYTE_ORDER == __LITTLE_ENDIAN
164 if (
sizeof(
size_t) == 4) {
165 return (
size_t)ntohl((uint32_t)value);
166 }
else if (
sizeof(
size_t) == 8) {
167 return ((
size_t)ntohl((uint32_t)(value >> 32)) |
168 ((
size_t)ntohl((uint32_t)value) << 32));
181char* wchar_to_char(
const wchar_t* pwchar) {
185 int currentCharIndex = 0;
186 char currentChar = (char)pwchar[currentCharIndex];
187 char* filePathC = NULL;
189 while (currentChar !=
'\0') {
191 currentChar = (char)pwchar[currentCharIndex];
194 const int charCount = currentCharIndex + 1;
197 Malloc(filePathC,
char, (
size_t)charCount);
200 for (
int i = 0; i < charCount; i++) {
202 char character = (char)pwchar[i];
204 *filePathC = character;
206 filePathC +=
sizeof(char);
210 filePathC -= (
sizeof(char) * (size_t)charCount);
216#define NETW_CALL_RETRY(__retvar, __expression, __max_tries) \
218 int __nb_retries = 0; \
220 __retvar = (__expression); \
222 } while (__retvar == -1 && (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK) && __nb_retries < (__max_tries)); \
223 if (__retvar == -1 && __nb_retries >= (__max_tries)) __retvar = -2; \
227#define neterrno WSAGetLastError()
234 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
236 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
237 (LPWSTR)&ws, 0, NULL);
238 char* netstr = wchar_to_char(ws);
243#if __GNUC__ <= 6 && __GNUC_MINOR__ <= 3
279size_t strlcpy(
char* dst,
const char* src,
size_t siz) {
287 if ((*d++ = *s++) ==
'\0')
299 return (s - src - 1);
324static char* inet_ntop4(
const unsigned char* src,
char* dst, socklen_t size);
325static char* inet_ntop6(
const unsigned char* src,
char* dst, socklen_t size);
335char* inet_ntop(
int af,
const void* src,
char* dst, socklen_t size) {
338 return (inet_ntop4((
const unsigned char*)src, dst, size));
340 return (inet_ntop6((
const unsigned char*)src, dst, size));
358static char* inet_ntop4(
const unsigned char* src,
char* dst, socklen_t size) {
359 static const char fmt[] =
"%u.%u.%u.%u";
360 char tmp[
sizeof "255.255.255.255"];
363 l = snprintf(tmp,
sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
364 if (l <= 0 || (socklen_t)l >= size) {
367 strlcpy(dst, tmp, size);
377static char* inet_ntop6(
const unsigned char* src,
char* dst, socklen_t size) {
385 char tmp[
sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
390#define NS_IN6ADDRSZ 16
392 u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
400 memset(words,
'\0',
sizeof words);
401 for (i = 0; i < NS_IN6ADDRSZ; i++)
402 words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
407 for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
410 cur.base = i, cur.len = 1;
414 if (cur.base != -1) {
415 if (best.base == -1 || cur.len > best.len)
421 if (cur.base != -1) {
422 if (best.base == -1 || cur.len > best.len)
425 if (best.base != -1 && best.len < 2)
432 for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
434 if (best.base != -1 && i >= best.base &&
435 i < (best.base + best.len)) {
444 if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 7 && words[7] != 0x0001) || (best.len == 5 && words[5] == 0xffff))) {
445 if (!inet_ntop4(src + 12, tp,
sizeof tmp - (tp - tmp)))
450 tp += sprintf(tp,
"%x", words[i]);
453 if (best.base != -1 && (best.base + best.len) ==
454 (NS_IN6ADDRSZ / NS_INT16SZ))
461 if ((socklen_t)(tp - tmp) > size) {
490static int inet_pton4(
const char* src, u_char* dst);
491static int inet_pton6(
const char* src, u_char* dst);
500int inet_pton(
int af,
const char* src,
void* dst) {
503 return (inet_pton4(src, (
unsigned char*)dst));
505 return (inet_pton6(src, (
unsigned char*)dst));
522static int inet_pton4(
const char* src, u_char* dst) {
523 static const char digits[] =
"0123456789";
524 int saw_digit, octets, ch;
530 u_char tmp[NS_INADDRSZ] = {0}, *tp;
535 while ((ch = *src++) !=
'\0') {
538 if ((pch = strchr(digits, ch)) != NULL) {
539 u_int uiNew = *tp * 10 + (pch - digits);
541 if (saw_digit && *tp == 0)
551 }
else if (ch ==
'.' && saw_digit) {
561 memcpy(dst, tmp, NS_INADDRSZ);
578static int inet_pton6(
const char* src, u_char* dst) {
579 static const char xdigits_l[] =
"0123456789abcdef",
580 xdigits_u[] =
"0123456789ABCDEF";
581#define NS_IN6ADDRSZ 16
583 u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
585 int ch, seen_xdigits;
588 memset((tp = tmp),
'\0', NS_IN6ADDRSZ);
589 endp = tp + NS_IN6ADDRSZ;
598 while ((ch = *src++) !=
'\0') {
599 const char* xdigits :
const char* pch;
601 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
602 pch = strchr((xdigits = xdigits_u), ch);
605 val |= (pch - xdigits);
606 if (++seen_xdigits > 4)
617 }
else if (*src ==
'\0') {
620 if (tp + NS_INT16SZ > endp)
622 *tp++ = (u_char)(val >> 8) & 0xff;
623 *tp++ = (u_char)val & 0xff;
628 if (ch ==
'.' && ((tp + NS_INADDRSZ) <= endp) &&
629 inet_pton4(curtok, tp) > 0) {
637 if (tp + NS_INT16SZ > endp)
639 *tp++ = (u_char)(val >> 8) & 0xff;
640 *tp++ = (u_char)val & 0xff;
642 if (colonp != NULL) {
647 const int n = tp - colonp;
652 for (i = 1; i <= n; i++) {
653 endp[-i] = colonp[n - i];
660 memcpy(dst, tmp, NS_IN6ADDRSZ);
668#include <sys/types.h>
672#define NETW_CALL_RETRY(__retvar, __expression, __max_tries) \
674 int __nb_retries = 0; \
676 __retvar = (__expression); \
678 } while (__retvar == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) && __nb_retries < (__max_tries)); \
679 if (__retvar == -1 && __nb_retries >= (__max_tries)) __retvar = -2; \
683#define neterrno errno
703 return strdup(strerror(code));
721 netw->nb_pending = -1;
724 netw->nb_running_threads = 0;
733 netw->link.sock = INVALID_SOCKET;
735 netw->link.ip = NULL;
736 memset(&
netw->link.hints, 0,
sizeof(
struct addrinfo));
737 memset(&
netw->link.raddr, 0,
sizeof(
struct sockaddr_storage));
740 if (pthread_mutex_init(&
netw->sendbolt, NULL) != 0) {
746 if (pthread_mutex_init(&
netw->recvbolt, NULL) != 0) {
748 pthread_mutex_destroy(&
netw->sendbolt);
753 if (pthread_mutex_init(&
netw->eventbolt, NULL) != 0) {
755 pthread_mutex_destroy(&
netw->sendbolt);
756 pthread_mutex_destroy(&
netw->recvbolt);
761 if (sem_init(&
netw->send_blocker, 0, 0) != 0) {
763 pthread_mutex_destroy(&
netw->eventbolt);
764 pthread_mutex_destroy(&
netw->sendbolt);
765 pthread_mutex_destroy(&
netw->recvbolt);
771 if (!
netw->recv_buf) {
772 n_log(
LOG_ERR,
"Error when creating receive list with %zu item limit", recv_list_limit);
777 if (!
netw->send_buf) {
778 n_log(
LOG_ERR,
"Error when creating send list with %zu item limit", send_list_limit);
788 netw->addr_infos_loaded = 0;
789 netw->send_queue_consecutive_wait = 0;
790 netw->so_reuseaddr = -1;
792 netw->so_keepalive = -1;
793 netw->tcpnodelay = -1;
794 netw->so_sndbuf = -1;
795 netw->so_rcvbuf = -1;
796 netw->so_rcvtimeo = -1;
797 netw->so_sndtimeo = -1;
798 netw->so_linger = -1;
799 netw->deplete_socket_timeout = -1;
800 netw->deplete_queues_timeout = -1;
801 netw->wait_close_timeout = -1;
815 netw->certificate = NULL;
818 netw->link.is_blocking = 1;
829 return sa->sa_family == AF_INET
830 ? (
char*)&(((
struct sockaddr_in*)sa)->sin_addr)
831 : (
char*)&(((
struct sockaddr_in6*)sa)->sin6_addr);
842 int compiler_warning_suppressor = 0;
843#if !defined(__linux__) && !defined(__sun) && !defined(_AIX)
844 static WSADATA WSAdata;
845 static int WSA_IS_INITIALIZED = 0;
851 return WSA_IS_INITIALIZED;
855 if (WSA_IS_INITIALIZED == 1)
857 if ((WSAStartup(MAKEWORD(v1, v2), &WSAdata)) != 0) {
858 WSA_IS_INITIALIZED = 0;
861 WSA_IS_INITIALIZED = 1;
867 if (WSA_IS_INITIALIZED == 0)
869 if (WSACleanup() == 0) {
870 WSA_IS_INITIALIZED = 0;
876 compiler_warning_suppressor =
mode + v1 + v2;
877 (void)compiler_warning_suppressor;
878 compiler_warning_suppressor = TRUE;
879 return compiler_warning_suppressor;
918#if defined(__linux__) || defined(__sun)
920 flags = fcntl(
netw->link.sock, F_GETFL, 0);
921 if (
netw->link.is_blocking != 0 && !is_blocking) {
922 if (flags & O_NONBLOCK) {
925 netw->link.is_blocking = 0;
928 }
else if (
netw->link.is_blocking != 1 && is_blocking) {
929 if (!(flags & O_NONBLOCK)) {
932 netw->link.is_blocking = 1;
936 if (fcntl(
netw->link.sock, F_SETFL, is_blocking ? flags & ~O_NONBLOCK : flags | O_NONBLOCK) == -1) {
940 n_log(
LOG_ERR,
"couldn't set blocking mode %lu on %d: %s", is_blocking,
netw->link.sock,
_str(errmsg));
945 unsigned long int blocking = 1 - is_blocking;
946 int res = ioctlsocket(
netw->link.sock, (
long)FIONBIO, &blocking);
948 if (res != NO_ERROR) {
951 n_log(
LOG_ERR,
"ioctlsocket failed with error: %d , neterrno: %s", res,
_str(errmsg));
957 netw->link.is_blocking = is_blocking;
979 if (setsockopt(
netw->link.sock, IPPROTO_TCP, TCP_NODELAY, (
const char*)&value,
sizeof(value)) == -1) {
988 netw->tcpnodelay = value;
993 if (setsockopt(
netw->link.sock, SOL_SOCKET, SO_SNDBUF, (
const char*)&value,
sizeof(value)) == -1) {
1002 netw->so_sndbuf = value;
1007 if (setsockopt(
netw->link.sock, SOL_SOCKET, SO_RCVBUF, (
const char*)&value,
sizeof(value)) == -1) {
1016 netw->so_rcvbuf = value;
1020 if (setsockopt(
netw->link.sock, SOL_SOCKET, SO_REUSEADDR, (
char*)&value,
sizeof(value)) == -1) {
1028 netw->so_reuseaddr = value;
1047 }
else if (value == 0) {
1053 ling.l_linger = (u_short)value;
1055 ling.l_linger = value;
1059 if (setsockopt(
netw->link.sock, SOL_SOCKET, SO_LINGER, &ling,
sizeof(ling)) == -1) {
1063 n_log(
LOG_ERR,
"Error from setsockopt(SO_LINGER) on socket %d. neterrno: %s",
netw->link.sock,
_str(errmsg));
1068 if (setsockopt(
netw->link.sock, SOL_SOCKET, SO_LINGER, (
const char*)&ling,
sizeof(ling)) == -1) {
1077 netw->so_linger = value;
1086 if (setsockopt(
netw->link.sock, SOL_SOCKET, SO_RCVTIMEO, (
const char*)&tv,
sizeof tv) == -1) {
1090 n_log(
LOG_ERR,
"Error from setsockopt(SO_RCVTIMEO) on socket %d. neterrno: %s",
netw->link.sock,
_str(errmsg));
1096 if (setsockopt(
netw->link.sock, SOL_SOCKET, SO_RCVTIMEO, (
const char*)&value,
sizeof value) == -1) {
1106 netw->so_rcvtimeo = value;
1116 if (setsockopt(
netw->link.sock, SOL_SOCKET, SO_SNDTIMEO, (
const char*)&tv,
sizeof tv) == -1) {
1120 n_log(
LOG_ERR,
"Error from setsockopt(SO_SNDTIMEO) on socket %d. neterrno: %s",
netw->link.sock,
_str(errmsg));
1126 if (setsockopt(
netw->link.sock, SOL_SOCKET, SO_SNDTIMEO, (
const char*)&value,
sizeof value) == -1) {
1136 netw->so_sndtimeo = value;
1139 netw->deplete_socket_timeout = value;
1142 netw->deplete_queues_timeout = value;
1145 netw->wait_close_timeout = value;
1148 netw->send_queue_consecutive_wait = value;
1151 case TCP_USER_TIMEOUT:
1153 if (setsockopt(
netw->link.sock, IPPROTO_TCP, TCP_USER_TIMEOUT, (
const char*)&value,
sizeof value) == -1) {
1157 n_log(
LOG_ERR,
"Error from setsockopt(TCP_USER_TIMEOUT) on socket %d. neterrno: %s",
netw->link.sock,
_str(errmsg));
1164 if (setsockopt(
netw->link.sock, IPPROTO_TCP, TCP_QUICKACK, &value,
sizeof(value)) < 0) {
1168 n_log(
LOG_ERR,
"Error setting setsockopt(TCP_QUICKACK) to %d on sock %d. neterrno: %s", value,
netw->link.sock,
_str(errmsg));
1175 if (setsockopt(
netw->link.sock, SOL_SOCKET, SO_KEEPALIVE, (
const char*)&value,
sizeof value) == -1) {
1183 netw->so_keepalive = value;
1188 n_log(
LOG_ERR,
"%d is not a supported setsockopt", optname);
1201 BIO* bio = BIO_new(BIO_s_mem());
1206 ERR_print_errors(bio);
1209 size_t len = (size_t)BIO_get_mem_data(bio, &buf);
1212 char* error_str = malloc(len + 1);
1214 memcpy(error_str, buf, len);
1215 error_str[len] =
'\0';
1232 unsigned long error = 0;
1233 while ((error = ERR_get_error())) {
1235#ifdef SSL_R_UNEXPECTED_EOF_WHILE_READING
1242 if (ERR_GET_REASON(error) == SSL_R_UNEXPECTED_EOF_WHILE_READING)
1245 n_log(level,
"socket %d: %s", socket, ERR_reason_error_string(error));
1291 if (
mode & CRYPTO_LOCK) {
1298__attribute__((unused))
static unsigned long thread_id(
void) {
1301 ret = (
unsigned long)pthread_self();
1308 size_t lock_count = (size_t)CRYPTO_num_locks();
1309 netw_ssl_lockarray = (pthread_mutex_t*)OPENSSL_malloc((
size_t)(lock_count *
sizeof(pthread_mutex_t)));
1311 for (i = 0; i < CRYPTO_num_locks(); i++) {
1315 CRYPTO_set_id_callback((
unsigned long (*)())thread_id);
1316 CRYPTO_set_locking_callback((
void (*)())netw_ssl_lock_callback);
1322 CRYPTO_set_locking_callback(NULL);
1323 for (i = 0; i < CRYPTO_num_locks(); i++)
1340 SSL_load_error_strings();
1342#if OPENSSL_VERSION_NUMBER < 0x10100000L
1343 ERR_load_BIO_strings();
1345 OpenSSL_add_all_algorithms();
1356 signal(SIGPIPE, SIG_IGN);
1387 SSL_CTX_free(
netw->ctx);
1402 char* tmp_key = NULL;
1403 char* tmp_cert = NULL;
1404 if (
key && strlen(
key) > 0) {
1405 tmp_key = strdup(
key);
1407 n_log(
LOG_ERR,
"strdup failed for key in netw_set_crypto");
1411 if (certificate && strlen(certificate) > 0) {
1412 tmp_cert = strdup(certificate);
1414 n_log(
LOG_ERR,
"strdup failed for certificate in netw_set_crypto");
1421 netw->key = tmp_key;
1425 netw->certificate = tmp_cert;
1427 if (
key && certificate) {
1429#if OPENSSL_VERSION_NUMBER >= 0x10100000L
1430 netw->method = TLS_method();
1432 netw->method = TLSv1_2_method();
1434 netw->ctx = SSL_CTX_new(
netw->method);
1437 if (
netw->ctx == NULL) {
1446 SSL_CTX_set_mode(
netw->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
1448#if OPENSSL_VERSION_NUMBER >= 0x10101000L
1463 SSL_CTX_set_num_tickets(
netw->ctx, 0);
1467 if (SSL_CTX_load_verify_locations(
netw->ctx, NULL,
"/etc/ssl/certs/") != 1) {
1473 if (SSL_CTX_use_certificate_file(
netw->ctx, certificate, SSL_FILETYPE_PEM) <= 0) {
1478 if (SSL_CTX_use_PrivateKey_file(
netw->ctx,
key, SSL_FILETYPE_PEM) <= 0) {
1508#if OPENSSL_VERSION_NUMBER >= 0x10100000L
1509 netw->method = TLS_method();
1511 netw->method = TLSv1_2_method();
1513 netw->ctx = SSL_CTX_new(
netw->method);
1514 if (
netw->ctx == NULL) {
1523 SSL_CTX_set_mode(
netw->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
1525#if OPENSSL_VERSION_NUMBER >= 0x10101000L
1531 SSL_CTX_set_num_tickets(
netw->ctx, 0);
1535 BIO* cert_bio = BIO_new_mem_buf(cert_pem, -1);
1537 n_log(
LOG_ERR,
"Failed to create BIO for certificate PEM");
1541 X509*
cert = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL);
1549 if (SSL_CTX_use_certificate(
netw->ctx,
cert) <= 0) {
1558 BIO* key_bio = BIO_new_mem_buf(key_pem, -1);
1564 EVP_PKEY* pkey = PEM_read_bio_PrivateKey(key_bio, NULL, NULL, NULL);
1572 if (SSL_CTX_use_PrivateKey(
netw->ctx, pkey) <= 0) {
1573 EVP_PKEY_free(pkey);
1578 EVP_PKEY_free(pkey);
1581 if (!SSL_CTX_check_private_key(
netw->ctx)) {
1582 n_log(
LOG_ERR,
"Private key does not match the certificate");
1615 if (SSL_CTX_use_certificate_chain_file(
netw->ctx, certificate) <= 0) {
1616 n_log(
LOG_ERR,
"Failed to load certificate chain from %s", certificate);
1622 if (SSL_CTX_load_verify_locations(
netw->ctx,
ca_file, NULL) != 1) {
1650 BIO* ca_bio = BIO_new_mem_buf(ca_pem, -1);
1656 X509_STORE* store = SSL_CTX_get_cert_store(
netw->ctx);
1659 n_log(
LOG_ERR,
"Failed to get certificate store from SSL context");
1663 X509* ca_cert = NULL;
1665 while ((ca_cert = PEM_read_bio_X509(ca_bio, NULL, NULL, NULL)) != NULL) {
1666 if (X509_STORE_add_cert(store, ca_cert) != 1) {
1667 n_log(
LOG_ERR,
"Failed to add CA certificate to store");
1677 if (ca_loaded == 0) {
1678 n_log(
LOG_ERR,
"No CA certificates were loaded from PEM string");
1683 BIO* chain_bio = BIO_new_mem_buf(cert_pem, -1);
1686 X509* skip_cert = PEM_read_bio_X509(chain_bio, NULL, NULL, NULL);
1688 X509_free(skip_cert);
1691 X509* chain_cert = NULL;
1692 while ((chain_cert = PEM_read_bio_X509(chain_bio, NULL, NULL, NULL)) != NULL) {
1693 if (SSL_CTX_add_extra_chain_cert(
netw->ctx, chain_cert) != 1) {
1695 X509_free(chain_cert);
1699 BIO_free(chain_bio);
1708static int _ssl_use_pem(SSL* ssl,
const char* key_pem,
const char* cert_pem) {
1709 BIO* cbio = BIO_new_mem_buf(cert_pem, -1);
1714 cert = PEM_read_bio_X509(cbio, NULL, NULL, NULL);
1718 if (SSL_use_certificate(ssl,
cert) == 1) {
1719 BIO* kbio = BIO_new_mem_buf(key_pem, -1);
1721 EVP_PKEY* pkey = PEM_read_bio_PrivateKey(kbio, NULL, NULL, NULL);
1724 if (SSL_use_PrivateKey(ssl, pkey) == 1 && SSL_check_private_key(ssl) == 1)
1726 EVP_PKEY_free(pkey);
1743 return SSL_get_servername(
netw->ssl, TLSEXT_NAMETYPE_host_name);
1779static int _netw_alpn_select_cb(SSL* ssl,
const unsigned char** out,
unsigned char* outlen,
const unsigned char* in,
unsigned int inlen,
void* arg) {
1784 return SSL_TLSEXT_ERR_NOACK;
1786 size_t want = a->
alpn[i] ? strlen(a->
alpn[i]) : 0;
1787 unsigned int off = 0;
1788 if (want == 0 || want > 255)
1790 while (off < inlen) {
1791 unsigned int l = in[off];
1792 if (l == 0 || off + 1 + l > inlen)
1794 if (l == want && memcmp(in + off + 1, a->
alpn[i], want) == 0) {
1795 *out = in + off + 1;
1796 *outlen = (
unsigned char)l;
1797 return SSL_TLSEXT_ERR_OK;
1802 return SSL_TLSEXT_ERR_NOACK;
1807static size_t _netw_alpn_encode(
const char*
const* protos,
size_t count,
unsigned char* out,
size_t out_cap) {
1809 if (!protos || count == 0 || !out)
1811 for (i = 0; i < count; i++) {
1812 size_t l = protos[i] ? strlen(protos[i]) : 0;
1813 if (l == 0 || l > 255 || off + 1 + l > out_cap)
1815 out[off++] = (
unsigned char)l;
1816 memcpy(out + off, protos[i], l);
1835 unsigned char wire[512];
1845 if (SSL_CTX_set_alpn_protos(
netw->ctx, wire, (
unsigned int)len) != 0) {
1846 n_log(
LOG_ERR,
"OpenSSL refused the ALPN protocol list");
1865 const unsigned char* proto = NULL;
1866 unsigned int plen = 0;
1867 if (out && out_len > 0)
1871 if (!
netw->ssl || out_len < 2)
1873 SSL_get0_alpn_selected(
netw->ssl, &proto, &plen);
1874 if (!proto || plen == 0 || plen >= out_len)
1876 memcpy(out, proto, plen);
1887 N_STR* cert_pem = NULL;
1888 N_STR* key_pem = NULL;
1892 return SSL_TLSEXT_ERR_OK;
1893 sni = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
1894 if (a->
pick(sni, &cert_pem, &key_pem, a->
user_data) != 0 || !cert_pem || !key_pem) {
1899 return SSL_TLSEXT_ERR_ALERT_FATAL;
1904 return ok ? SSL_TLSEXT_ERR_OK : SSL_TLSEXT_ERR_ALERT_FATAL;
1953#if OPENSSL_VERSION_NUMBER >= 0x10100000L
1954 netw->method = TLS_method();
1956 netw->method = TLSv1_2_method();
1958 netw->ctx = SSL_CTX_new(
netw->method);
1963 SSL_CTX_set_mode(
netw->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
1972 SSL_CTX_set_tlsext_servername_arg(
netw->ctx, &arg);
1981 SSL_set_fd(
netw->ssl, (
int)
netw->link.sock);
1988 if (SSL_accept(
netw->ssl) <= 0) {
2033 n_log(
LOG_ERR,
"At least one of ca_file or ca_path must be specified");
2037 if (SSL_CTX_load_verify_locations(
netw->ctx,
ca_file, ca_path) != 1) {
2058 SSL_CTX_set_verify(
netw->ctx, SSL_VERIFY_PEER, NULL);
2060 SSL_CTX_set_verify(
netw->ctx, SSL_VERIFY_NONE, NULL);
2089#if N_OSSL_HAVE_PROTO_VERSION_SETTERS
2090 if (min_version != 0 && SSL_CTX_set_min_proto_version(
netw->ctx, min_version) != 1) {
2091 n_log(
LOG_ERR,
"SSL_CTX_set_min_proto_version(%d) failed", min_version);
2094 if (max_version != 0 && SSL_CTX_set_max_proto_version(
netw->ctx, max_version) != 1) {
2095 n_log(
LOG_ERR,
"SSL_CTX_set_max_proto_version(%d) failed", max_version);
2107 if (min_version != 0 && min_version > TLS1_2_VERSION) {
2108 n_log(
LOG_ERR,
"netw_ssl_set_versions: minimum %d is above TLS 1.2, the highest this OpenSSL supports", min_version);
2111 if (max_version != 0 && max_version < TLS1_2_VERSION) {
2112 n_log(
LOG_ERR,
"netw_ssl_set_versions: maximum %d is below TLS 1.2, the only version this OpenSSL context speaks", max_version);
2139 if (cipher_list && cipher_list[0] && SSL_CTX_set_cipher_list(
netw->ctx, cipher_list) != 1) {
2140 n_log(
LOG_ERR,
"SSL_CTX_set_cipher_list(%s) failed: no cipher matched", cipher_list);
2144 if (ciphersuites && ciphersuites[0] && SSL_CTX_set_ciphersuites(
netw->ctx, ciphersuites) != 1) {
2145 n_log(
LOG_ERR,
"SSL_CTX_set_ciphersuites(%s) failed", ciphersuites);
2156 if (ciphersuites && ciphersuites[0]) {
2157 n_log(
LOG_DEBUG,
"netw_ssl_set_ciphers: ignoring the TLS 1.3 ciphersuites '%s', this OpenSSL cannot negotiate TLS 1.3", ciphersuites);
2181 STACK_OF(X509)* chain = NULL;
2182 X509_STORE* store = NULL;
2183 X509_STORE_CTX* store_ctx = NULL;
2188 if (errbuf && errsz > 0)
2195 if (errbuf && errsz > 0)
2196 snprintf(errbuf, errsz,
"no peer certificate");
2199 chain = SSL_get_peer_cert_chain(
netw->ssl);
2201 store = X509_STORE_new();
2202 store_ctx = X509_STORE_CTX_new();
2203 if (!store || !store_ctx) {
2204 if (errbuf && errsz > 0)
2205 snprintf(errbuf, errsz,
"out of memory");
2208 X509_STORE_set_default_paths(store);
2209 if (X509_STORE_CTX_init(store_ctx, store,
cert, chain) != 1) {
2210 if (errbuf && errsz > 0)
2211 snprintf(errbuf, errsz,
"verify init failed");
2214 if (X509_verify_cert(store_ctx) == 1) {
2217 int verr = X509_STORE_CTX_get_error(store_ctx);
2218 if (errbuf && errsz > 0)
2219 snprintf(errbuf, errsz,
"%s", X509_verify_cert_error_string(verr));
2221 if (expected_host && expected_host[0]) {
2222 if (X509_check_host(
cert, expected_host, 0, 0, NULL) != 1) {
2224 if (errbuf && errsz > 0 && chain_ok)
2225 snprintf(errbuf, errsz,
"hostname mismatch for %s", expected_host);
2228 result = (chain_ok && host_ok) ? TRUE : FALSE;
2232 X509_STORE_CTX_free(store_ctx);
2234 X509_STORE_free(store);
2251 if (SSL_CTX_use_certificate_file(
netw->ctx, cert_file, SSL_FILETYPE_PEM) != 1) {
2252 n_log(
LOG_ERR,
"Failed to load client certificate from %s", cert_file);
2258 const char* kf = key_file ? key_file : cert_file;
2259 if (SSL_CTX_use_PrivateKey_file(
netw->ctx, kf, SSL_FILETYPE_PEM) != 1) {
2260 n_log(
LOG_ERR,
"Failed to load client private key from %s", kf);
2266 if (SSL_CTX_check_private_key(
netw->ctx) != 1) {
2267 n_log(
LOG_ERR,
"Client certificate and private key do not match");
2280#define NETW_CONNECT_ABORT_POLL_MS 100
2303 if (connect_timeout_ms <= 0) {
2304 return connect(sock, rp->ai_addr, (socklen_t)rp->ai_addrlen);
2309 return connect(sock, rp->ai_addr, (socklen_t)rp->ai_addrlen);
2312 int rc = connect(sock, rp->ai_addr, (socklen_t)rp->ai_addrlen);
2318 int in_progress = 0;
2320 if (
neterrno == WSAEWOULDBLOCK ||
neterrno == WSAEINPROGRESS) in_progress = 1;
2322 if (
neterrno == EINPROGRESS) in_progress = 1;
2340 time_t remaining_us = (time_t)connect_timeout_ms * 1000;
2356 FD_SET(sock, &wset);
2358 FD_SET(sock, &eset);
2359 time_t wait_us = remaining_us;
2360 if (poll_us > 0 && wait_us > poll_us)
2363 tv.tv_sec = (long)(wait_us / 1000000);
2364 tv.tv_usec = (long)(wait_us % 1000000);
2366 int sel = select((
int)sock + 1, NULL, &wset, &eset, &tv);
2377 if (remaining_us <= 0)
2383 int interrupted = (
neterrno == WSAEINTR);
2385 int interrupted = (
neterrno == EINTR);
2393 if (remaining_us <= 0) {
2398 if (aborted || !connected) {
2404 socklen_t slen =
sizeof(so_error);
2405 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (
char*)&so_error, &slen) == -1 || so_error != 0) {
2451 (void)ssl_cert_file;
2452 int error = 0, net_status = 0;
2453 char* errmsg = NULL;
2457 n_log(
LOG_ERR,
"Unable to allocate (*netw), already existing. You must use empty NETWORK *structs.");
2462 (*netw) =
netw_new(send_list_limit, recv_list_limit);
2474 (*netw)->link.hints.ai_family = AF_INET;
2476 (*netw)->link.hints.ai_family = AF_INET6;
2479 (*netw)->link.hints.ai_family = AF_UNSPEC;
2482 (*netw)->link.hints.ai_socktype = SOCK_STREAM;
2483 (*netw)->link.hints.ai_protocol = IPPROTO_TCP;
2484 (*netw)->link.hints.ai_flags = AI_PASSIVE;
2485 (*netw)->link.hints.ai_canonname = NULL;
2486 (*netw)->link.hints.ai_next = NULL;
2498 error = getaddrinfo(host,
port, &(*netw)->link.hints, &(*netw)->link.rhost);
2501 n_log(
LOG_ERR,
"Error when resolving %s:%s getaddrinfo: %s", host,
port, gai_strerror(error));
2506 (*netw)->addr_infos_loaded = 1;
2507 (*netw)->connect_dns_usec = (
long long)
get_usec(&connect_timer);
2508 Malloc((*netw)->link.ip,
char, 64);
2512 struct addrinfo* rp = NULL;
2513 for (rp = (*netw)->link.rhost; rp != NULL; rp = rp->ai_next) {
2514 SOCKET sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
2515 if (sock == INVALID_SOCKET) {
2523 (*netw)->link.sock = sock;
2526 if (net_status == -1) {
2532 (*netw)->link.sock = INVALID_SOCKET;
2536 if (!inet_ntop(rp->ai_family,
get_in_addr(rp->ai_addr), (*netw)->link.ip, 64)) {
2548 n_log(
LOG_ERR,
"Couldn't connect to %s:%s : no address succeeded", host,
port);
2553 (*netw)->connect_tcp_usec = (
long long)
get_usec(&connect_timer);
2555 (*netw)->link.port = strdup(
port);
2558 if (ssl_key_file && ssl_cert_file) {
2568 (*netw)->ssl = SSL_new((*netw)->ctx);
2569 SSL_set_fd((*netw)->ssl, (
int)(*netw)->link.sock);
2572 if (SSL_connect((*netw)->ssl) <= 0) {
2579 n_log(
LOG_DEBUG,
"SSL-Connected to %s:%s", (*netw)->link.ip, (*netw)->link.port);
2581 _netw_capture_error(*
netw,
"%s:%s trying to configure SSL but application was compiled without SSL support !", (*netw)->link.ip, (*netw)->link.port);
2582 n_log(
LOG_ERR,
"%s:%s trying to configure SSL but application was compiled without SSL support !", (*netw)->link.ip, (*netw)->link.port);
2585 n_log(
LOG_DEBUG,
"Connected to %s:%s", (*netw)->link.ip, (*netw)->link.port);
2604 while (
netw->link.connect_rp) {
2605 struct addrinfo* rp =
netw->link.connect_rp;
2606 SOCKET sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
2607 if (sock == INVALID_SOCKET) {
2608 netw->link.connect_rp = rp->ai_next;
2611 netw->link.sock = sock;
2614 netw->link.sock = INVALID_SOCKET;
2615 netw->link.connect_rp = rp->ai_next;
2618 if (connect(sock, rp->ai_addr, (socklen_t)rp->ai_addrlen) == 0)
2628 netw->link.sock = INVALID_SOCKET;
2629 netw->link.connect_rp = rp->ai_next;
2655 n_log(
LOG_ERR,
"netw_connect_nonblock: pass an empty NETWORK* (got a non-NULL one)");
2661 n_log(
LOG_ERR,
"netw_connect_nonblock: unable to load WSA");
2666 (*netw)->link.hints.ai_family = AF_INET;
2668 (*netw)->link.hints.ai_family = AF_INET6;
2670 (*netw)->link.hints.ai_family = AF_UNSPEC;
2671 (*netw)->link.hints.ai_socktype = SOCK_STREAM;
2672 (*netw)->link.hints.ai_protocol = IPPROTO_TCP;
2677 Malloc((*netw)->link.ip,
char, 64);
2680 error = getaddrinfo(host,
port, &(*netw)->link.hints, &(*netw)->link.rhost);
2683 n_log(
LOG_ERR,
"netw_connect_nonblock resolve %s:%s: %s", host,
port, gai_strerror(error));
2687 (*netw)->addr_infos_loaded = 1;
2688 (*netw)->link.connect_rp = (*netw)->link.rhost;
2689 (*netw)->link.port = strdup(
port);
2694 n_log(
LOG_ERR,
"netw_connect_nonblock: could not initiate connect to %s:%s", host,
port);
2715 if (
netw->link.sock == INVALID_SOCKET)
2720 FD_SET(
netw->link.sock, &wset);
2722 FD_SET(
netw->link.sock, &eset);
2726 int sel = select((
int)
netw->link.sock + 1, NULL, &wset, &eset, &tv);
2737 socklen_t slen =
sizeof(so_err);
2738 if (getsockopt(
netw->link.sock, SOL_SOCKET, SO_ERROR, (
char*)&so_err, &slen) != 0)
2743 if (
netw->link.connect_rp &&
2744 !inet_ntop(
netw->link.connect_rp->ai_family,
get_in_addr(
netw->link.connect_rp->ai_addr),
netw->link.ip, 64)) {
2754 n_log(
LOG_INFO,
"netw_connect_check: connect failed on an address: %s",
_str(errmsg));
2757 closesocket(
netw->link.sock);
2758 netw->link.sock = INVALID_SOCKET;
2759 netw->link.connect_rp =
netw->link.connect_rp ?
netw->link.connect_rp->ai_next : NULL;
2872#if OPENSSL_VERSION_NUMBER >= 0x10100000L
2873 netw->method = TLS_client_method();
2875 netw->method = TLSv1_2_client_method();
2877 netw->ctx = SSL_CTX_new(
netw->method);
2887 SSL_CTX_set_mode(
netw->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
2889 SSL_CTX_set_default_verify_paths(
netw->ctx);
2928 if (!host || !host[0])
2930 if (inet_pton(AF_INET, host, &v4) == 1)
2932 if (inet_pton(AF_INET6, host, &v6) == 1)
2958 SSL_set_fd(
netw->ssl, (
int)
netw->link.sock);
2960 SSL_set_tlsext_host_name(
netw->ssl, sni_hostname);
2974 X509_VERIFY_PARAM* vp = SSL_get0_param(
netw->ssl);
2975 if (!vp || X509_VERIFY_PARAM_set1_ip_asc(vp, sni_hostname) != 1)
2976 n_log(
LOG_ERR,
"netw_ssl_do_handshake: could not set the expected peer IP %s", sni_hostname);
2980 n_log(
LOG_ERR,
"netw_ssl_do_handshake: could not set the expected peer name %s", sni_hostname);
2983 if (SSL_connect(
netw->ssl) <= 0) {
2985 unsigned long err = ERR_peek_error();
2988 err ? ERR_reason_error_string(err) :
"unknown error");
3006 int r = SSL_do_handshake(
netw->ssl);
3007 int sys_errno = errno;
3010 int e = SSL_get_error(
netw->ssl, r);
3011 if (e == SSL_ERROR_WANT_READ)
3013 if (e == SSL_ERROR_WANT_WRITE)
3016 unsigned long err = ERR_peek_error();
3017 const char* why = err ? ERR_reason_error_string(err) : NULL;
3020 why ? why :
"unknown error");
3025 n_log(
LOG_ERR,
"socket %d: non-blocking TLS handshake failed (SSL_get_error %d, %s, errno %d)",
3026 netw->link.sock, e, why ? why : (e == SSL_ERROR_ZERO_RETURN ?
"peer closed" : (e == SSL_ERROR_SYSCALL ?
"syscall/EOF" :
"no reason")), sys_errno);
3060 SSL_set_fd(
netw->ssl, (
int)
netw->link.sock);
3061 SSL_set_connect_state(
netw->ssl);
3063 SSL_set_tlsext_host_name(
netw->ssl, sni_hostname);
3097 netw->ssl = SSL_new(ctx);
3103 SSL_set_fd(
netw->ssl, (
int)
netw->link.sock);
3104 SSL_set_accept_state(
netw->ssl);
3131 pthread_mutex_lock(&
netw->eventbolt);
3134 if (thr_engine_status)
3135 (*thr_engine_status) =
netw->threaded_engine_status;
3136 pthread_mutex_unlock(&
netw->eventbolt);
3153 pthread_mutex_lock(&
netw->sendbolt);
3156 pthread_mutex_unlock(&
netw->sendbolt);
3159 pthread_mutex_lock(&
netw->recvbolt);
3162 pthread_mutex_unlock(&
netw->recvbolt);
3165 pthread_mutex_lock(&
netw->sendbolt);
3168 pthread_mutex_unlock(&
netw->sendbolt);
3171 pthread_mutex_lock(&
netw->recvbolt);
3174 pthread_mutex_unlock(&
netw->recvbolt);
3176 pthread_mutex_lock(&
netw->eventbolt);
3201 pthread_mutex_unlock(&
netw->eventbolt);
3215 sem_post(&
netw->send_blocker);
3228#if defined(__linux__)
3229 int outstanding = 0;
3233 for (
int it = 0; it < timeout; it += 100) {
3235 if (ioctl(fd, SIOCOUTQ, &outstanding) == -1) {
3237 n_log(
LOG_ERR,
"ioctl SIOCOUTQ returned -1: %s for socket %d", strerror(error), fd);
3261 int thr_engine_status = 0;
3284#if N_REACTOR_AVAILABLE
3285 if (__atomic_load_n(&(*netw)->reactor_registered, __ATOMIC_ACQUIRE)) {
3290 if ((*netw)->deplete_queues_timeout > 0) {
3292 int max_iterations = (*netw)->deplete_queues_timeout * 10;
3297 pthread_mutex_lock(&(*netw)->eventbolt);
3298 nb_running = (*netw)->nb_running_threads;
3299 pthread_mutex_unlock(&(*netw)->eventbolt);
3302 }
while (nb_running > 0 && it < max_iterations);
3304 if (it >= max_iterations && nb_running > 0) {
3310 if ((*netw)->link.sock != INVALID_SOCKET) {
3311 int remaining =
deplete_send_buffer((
int)(*netw)->link.sock, (*netw)->deplete_socket_timeout);
3313 if (remaining > 0) {
3314 n_log(
LOG_ERR,
"socket " SOCKET_SIZE_FORMAT " (%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);
3331 pthread_mutex_lock(&(*netw)->eventbolt);
3332 nb_running = (*netw)->nb_running_threads;
3333 pthread_mutex_unlock(&(*netw)->eventbolt);
3335 if ((*netw)->link.sock != INVALID_SOCKET) {
3341 if (nb_running == 0) {
3342 int shutdown_res = SSL_shutdown((*netw)->ssl);
3343 if (shutdown_res == 0) {
3347 shutdown_res = SSL_shutdown((*netw)->ssl);
3349 if (shutdown_res < 0) {
3350 int err = SSL_get_error((*netw)->ssl, shutdown_res);
3351 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
3357 n_log(
LOG_DEBUG,
"SSL_shutdown() would block on non-blocking socket %d: %d", (*netw)->link.sock, err);
3359 }
else if (err != SSL_ERROR_SYSCALL && err != SSL_ERROR_SSL) {
3366 n_log(
LOG_WARNING,
"netw %d: forcing bidirectional SSL_shutdown with %d threads still running", (*netw)->link.sock, nb_running);
3367 int shutdown_res = SSL_shutdown((*netw)->ssl);
3368 if (shutdown_res == 0) {
3370 shutdown_res = SSL_shutdown((*netw)->ssl);
3372 if (shutdown_res < 0) {
3373 int err = SSL_get_error((*netw)->ssl, shutdown_res);
3374 n_log(
LOG_ERR,
"netw %d: SSL_shutdown() failed with %d threads still running: %d", (*netw)->link.sock, nb_running, err);
3381 SSL_free((*netw)->ssl);
3384 n_log(
LOG_ERR,
"SSL handle of socket %d was already NULL", (*netw)->link.sock);
3386 n_log(
LOG_DEBUG,
"listening socket %d has no SSL handle (expected)", (*netw)->link.sock);
3393 shutdown((*netw)->link.sock, SHUT_WR);
3395 if ((*netw)->wait_close_timeout > 0) {
3398 char buffer[4096] =
"";
3399 int max_iters = (*netw)->wait_close_timeout * 10;
3400 for (
int it = 0; it < max_iters; it++) {
3404#pragma GCC diagnostic push
3405#pragma GCC diagnostic ignored "-Wsign-conversion"
3406 FD_SET((*netw)->link.sock, &rfds);
3407#pragma GCC diagnostic pop
3409 tv.tv_usec = 100000;
3410 int sel = select((
int)(*netw)->link.sock + 1, &rfds, NULL, NULL, &tv);
3412 ssize_t res = recv((*netw)->link.sock, buffer, 4096, NETFLAGS);
3417 if (error != ENOTCONN && error != EINTR && error != ECONNRESET
3419 && error != WSAENOTCONN && error != WSAECONNRESET && error != WSAESHUTDOWN && error != WSAEWOULDBLOCK && error != WSAEINTR
3430 }
else if (sel < 0) {
3435 && error != WSAEINTR
3449 closesocket((*netw)->link.sock);
3456 SSL_CTX_free((*netw)->ctx);
3467 if ((*netw)->link.rhost) {
3468 freeaddrinfo((*netw)->link.rhost);
3474 pthread_mutex_destroy(&(*netw)->recvbolt);
3475 pthread_mutex_destroy(&(*netw)->sendbolt);
3476 pthread_mutex_destroy(&(*netw)->eventbolt);
3477 sem_destroy(&(*netw)->send_blocker);
3497 char* errmsg = NULL;
3500 n_log(
LOG_ERR,
"Cannot use an allocated network. Please pass a NULL network to modify");
3512 (*netw)->link.port = strdup(
port);
3515 (*netw)->link.hints.ai_family = AF_INET;
3517 (*netw)->link.hints.ai_family = AF_INET6;
3520 (*netw)->link.hints.ai_family = AF_UNSPEC;
3523 (*netw)->link.hints.ai_flags = AI_PASSIVE;
3525 (*netw)->link.hints.ai_socktype = SOCK_STREAM;
3526 (*netw)->link.hints.ai_protocol = IPPROTO_TCP;
3527 (*netw)->link.hints.ai_canonname = NULL;
3528 (*netw)->link.hints.ai_next = NULL;
3530 error = getaddrinfo(
addr,
port, &(*netw)->link.hints, &(*netw)->link.rhost);
3537 (*netw)->addr_infos_loaded = 1;
3543 struct addrinfo* rp = NULL;
3544 for (rp = (*netw)->link.rhost; rp != NULL; rp = rp->ai_next) {
3545 (*netw)->link.sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3546 if ((*netw)->link.sock == INVALID_SOCKET) {
3568 if (bind((*netw)->link.sock, rp->ai_addr, (socklen_t)rp->ai_addrlen) == 0) {
3576 if (!inet_ntop(rp->ai_family,
get_in_addr(rp->ai_addr), ip, 64)) {
3579 n_log(
LOG_ERR,
"inet_ntop: %p , %s", (
void*)&(*netw)->link.raddr,
_str(errmsg));
3583 (*netw)->link.ip = ip;
3591 closesocket((*netw)->link.sock);
3602 (*netw)->nb_pending = nbpending;
3603 if (listen((*netw)->link.sock, (*netw)->nb_pending) != 0) {
3630 char* errmsg = NULL;
3633 n_log(
LOG_ERR,
"Cannot use an allocated network. Please pass a NULL network to modify");
3645 (*netw)->link.port = strdup(
port);
3650 (*netw)->link.hints.ai_family = AF_INET;
3652 (*netw)->link.hints.ai_family = AF_INET6;
3654 (*netw)->link.hints.ai_family = AF_UNSPEC;
3657 (*netw)->link.hints.ai_flags = AI_PASSIVE;
3659 (*netw)->link.hints.ai_socktype = SOCK_DGRAM;
3660 (*netw)->link.hints.ai_protocol = IPPROTO_UDP;
3661 (*netw)->link.hints.ai_canonname = NULL;
3662 (*netw)->link.hints.ai_next = NULL;
3664 error = getaddrinfo(
addr,
port, &(*netw)->link.hints, &(*netw)->link.rhost);
3671 (*netw)->addr_infos_loaded = 1;
3673 struct addrinfo* rp = NULL;
3674 for (rp = (*netw)->link.rhost; rp != NULL; rp = rp->ai_next) {
3675 (*netw)->link.sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3676 if ((*netw)->link.sock == INVALID_SOCKET) {
3679 n_log(
LOG_ERR,
"Error while trying to make a UDP socket: %s",
_str(errmsg));
3697 if (bind((*netw)->link.sock, rp->ai_addr, (socklen_t)rp->ai_addrlen) == 0) {
3705 if (!inet_ntop(rp->ai_family,
get_in_addr(rp->ai_addr), ip, 64)) {
3708 n_log(
LOG_ERR,
"inet_ntop: %p , %s", (
void*)&(*netw)->link.raddr,
_str(errmsg));
3711 (*netw)->link.ip = ip;
3717 n_log(
LOG_ERR,
"Error from bind() on UDP port %s neterrno: %s",
port, errmsg);
3719 closesocket((*netw)->link.sock);
3749 char* errmsg = NULL;
3752 n_log(
LOG_ERR,
"Unable to allocate (*netw), already existing. You must use empty NETWORK *structs.");
3768 (*netw)->link.hints.ai_family = AF_INET;
3770 (*netw)->link.hints.ai_family = AF_INET6;
3772 (*netw)->link.hints.ai_family = AF_UNSPEC;
3775 (*netw)->link.hints.ai_socktype = SOCK_DGRAM;
3776 (*netw)->link.hints.ai_protocol = IPPROTO_UDP;
3777 (*netw)->link.hints.ai_flags = AI_PASSIVE;
3778 (*netw)->link.hints.ai_canonname = NULL;
3779 (*netw)->link.hints.ai_next = NULL;
3781 error = getaddrinfo(host,
port, &(*netw)->link.hints, &(*netw)->link.rhost);
3784 n_log(
LOG_ERR,
"Error when resolving %s:%s getaddrinfo: %s", host,
port, gai_strerror(error));
3788 (*netw)->addr_infos_loaded = 1;
3789 Malloc((*netw)->link.ip,
char, 64);
3792 struct addrinfo* rp = NULL;
3793 for (rp = (*netw)->link.rhost; rp != NULL; rp = rp->ai_next) {
3794 SOCKET sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3795 if (sock == INVALID_SOCKET) {
3798 n_log(
LOG_ERR,
"Error while trying to make a UDP socket: %s",
_str(errmsg));
3803 (*netw)->link.sock = sock;
3806 if (connect(sock, rp->ai_addr, (socklen_t)rp->ai_addrlen) == -1) {
3812 (*netw)->link.sock = INVALID_SOCKET;
3815 if (!inet_ntop(rp->ai_family,
get_in_addr(rp->ai_addr), (*netw)->link.ip, 64)) {
3826 n_log(
LOG_ERR,
"Couldn't connect UDP to %s:%s : no address succeeded", host,
port);
3831 (*netw)->link.port = strdup(
port);
3840 n_log(
LOG_DEBUG,
"UDP-Connected to %s:%s", (*netw)->link.ip, (*netw)->link.port);
3858 char* errmsg = NULL;
3866 ssize_t bs = send(s, buf, NETW_BUFLEN_CAST(n), NETFLAGS);
3869 if (error == ECONNRESET || error == ENOTCONN
3871 || error == WSAECONNRESET || error == WSAENOTCONN
3905 ssize_t br = recv(s, buf, NETW_BUFLEN_CAST(n), NETFLAGS);
3940 ssize_t bs = sendto(
netw->link.sock, buf, NETW_BUFLEN_CAST(n), NETFLAGS, dest_addr, dest_len);
3971 ssize_t br = recvfrom(
netw->link.sock, buf, NETW_BUFLEN_CAST(n), NETFLAGS, src_addr, src_len);
3993 SOCKET tmp = INVALID_SOCKET;
3995 char* errmsg = NULL;
3997#if defined(__linux__) || defined(__sun) || defined(_AIX)
3998 socklen_t sin_size = 0;
4015 sin_size =
sizeof(
netw->link.raddr);
4018 int secs = blocking / 1000;
4019 int usecs = (blocking % 1000) * 1000;
4020 struct timeval select_timeout = {secs, usecs};
4023 FD_ZERO(&accept_set);
4024#pragma GCC diagnostic push
4025#pragma GCC diagnostic ignored "-Wsign-conversion"
4026 FD_SET(from->
link.
sock, &accept_set);
4027#pragma GCC diagnostic pop
4029 int ret = select((
int)(from->
link.
sock + 1), &accept_set, NULL, NULL, &select_timeout);
4035 n_log(
LOG_DEBUG,
"error on select with timeout %ds (%d.%ds), neterrno: %s", blocking, secs, usecs,
_str(errmsg));
4039 }
else if (ret == 0) {
4045#pragma GCC diagnostic push
4046#pragma GCC diagnostic ignored "-Wsign-conversion"
4047 if (FD_ISSET(from->
link.
sock, &accept_set)) {
4048#pragma GCC diagnostic pop
4050 tmp = accept(from->
link.
sock, (
struct sockaddr*)&
netw->link.raddr, &sin_size);
4051 if (tmp == INVALID_SOCKET) {
4067 netw->link.is_blocking = 0;
4068 }
else if (blocking == -1) {
4072 tmp = accept(from->
link.
sock, (
struct sockaddr*)&
netw->link.raddr, &sin_size);
4076 if (tmp == INVALID_SOCKET) {
4077 if (error != EINTR && error != EAGAIN
4079 && error != WSAEWOULDBLOCK
4090 netw->link.is_blocking = 0;
4096 tmp = accept(from->
link.
sock, (
struct sockaddr*)&
netw->link.raddr, &sin_size);
4097 if (tmp == INVALID_SOCKET) {
4107 netw->link.is_blocking = 1;
4109 netw->link.sock = tmp;
4116 netw->link.is_blocking = 1;
4120 if (!
netw->link.ip) {
4125 if (!inet_ntop(
netw->link.raddr.ss_family,
get_in_addr(((
struct sockaddr*)&
netw->link.raddr)),
netw->link.ip, 64)) {
4144 netw->ssl = SSL_new(from->
ctx);
4145 SSL_set_fd(
netw->ssl, (
int)
netw->link.sock);
4155 if (SSL_accept(
netw->ssl) <= 0) {
4211 n_log(
LOG_ERR,
"Empty messages are not supported. msg(%p)->length=%zu", msg, msg->
length);
4223 long long bytes_for_counter = (
long long)msg->
written;
4225 pthread_mutex_lock(&
netw->sendbolt);
4228 pthread_mutex_unlock(&
netw->sendbolt);
4232 pthread_mutex_unlock(&
netw->sendbolt);
4248#if N_REACTOR_AVAILABLE
4253 sem_post(&
netw->send_blocker);
4256 sem_post(&
netw->send_blocker);
4284 pthread_mutex_lock(&
netw->sendbolt);
4286 pthread_mutex_unlock(&
netw->sendbolt);
4289 pthread_mutex_unlock(&
netw->sendbolt);
4291 sem_post(&
netw->send_blocker);
4306 pthread_mutex_lock(&
netw->recvbolt);
4310 pthread_mutex_unlock(&
netw->recvbolt);
4325 unsigned int secs = 0;
4326 unsigned int usecs = 0;
4331 if (refresh > 999999) {
4332 secs = refresh / 1000000;
4333 usecs = refresh % 1000000;
4348 if (timed >= refresh)
4350 if (timed == 0 || timed < refresh) {
4378 pthread_mutex_lock(&
netw->eventbolt);
4382 pthread_mutex_unlock(&
netw->eventbolt);
4389 pthread_mutex_unlock(&
netw->eventbolt);
4392 netw->nb_running_threads++;
4400#if defined(__ANDROID__)
4403 pthread_cancel(
netw->recv_thr);
4405 pthread_join(
netw->recv_thr, NULL);
4406 netw->nb_running_threads--;
4407 pthread_mutex_unlock(&
netw->eventbolt);
4410 netw->nb_running_threads++;
4414 pthread_mutex_unlock(&
netw->eventbolt);
4427 ssize_t net_status = 0;
4441 sem_wait(&
netw->send_blocker);
4442 int message_sent = 0;
4443 while (message_sent == 0 && !
DONE) {
4453 memcpy(nboct, &nboctet,
sizeof(uint32_t));
4455 net_status =
netw->send_data(
netw, nboct,
sizeof(int32_t));
4463 pthread_mutex_lock(&
netw->sendbolt);
4465 pthread_mutex_unlock(&
netw->sendbolt);
4470 if (ptr->
written <= UINT_MAX - 2 *
sizeof(uint32_t)) {
4479 uint32_t pkt_state = state;
4482 N_STR* zipped = NULL;
4483 uint32_t flag_bit = 0;
4491 if (zipped && zipped->
written > 0 &&
4497 pkt_state |= flag_bit;
4498 }
else if (zipped) {
4517 size_t frame_len = 2 *
sizeof(uint32_t) + ptr->
written;
4519 Malloc(frame,
char, frame_len);
4521 nboctet = htonl(pkt_state);
4522 memcpy(frame, &nboctet,
sizeof(uint32_t));
4523 nboctet = htonl((uint32_t)ptr->
written);
4524 memcpy(frame +
sizeof(uint32_t), &nboctet,
sizeof(uint32_t));
4525 memcpy(frame + 2 *
sizeof(uint32_t), ptr->
data, ptr->
written);
4526 net_status =
netw->send_data(
netw, frame, (uint32_t)frame_len);
4536 n_log(
LOG_ERR,
"could not allocate %zu byte send frame, packet dropped", frame_len);
4538 if (
netw->send_queue_consecutive_wait >= 0) {
4539 u_sleep((
unsigned int)
netw->send_queue_consecutive_wait);
4545 n_log(
LOG_ERR,
"discarded packet of size %zu which is greater than %" PRIu32, ptr->
written, UINT_MAX);
4568 }
else if (
DONE == 2) {
4571 }
else if (
DONE == 3)
4573 else if (
DONE == 4) {
4576 }
else if (
DONE == 5) {
4590 pthread_mutex_lock(&
netw->eventbolt);
4591 netw->nb_running_threads--;
4592 pthread_mutex_unlock(&
netw->eventbolt);
4597#if !defined(__linux__)
4609 ssize_t net_status = 0;
4617 N_STR* recvdmsg = NULL;
4634 net_status =
netw->recv_data(
netw, nboct,
sizeof(uint32_t));
4635 if (net_status < 0) {
4644 memcpy(&nboctet, nboct,
sizeof(uint32_t));
4645 tmpstate = ntohl(nboctet);
4651 uint32_t pkt_state = tmpstate;
4658 net_status =
netw->recv_data(
netw, nboct,
sizeof(uint32_t));
4659 if (net_status < 0) {
4662 memcpy(&nboctet, nboct,
sizeof(uint32_t));
4663 tmpstate = ntohl(nboctet);
4680 if (!recvdmsg->
data) {
4684 recvdmsg->
length = nboctet + 1;
4688 net_status =
netw->recv_data(
netw, recvdmsg->
data, nboctet);
4689 if (net_status < 0) {
4702 if (want_zlib || want_lz4) {
4703 recvdmsg->
data[nboctet] =
'\0';
4704 N_STR* plain = want_lz4
4712 "socket " SOCKET_SIZE_FORMAT " : failed to decompress payload (%" PRIu32
" bytes, codec=%s); dropping",
4713 netw->link.sock, nboctet,
4714 want_lz4 ?
"lz4" :
"zlib");
4720 pthread_mutex_lock(&
netw->recvbolt);
4723 pthread_mutex_unlock(&
netw->recvbolt);
4738 }
else if (
DONE == 2) {
4741 }
else if (
DONE == 3) {
4744 }
else if (
DONE == 4) {
4747 }
else if (
DONE == 5) {
4750 }
else if (
DONE == 6) {
4753 }
else if (
DONE == 8) {
4767 pthread_mutex_lock(&
netw->eventbolt);
4768 netw->nb_running_threads--;
4769 pthread_mutex_unlock(&
netw->eventbolt);
4774#if !defined(__linux__)
4787 int thr_engine_status = 0;
4795 n_log(
LOG_ERR,
"Thread engine status already stopped for network %p",
netw);
4802 for (
int it = 0; it < 10; it++) {
4803 sem_post(&
netw->send_blocker);
4806 pthread_join(
netw->send_thr, NULL);
4808 pthread_join(
netw->recv_thr, NULL);
4831 char* errmsg = NULL;
4839 char* tmp_buf = buf;
4850 }
else if (error == ECONNRESET || error == ENOTCONN || error == EPIPE
4852 || error == WSAECONNRESET || error == WSAENOTCONN || error == WSAESHUTDOWN
4857 }
else if (bs == -1) {
4864 }
else if (bs == -2) {
4869 }
else if (bs == 0) {
4892 char* errmsg = NULL;
4894#if defined(NETWORK_DISABLE_ZERO_LENGTH_RECV) && (NETWORK_DISABLE_ZERO_LENGTH_RECV == TRUE)
4902 char* tmp_buf = buf;
4913 }
else if (br == 0) {
4917 }
else if (error == ECONNRESET || error == ENOTCONN
4919 || error == WSAECONNRESET || error == WSAENOTCONN || error == WSAESHUTDOWN
4925 }
else if (br == -1) {
4932 }
else if (br == -2) {
4963 ssize_t bs = send(s, buf, NETW_BUFLEN_CAST(n), NETFLAGS);
4965 if (bs > 0)
return bs;
4973 || error == WSAEINTR
4977 if (error == EAGAIN || error == EWOULDBLOCK
4979 || error == WSAEWOULDBLOCK
4983 if (error == ECONNRESET || error == ENOTCONN || error == EPIPE
4985 || error == WSAECONNRESET || error == WSAENOTCONN || error == WSAESHUTDOWN
5019 ssize_t br = recv(s, buf, NETW_BUFLEN_CAST(n), NETFLAGS);
5021 if (br > 0)
return br;
5029 || error == WSAEINTR
5033 if (error == EAGAIN || error == EWOULDBLOCK
5035 || error == WSAEWOULDBLOCK
5039 if (error == ECONNRESET || error == ENOTCONN
5041 || error == WSAECONNRESET || error == WSAENOTCONN
5074 char* errmsg = NULL;
5088#if OPENSSL_VERSION_NUMBER < 0x1010107fL
5089 int status = SSL_write(ssl, buf, (
int)(n - bcount));
5090 bs = (status > 0) ? (
size_t)status : 0;
5092 int status = SSL_write_ex(ssl, buf, (
size_t)(n - bcount), &bs);
5096 bcount += (ssize_t)bs;
5099 int ssl_error = SSL_get_error(ssl, status);
5100 if (ssl_error == SSL_ERROR_WANT_READ || ssl_error == SSL_ERROR_WANT_WRITE) {
5119 if (ssl_error == SSL_ERROR_ZERO_RETURN ||
5120 (ssl_error == SSL_ERROR_SYSCALL &&
5121 (error == 0 || error == EPIPE || error == ECONNRESET || error == ENOTCONN
5123 || error == WSAECONNRESET || error == WSAENOTCONN || error == WSAESHUTDOWN
5126 n_log(
LOG_DEBUG,
"socket %d disconnected during SSL_write !", s);
5130 switch (ssl_error) {
5131 case SSL_ERROR_SYSCALL:
5139 n_log(
LOG_ERR,
"socket %d SSL_write returned %zu, error: %s", s, bs, ERR_reason_error_string(ERR_get_error()));
5144 n_log(
LOG_ERR,
"socket %d SSL_write returned %zu, errno: %s", s, bs,
_str(errmsg));
5171 char* errmsg = NULL;
5179 while (bcount < n) {
5183#if OPENSSL_VERSION_NUMBER < 0x10101000L
5184 int status = SSL_read(ssl, buf, (
int)(n - bcount));
5185 br = (status > 0) ? (
size_t)status : 0;
5187 int status = SSL_read_ex(ssl, buf, (
size_t)(n - bcount), &br);
5191 bcount += (ssize_t)br;
5194 int ssl_error = SSL_get_error(ssl, status);
5195 if (ssl_error == SSL_ERROR_WANT_READ || ssl_error == SSL_ERROR_WANT_WRITE) {
5211 if (ssl_error == SSL_ERROR_ZERO_RETURN) {
5213 n_log(
LOG_DEBUG,
"socket %d : TLS session closed by peer (close_notify)", s);
5216 if (ssl_error == SSL_ERROR_SYSCALL && (error == 0 || error == ECONNRESET || error == ENOTCONN
5218 || error == WSAECONNRESET || error == WSAENOTCONN || error == WSAESHUTDOWN
5222 n_log(
LOG_DEBUG,
"socket %d : peer closed connection (TLS, no close_notify)", s);
5229 unsigned long ssl_reason = ERR_get_error();
5231#ifdef SSL_R_UNEXPECTED_EOF_WHILE_READING
5232 if (ssl_error == SSL_ERROR_SSL && ERR_GET_REASON(ssl_reason) == SSL_R_UNEXPECTED_EOF_WHILE_READING) {
5236 n_log(
LOG_DEBUG,
"socket %d : peer closed connection (TLS unexpected EOF)", s);
5241 switch (ssl_error) {
5242 case SSL_ERROR_SYSCALL:
5250 n_log(
LOG_ERR,
"socket %d SSL_read protocol error: %s", s,
_str((
char*)ERR_reason_error_string(ssl_reason)));
5255 n_log(
LOG_ERR,
"socket %d SSL_read returned %zu, errno: %s", s, br,
_str(errmsg));
5302#if OPENSSL_VERSION_NUMBER < 0x1010107fL
5303 int status = SSL_write(ssl, buf, (
int)n);
5304 bs = (status > 0) ? (
size_t)status : 0;
5306 int status = SSL_write_ex(ssl, buf, (
size_t)n, &bs);
5309 if (status > 0)
return (ssize_t)bs;
5311 int ssl_error = SSL_get_error(ssl, status);
5312 switch (ssl_error) {
5313 case SSL_ERROR_WANT_READ:
5315 case SSL_ERROR_WANT_WRITE:
5317 case SSL_ERROR_ZERO_RETURN:
5320 case SSL_ERROR_SYSCALL:
5321 if (error == 0 || error == ECONNRESET || error == EPIPE || error == ENOTCONN
5323 || error == WSAECONNRESET || error == WSAENOTCONN
5326 n_log(
LOG_DEBUG,
"socket %d SSL_write syscall: connection closed by peer", s);
5329 if (error == EINTR || error == EAGAIN || error == EWOULDBLOCK
5331 || error == WSAEINTR || error == WSAEWOULDBLOCK
5338 n_log(
LOG_ERR,
"socket %d SSL_write_once syscall error: %s", s,
_str(errmsg));
5346 unsigned long ssl_reason = ERR_get_error();
5349 n_log(
LOG_ERR,
"socket %d SSL_write_once error: %s", s,
_str((
char*)ERR_reason_error_string(ssl_reason)));
5383#if OPENSSL_VERSION_NUMBER < 0x10101000L
5384 int status = SSL_read(ssl, buf, (
int)n);
5385 br = (status > 0) ? (
size_t)status : 0;
5387 int status = SSL_read_ex(ssl, buf, (
size_t)n, &br);
5390 if (status > 0)
return (ssize_t)br;
5392 int ssl_error = SSL_get_error(ssl, status);
5393 switch (ssl_error) {
5394 case SSL_ERROR_WANT_READ:
5396 case SSL_ERROR_WANT_WRITE:
5398 case SSL_ERROR_ZERO_RETURN:
5401 case SSL_ERROR_SYSCALL:
5402 if (error == 0 || error == ECONNRESET || error == ENOTCONN
5404 || error == WSAECONNRESET || error == WSAENOTCONN
5407 n_log(
LOG_DEBUG,
"socket %d SSL_read syscall: connection closed by peer", s);
5410 if (error == EINTR || error == EAGAIN || error == EWOULDBLOCK
5412 || error == WSAEINTR || error == WSAEWOULDBLOCK
5419 n_log(
LOG_ERR,
"socket %d SSL_read_once syscall error: %s", s,
_str(errmsg));
5428 unsigned long ssl_reason = ERR_get_error();
5430#ifdef SSL_R_UNEXPECTED_EOF_WHILE_READING
5431 if (ssl_error == SSL_ERROR_SSL && ERR_GET_REASON(ssl_reason) == SSL_R_UNEXPECTED_EOF_WHILE_READING) {
5432 n_log(
LOG_DEBUG,
"socket %d : peer closed connection (TLS unexpected EOF)", s);
5437 n_log(
LOG_ERR,
"socket %d SSL_read_once error: %s", s,
_str((
char*)ERR_reason_error_string(ssl_reason)));
5457 char* errmsg = NULL;
5473 bs = send(s, ptr, NETW_BUFLEN_CAST(
HEAD_SIZE - bcount), NETFLAGS);
5492 bs = send(s, ptr, NETW_BUFLEN_CAST(
HEAD_CODE - bcount), NETFLAGS);
5509 bs = send(s, buf, NETW_BUFLEN_CAST(n - bcount), NETFLAGS);
5537 long int tmpnb = 0, size = 0;
5541 char* errmsg = NULL;
5551 br = recv(s, ptr, NETW_BUFLEN_CAST(
HEAD_SIZE - bcount), NETFLAGS);
5565 tmpnb = strtol(head, NULL, 10);
5566 if (tmpnb == LONG_MIN || tmpnb == LONG_MAX) {
5578 br = recv(s, ptr, NETW_BUFLEN_CAST(
HEAD_CODE - bcount), NETFLAGS);
5592 tmpnb = strtol(code, NULL, 10);
5593 if (tmpnb <= INT_MIN || tmpnb >= INT_MAX) {
5598 (*_code) = (int)tmpnb;
5605 Malloc((*buf),
char, (
size_t)(size + 1));
5613 while (bcount < size) {
5615 br = recv(s, ptr, NETW_BUFLEN_CAST(size - bcount), NETFLAGS);
5642 pthread_mutex_lock(&
netw->sendbolt);
5643 (*nb_to_send) =
netw->send_buf->nb_items;
5644 pthread_mutex_unlock(&
netw->sendbolt);
5646 pthread_mutex_lock(&
netw->recvbolt);
5647 (*nb_to_read) =
netw->recv_buf->nb_items;
5648 pthread_mutex_unlock(&
netw->recvbolt);
5678 __n_assert(netw_pool && (*netw_pool),
return FALSE);
5681 if ((*netw_pool)->pool)
5683 unlock((*netw_pool)->rwlock);
5731 if ((retval =
list_push(
netw->pools, netw_pool, NULL)) == TRUE) {
5849 N_STR* tmpstr = NULL;
5868 N_STR* tmpstr = NULL;
5892 N_STR* tmpstr = NULL;
5914 N_STR* tmpstr = NULL;
5934 N_STR* tmpstr = NULL;
5952 N_STR* tmpstr = NULL;
5969 size_t encoded_size = 0;
5971 for (
size_t i = 0; i < len; i++) {
5972 unsigned char c = (
unsigned char)str[i];
5973 if (isalnum(c) || c ==
'-' || c ==
'_' || c ==
'.' || c ==
'~') {
5980 return encoded_size;
5992 static const char* hex =
"0123456789ABCDEF";
5994 char* encoded = (
char*)malloc(encoded_size + 1);
6000 char* pbuf = encoded;
6002 for (
size_t i = 0; i < len; i++) {
6003 unsigned char c = (
unsigned char)str[i];
6004 if (isalnum(c) || c ==
'-' || c ==
'_' || c ==
'.' || c ==
'~') {
6008 *pbuf++ = hex[c >> 4];
6009 *pbuf++ = hex[c & 0xF];
6025 const char* space = strchr(request,
' ');
6027 if (space == NULL) {
6033 size_t method_length = (size_t)(space - request);
6036 char* method = (
char*)malloc(method_length + 1);
6038 if (method == NULL) {
6044 strncpy(method, request, method_length);
6045 method[method_length] =
'\0';
6056 NETWORK_HTTP_INFO info = {.content_length = 0, .body = NULL, .type = NULL, .content_type = {0}};
6068 const char* content_type_header = strstr(request,
"Content-Type:");
6069 if (content_type_header) {
6070 const char* start = content_type_header + strlen(
"Content-Type: ");
6071 const char* end = strstr(start,
"\r\n");
6073 size_t length = (size_t)(end - start);
6074 if (length > 255) length = 255;
6084 const char* content_length_header = strstr(request,
"Content-Length:");
6085 if (content_length_header) {
6086 const char* start = content_length_header + strlen(
"Content-Length: ");
6088 unsigned long tmp_cl = strtoul(start, NULL, 10);
6092#if ULONG_MAX > SIZE_MAX
6093 if (error == ERANGE || tmp_cl > SIZE_MAX) {
6095 if (error == ERANGE) {
6097 n_log(
LOG_ERR,
"could not get content_length for request %p, returned %s", request, strerror(error));
6105 const char* body_start = strstr(request,
"\r\n\r\n");
6119 info.
type = request_type;
6142 __n_assert(request && strlen(request) > 0,
return FALSE);
6146 strncpy(url,
"/", size - 1);
6147 url[size - 1] =
'\0';
6150 const char* first_space = strchr(request,
' ');
6156 const char* second_space = strchr(first_space + 1,
' ');
6157 if (!second_space) {
6162 size_t len = (size_t)(second_space - first_space - 1);
6167 strncpy(url, first_space + 1, len);
6180 char* decoded = malloc(strlen(str) + 1);
6186 if (isxdigit((
unsigned char)str[1]) && isxdigit((
unsigned char)str[2])) {
6188 if (sscanf(str + 1,
"%2x", &value) >= 1) {
6192 n_log(
LOG_ERR,
"sscanf could not parse char *str (%p) for a %%2x", str);
6199 }
else if (*str ==
'+') {
6219 char* data = strdup(post_data);
6226 while (pair != NULL) {
6228 char* ampersand_pos = strchr(pair,
'&');
6231 if (ampersand_pos != NULL) {
6232 *ampersand_pos =
'\0';
6236 char* equal_pos = strchr(pair,
'=');
6237 if (equal_pos != NULL) {
6239 const char*
key = pair;
6240 const char* value = equal_pos + 1;
6246 free(decoded_value);
6249 pair = (ampersand_pos != NULL) ? (ampersand_pos + 1) : NULL;
6253 return post_data_table;
6265 char url_copy[1024];
6266 strncpy(url_copy, url,
sizeof(url_copy) - 1);
6267 url_copy[
sizeof(url_copy) - 1] =
'\0';
6270 char* query_start = strchr(url_copy,
'?');
6272 *query_start =
'\0';
6276 const char* ext = strrchr(url_copy,
'.');
6284 if (strcmp(ext,
".html") == 0 || strcmp(ext,
".htm") == 0) {
6286 }
else if (strcmp(ext,
".txt") == 0) {
6287 return "text/plain";
6288 }
else if (strcmp(ext,
".jpg") == 0 || strcmp(ext,
".jpeg") == 0) {
6289 return "image/jpeg";
6290 }
else if (strcmp(ext,
".png") == 0) {
6292 }
else if (strcmp(ext,
".gif") == 0) {
6294 }
else if (strcmp(ext,
".css") == 0) {
6296 }
else if (strcmp(ext,
".js") == 0) {
6297 return "application/javascript";
6298 }
else if (strcmp(ext,
".json") == 0) {
6299 return "application/json";
6300 }
else if (strcmp(ext,
".xml") == 0) {
6301 return "application/xml";
6302 }
else if (strcmp(ext,
".pdf") == 0) {
6303 return "application/pdf";
6304 }
else if (strcmp(ext,
".zip") == 0) {
6305 return "application/zip";
6306 }
else if (strcmp(ext,
".mp4") == 0) {
6308 }
else if (strcmp(ext,
".mp3") == 0) {
6309 return "audio/mpeg";
6310 }
else if (strcmp(ext,
".wav") == 0) {
6312 }
else if (strcmp(ext,
".ogg") == 0) {
6326 switch (status_code) {
6330 return "No Content";
6332 return "Not Modified";
6336 return "Internal Server Error";
6344 if (status_code < 100 || status_code > 599)
6346 return status_code / 100;
6374 size_t alloc, total = 0;
6378 memset(&strm, 0,
sizeof(strm));
6379 if (inflateInit2(&strm, window_bits) != Z_OK)
6384 buf = malloc(alloc);
6389 strm.next_in = (Bytef*)src;
6390 strm.avail_in = (uInt)len;
6392 if (total >= alloc - 1) {
6395 tmp = realloc(buf, alloc);
6403 strm.next_out = (Bytef*)(buf + total);
6404 strm.avail_out = (uInt)(alloc - total - 1);
6405 ret = inflate(&strm, Z_NO_FLUSH);
6406 if (ret == Z_STREAM_ERROR || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR || ret == Z_NEED_DICT) {
6411 total = alloc - 1 - strm.avail_out;
6412 }
while (ret != Z_STREAM_END);
6428 int is_gzip = 0, is_deflate = 0;
6436 for (i = 0; i <
sizeof(enc) - 1 && encoding[i]; i++)
6437 enc[i] = (
char)tolower((
unsigned char)encoding[i]);
6440 if (strstr(enc,
"gzip"))
6442 else if (strstr(enc,
"deflate"))
6446 if ((!is_gzip && !is_deflate) || !body->
data || body->
written == 0) {
6463static long mp_find(
const unsigned char* hay,
size_t hlen,
const char* needle,
size_t nlen) {
6465 if (nlen == 0 || nlen > hlen)
6467 for (i = 0; i + nlen <= hlen; i++) {
6468 if (hay[i] == (
unsigned char)needle[0] && memcmp(hay + i, needle, nlen) == 0)
6477 Malloc(r,
char, len + 1);
6489 if (!content_type || !out || outsz == 0)
6492 for (p = content_type; *p; p++) {
6493 if (strncasecmp(p,
"boundary=", 9) == 0)
6502 while (*p && *p !=
'"' && i + 1 < outsz)
6505 while (*p && *p !=
';' && *p !=
' ' && *p !=
'\r' && *p !=
'\n' && i + 1 < outsz)
6509 return i > 0 ? 0 : -1;
6527 if (parts && *parts)
6533static const char*
mp_header_value(
const char* block,
size_t blen,
const char* name,
size_t* vlen) {
6534 size_t nlen = strlen(name);
6538 size_t line_end = i;
6539 while (line_end < blen && block[line_end] !=
'\n')
6542 if (line_end - i > nlen + 1 && strncasecmp(block + i, name, nlen) == 0 && block[i + nlen] ==
':') {
6543 const char* v = block + i + nlen + 1;
6544 size_t vl = line_end - (i + nlen + 1);
6545 while (vl > 0 && (*v ==
' ' || *v ==
'\t')) {
6549 while (vl > 0 && (v[vl - 1] ==
'\r' || v[vl - 1] ==
' ' || v[vl - 1] ==
'\t'))
6564 snprintf(
key,
sizeof(
key),
"%s=", param);
6566 for (i = 0; i + klen <= dlen; i++) {
6567 if (strncasecmp(disp + i,
key, klen) == 0) {
6568 const char* v = disp + i + klen;
6569 size_t avail = dlen - (i + klen);
6571 if (avail > 0 && *v ==
'"') {
6574 while (j < avail && v[j] !=
'"')
6577 while (j < avail && v[j] !=
';' && v[j] !=
' ')
6588 char crlf_dash[260];
6589 const unsigned char* data;
6590 size_t len, dlen, cdlen, cursor;
6594 if (!body || !body->
data || !boundary || !boundary[0])
6596 if (strlen(boundary) + 2 >=
sizeof(dash))
6598 snprintf(dash,
sizeof(dash),
"--%s", boundary);
6599 snprintf(crlf_dash,
sizeof(crlf_dash),
"\r\n--%s", boundary);
6600 dlen = strlen(dash);
6601 cdlen = strlen(crlf_dash);
6607 data = (
const unsigned char*)body->
data;
6609 first =
mp_find(data, len, dash, dlen);
6612 cursor = (size_t)first + dlen;
6616 size_t part_start, part_end;
6620 if (cursor + 2 <= len && data[cursor] ==
'-' && data[cursor + 1] ==
'-')
6623 if (cursor + 2 <= len && data[cursor] ==
'\r' && data[cursor + 1] ==
'\n')
6625 else if (cursor < len && data[cursor] ==
'\n')
6627 part_start = cursor;
6628 rel =
mp_find(data + part_start, len - part_start, crlf_dash, cdlen);
6631 part_end = part_start + (size_t)rel;
6642 long s =
mp_find(data + part_start, part_end - part_start,
"\r\n\r\n", 4);
6644 size_t hlen = (size_t)s;
6645 size_t bbeg = part_start + (size_t)s + 4;
6646 const char* hblock = (
const char*)(data + part_start);
6647 const char* hv =
mp_header_value(hblock, hlen,
"Content-Disposition", &hvl);
6656 size_t blen = part_end - bbeg;
6660 memcpy(mp->
body->
data, data + bbeg, blen);
6677 cursor = part_end + cdlen;
6683static void mp_put(
char* buf,
size_t* pos,
const void* src,
size_t n) {
6685 memcpy(buf + *pos, src, n);
6690static void mp_puts(
char* buf,
size_t* pos,
const char* s) {
6692 mp_put(buf, pos, s, strlen(s));
6696static void mp_emit(
char* buf,
size_t* pos,
LIST* parts,
const char* dash) {
6702 mp_puts(buf, pos,
"\r\nContent-Disposition: form-data; name=\"");
6706 mp_puts(buf, pos,
"; filename=\"");
6714 mp_puts(buf, pos,
"Content-Type: ");
6732 if (!parts || !boundary || !boundary[0])
6734 if (strlen(boundary) + 2 >=
sizeof(dash))
6736 snprintf(dash,
sizeof(dash),
"--%s", boundary);
6738 mp_emit(NULL, &total, parts, dash);
6739 Malloc(buf,
char, total + 1);
6743 mp_emit(buf, &total, parts, dash);
6758 const time_t now = time(NULL);
6761 if (gmtime_s(&gmt, &now) != 0) {
6766 if (!gmtime_r(&now, &gmt)) {
6771 if (strftime(buffer, buffer_size,
"%a, %d %b %Y %H:%M:%S GMT", &gmt) == 0) {
6791 __n_assert(additional_headers,
return FALSE);
6794 const char* connection_type =
"close";
6797 char date_buffer[128] =
"";
6800 if ((*http_response)) {
6801 (*http_response)->written = 0;
6804 if (!body || body->
written == 0) {
6807 "HTTP/1.1 %d %s\r\n"
6810 "Content-Length: 0\r\n"
6812 "Connection: %s\r\n\r\n",
6813 status_code, status_message, date_buffer, server_name, additional_headers, connection_type);
6818 "HTTP/1.1 %d %s\r\n"
6821 "Content-Type: %s\r\n"
6822 "Content-Length: %zu\r\n"
6824 "Connection: %s\r\n\r\n",
6825 status_code, status_message, date_buffer, server_name, content_type, body->
written, additional_headers, connection_type);
6826 nstrcat((*http_response), body);
6843 int ret = SSL_write(conn->
netw->
ssl, buf, (
int)len);
6844 return (ret > 0) ? (ssize_t)ret : -1;
6846 ssize_t ret = send(conn->
netw->
link.
sock, buf, NETW_BUFLEN_CAST(len), NETFLAGS);
6859 char* p = (
char*)buf;
6860 while (total < len) {
6863 ret = SSL_read(conn->
netw->
ssl, p + total, (
int)(len - total));
6865 ret = recv(conn->
netw->
link.
sock, p + total, NETW_BUFLEN_CAST(len - total), 0);
6870 total += (size_t)ret;
6872 return (ssize_t)total;
6897 conn->
host = strdup(host);
6898 conn->
path = strdup(path);
6901 goto ws_connect_fail;
6907 n_log(
LOG_ERR,
"n_ws_connect: TCP+SSL context setup failed for %s:%s", host,
port);
6908 goto ws_connect_fail;
6912 n_log(
LOG_ERR,
"n_ws_connect: SSL handshake failed for %s:%s", host,
port);
6913 goto ws_connect_fail;
6918 goto ws_connect_fail;
6923 unsigned char rand_bytes[16];
6924 if (RAND_bytes(rand_bytes, 16) != 1) {
6927 goto ws_connect_fail;
6932 memcpy(rand_nstr->
data, rand_bytes, 16);
6937 __n_assert(ws_key_nstr,
goto ws_connect_fail);
6939 while (ws_key_nstr->
written > 0 &&
6940 (ws_key_nstr->
data[ws_key_nstr->
written - 1] ==
'\n' ||
6941 ws_key_nstr->
data[ws_key_nstr->
written - 1] ==
'\r' ||
6942 ws_key_nstr->
data[ws_key_nstr->
written - 1] ==
' ')) {
6947 char upgrade_req[2048];
6948 int req_len = snprintf(upgrade_req,
sizeof(upgrade_req),
6949 "GET %s HTTP/1.1\r\n"
6951 "Upgrade: websocket\r\n"
6952 "Connection: Upgrade\r\n"
6953 "Sec-WebSocket-Key: %s\r\n"
6954 "Sec-WebSocket-Version: 13\r\n"
6956 path, host, ws_key_nstr->
data);
6958 if (req_len < 0 || (
size_t)req_len >=
sizeof(upgrade_req)) {
6960 n_log(
LOG_ERR,
"n_ws_connect: upgrade request too large");
6962 goto ws_connect_fail;
6966 if (
_ws_write(conn, upgrade_req, (
size_t)req_len) < 0) {
6968 n_log(
LOG_ERR,
"n_ws_connect: failed to send upgrade request");
6970 goto ws_connect_fail;
6974 char resp_buf[4096];
6975 memset(resp_buf, 0,
sizeof(resp_buf));
6976 size_t resp_len = 0;
6977 while (resp_len <
sizeof(resp_buf) - 1) {
6980 r = SSL_read(conn->
netw->
ssl, resp_buf + resp_len, 1);
6982 r = recv(conn->
netw->
link.
sock, resp_buf + resp_len, 1, 0);
6986 n_log(
LOG_ERR,
"n_ws_connect: failed reading handshake response");
6988 goto ws_connect_fail;
6990 resp_len += (size_t)r;
6991 if (resp_len >= 4 &&
6992 resp_buf[resp_len - 4] ==
'\r' && resp_buf[resp_len - 3] ==
'\n' &&
6993 resp_buf[resp_len - 2] ==
'\r' && resp_buf[resp_len - 1] ==
'\n') {
6997 resp_buf[resp_len] =
'\0';
7000 if (strstr(resp_buf,
"101") == NULL) {
7002 n_log(
LOG_ERR,
"n_ws_connect: server did not return 101: %.128s", resp_buf);
7004 goto ws_connect_fail;
7008 static const char ws_magic[] =
"258EAFA5-E914-47DA-95CA-5AB5FE44F513";
7009 char concat_key[256];
7010 snprintf(concat_key,
sizeof(concat_key),
"%s%s", ws_key_nstr->
data, ws_magic);
7015 unsigned char sha1_hash[SHA_DIGEST_LENGTH];
7016 SHA1((
const unsigned char*)concat_key, strlen(concat_key), sha1_hash);
7020 memcpy(sha1_nstr->
data, sha1_hash, SHA_DIGEST_LENGTH);
7021 sha1_nstr->
written = SHA_DIGEST_LENGTH;
7025 __n_assert(expected_accept,
goto ws_connect_fail);
7027 while (expected_accept->
written > 0 &&
7028 (expected_accept->
data[expected_accept->
written - 1] ==
'\n' ||
7029 expected_accept->
data[expected_accept->
written - 1] ==
'\r' ||
7030 expected_accept->
data[expected_accept->
written - 1] ==
' ')) {
7031 expected_accept->
data[--expected_accept->
written] =
'\0';
7035 const char* accept_hdr = NULL;
7036 char* search_pos = resp_buf;
7037 while (*search_pos) {
7038 if (strncasecmp(search_pos,
"Sec-WebSocket-Accept:", 21) == 0) {
7039 accept_hdr = search_pos + 21;
7046 n_log(
LOG_ERR,
"n_ws_connect: no Sec-WebSocket-Accept header in response");
7048 goto ws_connect_fail;
7051 while (*accept_hdr ==
' ') accept_hdr++;
7053 char accept_val[128];
7056 while (ai < (
int)
sizeof(accept_val) - 1 && accept_hdr[ai] && accept_hdr[ai] !=
'\r' && accept_hdr[ai] !=
'\n') {
7057 accept_val[ai] = accept_hdr[ai];
7060 accept_val[ai] =
'\0';
7062 if (strcmp(accept_val, expected_accept->
data) != 0) {
7065 n_log(
LOG_DEBUG,
"n_ws_connect: Sec-WebSocket-Accept mismatch (proxy?): got [%s] expected [%s]",
7066 accept_val, expected_accept->
data);
7071 n_log(
LOG_INFO,
"n_ws_connect: WebSocket handshake completed with %s:%s%s", host,
port, path);
7095 size_t frame_max = 14 + len;
7096 unsigned char* frame = NULL;
7097 Malloc(frame,
unsigned char, frame_max);
7103 frame[pos++] = (
unsigned char)(0x80 | (opcode & 0x0F));
7107 frame[pos++] = (
unsigned char)(0x80 | len);
7108 }
else if (len <= 65535) {
7109 frame[pos++] = (
unsigned char)(0x80 | 126);
7110 frame[pos++] = (
unsigned char)((len >> 8) & 0xFF);
7111 frame[pos++] = (
unsigned char)(len & 0xFF);
7113 frame[pos++] = (
unsigned char)(0x80 | 127);
7114 for (
int i = 7; i >= 0; i--) {
7115 frame[pos++] = (
unsigned char)((len >> (8 * i)) & 0xFF);
7120 unsigned char mask_key[4];
7121 if (RAND_bytes(mask_key, 4) != 1) {
7123 n_log(
LOG_ERR,
"n_ws_send: RAND_bytes failed for mask key");
7127 memcpy(frame + pos, mask_key, 4);
7131 if (payload && len > 0) {
7132 for (
size_t i = 0; i < len; i++) {
7133 frame[pos + i] = (
unsigned char)((
unsigned char)payload[i] ^ mask_key[i % 4]);
7138 ssize_t written =
_ws_write(conn, frame, pos);
7140 if (written < 0 || (
size_t)written != pos) {
7142 n_log(
LOG_ERR,
"n_ws_send: write failed (wrote %zd of %zu)", written, pos);
7162 memset(msg_out, 0,
sizeof(*msg_out));
7165 unsigned char hdr[2];
7171 int opcode = hdr[0] & 0x0F;
7172 int mask_bit = (hdr[1] >> 7) & 1;
7173 uint64_t payload_len = hdr[1] & 0x7F;
7176 if (payload_len == 126) {
7177 unsigned char ext[2];
7178 if (
_ws_read(conn, ext, 2) < 0)
return -1;
7179 payload_len = ((uint64_t)ext[0] << 8) | (uint64_t)ext[1];
7180 }
else if (payload_len == 127) {
7181 unsigned char ext[8];
7182 if (
_ws_read(conn, ext, 8) < 0)
return -1;
7184 for (
int i = 0; i < 8; i++) {
7185 payload_len = (payload_len << 8) | (uint64_t)ext[i];
7195 n_log(
LOG_ERR,
"n_ws_recv: payload length %llu exceeds N_WS_MAX_PAYLOAD (%zu)", (
unsigned long long)payload_len, (
size_t)
N_WS_MAX_PAYLOAD);
7200 unsigned char mask_key[4] = {0};
7202 if (
_ws_read(conn, mask_key, 4) < 0)
return -1;
7209 if (payload_len > 0) {
7210 if (
_ws_read(conn, payload->
data, (
size_t)payload_len) < 0) {
7216 for (uint64_t i = 0; i < payload_len; i++) {
7217 payload->
data[i] = (char)((
unsigned char)payload->
data[i] ^ mask_key[i % 4]);
7221 payload->
written = (size_t)payload_len;
7222 payload->
data[payload_len] =
'\0';
7224 msg_out->
opcode = opcode;
7226 msg_out->
masked = mask_bit;
7249 memset(&msg, 0,
sizeof(msg));
7307 __atomic_store_n(&conn->
stop_flag, 1, __ATOMIC_RELEASE);
7337 if (stop_flag && __atomic_load_n(stop_flag, __ATOMIC_ACQUIRE)) {
7342 tv.tv_usec = 500000;
7345 FD_SET(
netw->link.sock, &fds);
7350 if (SSL_pending(
netw->ssl) > 0) {
7355 int sel = select((
int)
netw->link.sock + 1, &fds, NULL, NULL, &tv);
7356 if (sel < 0)
return -1;
7357 if (sel == 0)
continue;
7362 ret = SSL_read(
netw->ssl, ch, 1);
7364 ret = recv(
netw->link.sock, ch, 1, 0);
7366 return (ret == 1) ? 1 : -1;
7378 int ret = SSL_write(
netw->ssl, buf, (
int)len);
7379 return (ret > 0) ? (ssize_t)ret : -1;
7381 ssize_t ret = send(
netw->link.sock, buf, NETW_BUFLEN_CAST(len), NETFLAGS);
7417 n_log(
LOG_ERR,
"n_sse_connect: TCP+SSL context setup failed for %s:%s", host,
port);
7418 goto sse_connect_fail;
7422 n_log(
LOG_ERR,
"n_sse_connect: SSL handshake failed for %s:%s", host,
port);
7423 goto sse_connect_fail;
7428 goto sse_connect_fail;
7435 if (user_agent && user_agent[0]) {
7436 req_len = snprintf(request,
sizeof(request),
7437 "GET %s HTTP/1.1\r\n"
7439 "Accept: text/event-stream\r\n"
7440 "Cache-Control: no-cache\r\n"
7441 "Connection: keep-alive\r\n"
7442 "User-Agent: %s\r\n"
7444 path, host, user_agent);
7446 req_len = snprintf(request,
sizeof(request),
7447 "GET %s HTTP/1.1\r\n"
7449 "Accept: text/event-stream\r\n"
7450 "Cache-Control: no-cache\r\n"
7451 "Connection: keep-alive\r\n"
7456 if (req_len < 0 || (
size_t)req_len >=
sizeof(request)) {
7459 goto sse_connect_fail;
7464 n_log(
LOG_ERR,
"n_sse_connect: failed to send HTTP request");
7465 goto sse_connect_fail;
7469 char resp_buf[8192];
7470 size_t resp_len = 0;
7471 while (resp_len <
sizeof(resp_buf) - 1) {
7475 n_log(
LOG_ERR,
"n_sse_connect: failed reading HTTP response");
7476 goto sse_connect_fail;
7478 resp_buf[resp_len++] = ch;
7479 if (resp_len >= 4 &&
7480 resp_buf[resp_len - 4] ==
'\r' && resp_buf[resp_len - 3] ==
'\n' &&
7481 resp_buf[resp_len - 2] ==
'\r' && resp_buf[resp_len - 1] ==
'\n') {
7485 resp_buf[resp_len] =
'\0';
7488 if (strstr(resp_buf,
"200") == NULL) {
7490 n_log(
LOG_ERR,
"n_sse_connect: server did not return 200: %.128s", resp_buf);
7491 goto sse_connect_fail;
7499 memset(¤t, 0,
sizeof(current));
7503 size_t line_len = 0;
7505 while (!__atomic_load_n(&conn->
stop_flag, __ATOMIC_ACQUIRE)) {
7508 n_log(
LOG_DEBUG,
"n_sse_connect: connection closed or read error");
7514 if (line_len > 0 &&
line[line_len - 1] ==
'\r') {
7517 line[line_len] =
'\0';
7519 if (line_len == 0) {
7524 memset(¤t, 0,
sizeof(current));
7526 }
else if (
line[0] ==
':') {
7531 char* colon = strchr(
line,
':');
7532 const char* field =
line;
7533 const char* value =
"";
7538 if (*value ==
' ') value++;
7541 if (strcmp(field,
"data") == 0) {
7548 }
else if (strcmp(field,
"event") == 0) {
7551 }
else if (strcmp(field,
"id") == 0) {
7554 }
else if (strcmp(field,
"retry") == 0) {
7555 current.
retry = atoi(value);
7561 if (line_len <
sizeof(
line) - 1) {
7562 line[line_len++] = ch;
7582#define N_WS_FRAME_MAX_PAYLOAD (16U * 1024U * 1024U)
7597 if (!buf || !frame || !consumed)
7601 frame->
fin = (buf[0] & 0x80) ? 1 : 0;
7602 frame->
rsv = (buf[0] >> 4) & 0x07;
7603 frame->
opcode = buf[0] & 0x0F;
7604 frame->
masked = (buf[1] & 0x80) ? 1 : 0;
7605 len7 = buf[1] & 0x7F;
7607 plen = (uint64_t)len7;
7608 }
else if (len7 == 126) {
7611 plen = ((uint64_t)buf[2] << 8) | (uint64_t)buf[3];
7618 for (i = 0; i < 8; i++)
7619 plen = (plen << 8) | (uint64_t)buf[2 + i];
7627 memcpy(frame->
mask, buf + hdr, 4);
7630 memset(frame->
mask, 0, 4);
7632 if (len < hdr + plen)
7636 *consumed = hdr + (size_t)plen;
7640void n_ws_unmask(
unsigned char* dst,
const unsigned char* src,
size_t len,
const unsigned char mask[4]) {
7642 if (!dst || !src || !mask)
7644 for (i = 0; i < len; i++)
7645 dst[i] = (
unsigned char)(src[i] ^ mask[i & 3]);
7648size_t n_ws_frame_build(
int fin,
int opcode,
int do_mask,
const unsigned char* payload,
size_t len,
const unsigned char mask_key[4],
unsigned char* out,
size_t out_cap) {
7650 unsigned char b1 = (
unsigned char)(do_mask ? 0x80 : 0);
7651 if (!out || out_cap < 2)
7653 out[pos++] = (
unsigned char)((fin ? 0x80 : 0) | (opcode & 0x0F));
7655 out[pos++] = (
unsigned char)(b1 | (
unsigned char)len);
7656 }
else if (len <= 65535) {
7657 if (out_cap < pos + 3)
7659 out[pos++] = (
unsigned char)(b1 | 126);
7660 out[pos++] = (
unsigned char)((len >> 8) & 0xFF);
7661 out[pos++] = (
unsigned char)(len & 0xFF);
7664 if (out_cap < pos + 9)
7666 out[pos++] = (
unsigned char)(b1 | 127);
7667 for (i = 7; i >= 0; i--)
7668 out[pos++] = (
unsigned char)((len >> (8 * i)) & 0xFF);
7671 if (!mask_key || out_cap < pos + 4)
7673 memcpy(out + pos, mask_key, 4);
7676 if (out_cap < pos + len)
7678 if (do_mask && mask_key) {
7680 for (i = 0; i < len; i++)
7681 out[pos + i] = (
unsigned char)((payload ? payload[i] : 0) ^ mask_key[i & 3]);
7682 }
else if (payload && len > 0) {
7683 memcpy(out + pos, payload, len);
7695 if (!buf || !req)
return;
7698 const char* line_end = strstr(buf,
"\r\n");
7699 if (!line_end) line_end = strchr(buf,
'\n');
7700 if (!line_end)
return;
7702 size_t line_len = (size_t)(line_end - buf);
7704 if (line_len >=
sizeof(
line)) line_len =
sizeof(
line) - 1;
7705 memcpy(
line, buf, line_len);
7706 line[line_len] =
'\0';
7709 char* sp1 = strchr(
line,
' ');
7711 size_t mlen = (size_t)(sp1 -
line);
7712 if (mlen >=
sizeof(req->
method)) mlen =
sizeof(req->
method) - 1;
7714 req->
method[mlen] =
'\0';
7718 char* sp2 = strchr(sp1,
' ');
7719 if (sp2) *sp2 =
'\0';
7720 char* qmark = strchr(sp1,
'?');
7723 strncpy(req->
query, qmark + 1,
sizeof(req->
query) - 1);
7726 strncpy(req->
path, sp1,
sizeof(req->
path) - 1);
7727 req->
path[
sizeof(req->
path) - 1] =
'\0';
7730 const char* hdr_start = line_end;
7731 if (*hdr_start ==
'\r') hdr_start++;
7732 if (*hdr_start ==
'\n') hdr_start++;
7736 const char* body_start = NULL;
7737 while (hdr_start && *hdr_start) {
7738 const char* next = strstr(hdr_start,
"\r\n");
7739 if (!next) next = strchr(hdr_start,
'\n');
7742 size_t hlen = (size_t)(next - hdr_start);
7746 if (*body_start ==
'\r') body_start++;
7747 if (*body_start ==
'\n') body_start++;
7751 char* header_line = strndup(hdr_start, hlen);
7757 if (*hdr_start ==
'\r') hdr_start++;
7758 if (*hdr_start ==
'\n') hdr_start++;
7762 if (body_start && *body_start) {
7795 server->user_data = user_data;
7808 server->listener = listener;
7822 while (!__atomic_load_n(&
server->stop_flag, __ATOMIC_ACQUIRE)) {
7826 FD_SET(
server->listener->link.sock, &readfds);
7829 tv.tv_usec = 200000;
7830 int sel = select((
int)(
server->listener->link.sock + 1), &readfds, NULL, NULL, &tv);
7831 if (sel <= 0)
continue;
7836 if (!client)
continue;
7840 ssize_t n = recv(client->
link.
sock, buf,
sizeof(buf) - 1, 0);
7849 memset(&req, 0,
sizeof(req));
7854 memset(&resp, 0,
sizeof(resp));
7863 if (!status_msg) status_msg =
"Unknown";
7865 size_t body_len = 0;
7866 const char* body_data =
"";
7872 char header_buf[1024];
7873 int hlen = snprintf(header_buf,
sizeof(header_buf),
7874 "HTTP/1.1 %d %s\r\n"
7875 "Content-Type: %s\r\n"
7876 "Content-Length: %zu\r\n"
7877 "Connection: close\r\n"
7884 send(client->
link.
sock, header_buf, NETW_BUFLEN_CAST(hlen), NETFLAGS);
7887 send(client->
link.
sock, body_data, NETW_BUFLEN_CAST(body_len), NETFLAGS);
7905 __atomic_store_n(&
server->stop_flag, 1, __ATOMIC_RELEASE);
7914 if ((*server)->listener) {
7922 if (!u || !qs || !qs[0])
return;
7923 char* buf = strdup(qs);
7925 char* saveptr = NULL;
7929 char* tok = strtok_r(buf,
"&", &saveptr);
7931 char*
eq = strchr(tok,
'=');
7941 tok = strtok_r(NULL,
"&", &saveptr);
7947 if (!url)
return NULL;
7950 if (!u)
return NULL;
7951 memset(u, 0,
sizeof(*u));
7952 const char* p = url;
7953 const char* scheme_end = strstr(p,
"://");
7955 u->
scheme = strndup(p, (
size_t)(scheme_end - p));
7958 u->
scheme = strdup(
"http");
7960 const char* host_end = p;
7961 while (*host_end && *host_end !=
'/' && *host_end !=
'?' && *host_end !=
':') host_end++;
7962 u->
host = strndup(p, (
size_t)(host_end - p));
7967 while (*p && *p !=
'/' && *p !=
'?') p++;
7971 while (*pe && *pe !=
'?') pe++;
7972 u->
path = strndup(p, (
size_t)(pe - p));
7975 u->
path = strdup(
"/");
7979 u->
query = strdup(p);
7986 if (!u)
return NULL;
7988 if (!result)
return NULL;
7991 int dp = (u->
scheme && strcmp(u->
scheme,
"https") == 0) ? 443 : 80;
8006 const char* seg_starts[256];
8007 size_t seg_lens[256];
8009 const char* p = (path && path[0]) ? path :
"/";
8020 slash = strchr(p,
'/');
8021 slen = slash ? (size_t)(slash - p) : strlen(p);
8022 if (slen == 1 && p[0] ==
'.') {
8024 }
else if (slen == 2 && p[0] ==
'.' && p[1] ==
'.') {
8026 }
else if (nb < 256) {
8028 seg_lens[nb] = slen;
8034 if (outsz == 0)
return;
8036 for (i = 0; i < nb && pos < outsz - 1; i++) {
8038 if (i > 0 && pos < outsz - 1) out[pos++] =
'/';
8039 for (k = 0; k < seg_lens[i] && pos < outsz - 1; k++)
8040 out[pos++] = seg_starts[i][k];
8043 if (path && path[0] && path[strlen(path) - 1] ==
'/' && pos < outsz - 1 && (nb > 0))
8058 N_STR* result = NULL;
8063 if (!u)
return NULL;
8064 snprintf(scheme,
sizeof(scheme),
"%s", u->
scheme ? u->
scheme :
"http");
8065 snprintf(host,
sizeof(host),
"%s", u->
host ? u->
host :
"");
8066 for (i = 0; scheme[i]; i++) scheme[i] = (
char)tolower((
unsigned char)scheme[i]);
8067 for (i = 0; host[i]; i++) host[i] = (
char)tolower((
unsigned char)host[i]);
8070 if (!result)
return NULL;
8073 int dp = (strcmp(scheme,
"https") == 0) ? 443 : 80;
8089 if (!u)
return NULL;
8101 if (!s || !isalpha((
unsigned char)s[0]))
return 0;
8103 while (s[i] && (isalnum((
unsigned char)s[i]) || s[i] ==
'+' || s[i] ==
'-' || s[i] ==
'.')) i++;
8125 if (!base)
return NULL;
8128 char* refbuf = strdup(ref ? ref :
"");
8129 if (!refbuf)
return NULL;
8130 char* frag = strchr(refbuf,
'#');
8131 if (frag) *frag =
'\0';
8148 char* refq = strchr(refbuf,
'?');
8149 const char* ref_query = NULL;
8152 ref_query = refq + 1;
8156 char authority[300];
8158 snprintf(authority,
sizeof(authority),
"%s:%d", b->
host ? b->
host :
"", b->
port);
8160 snprintf(authority,
sizeof(authority),
"%s", b->
host ? b->
host :
"");
8164 const char* t_query = NULL;
8166 if (refbuf[0] ==
'\0') {
8168 snprintf(rawpath,
sizeof(rawpath),
"%s", b->
path ? b->
path :
"/");
8169 t_query = refq ? ref_query : b->
query;
8170 }
else if (refbuf[0] ==
'/' && refbuf[1] ==
'/') {
8172 const char* a = refbuf + 2;
8173 const char* slash = strchr(a,
'/');
8175 snprintf(authority,
sizeof(authority),
"%.*s", (
int)(slash - a), a);
8176 snprintf(rawpath,
sizeof(rawpath),
"%s", slash);
8178 snprintf(authority,
sizeof(authority),
"%s", a);
8179 snprintf(rawpath,
sizeof(rawpath),
"/");
8181 t_query = ref_query;
8182 }
else if (refbuf[0] ==
'/') {
8184 snprintf(rawpath,
sizeof(rawpath),
"%s", refbuf);
8185 t_query = ref_query;
8188 const char* bp = (b->
path && b->
path[0]) ? b->
path :
"/";
8189 const char* lastslash = strrchr(bp,
'/');
8190 size_t prefixlen = lastslash ? (size_t)(lastslash - bp) + 1 : 0;
8191 snprintf(rawpath,
sizeof(rawpath),
"%.*s%s", (
int)prefixlen, bp, refbuf);
8192 t_query = ref_query;
8195 char normpath[4096];
8198 N_STR* out =
new_nstr(strlen(authority) +
sizeof(normpath) + 32);
8200 nstrprintf(out,
"%s://%s%s", t_scheme, authority, normpath);
8213 if (!str)
return NULL;
8214 size_t len = strlen(str);
8216 if (!result)
return NULL;
8217 for (
size_t i = 0; i < len; i++) {
8218 if (str[i] ==
'%' && i + 2 < len && isxdigit((
unsigned char)str[i + 1]) && isxdigit((
unsigned char)str[i + 2])) {
8219 const char hex[3] = {str[i + 1], str[i + 2],
'\0'};
8220 unsigned int val = 0;
8221 sscanf(hex,
"%x", &val);
8224 }
else if (str[i] ==
'+') {
8234 if (!u || !*u)
return;
8240 for (
int i = 0; i < p->
nb_params; i++) {
8258 struct addrinfo hints;
8259 memset(&hints, 0,
sizeof(hints));
8260 hints.ai_family = AF_UNSPEC;
8261 hints.ai_socktype = SOCK_STREAM;
8263 struct addrinfo* res = NULL;
8264 int gai_rc = getaddrinfo(host,
port_str, &hints, &res);
8265 if (gai_rc != 0 || !res) {
8266 n_log(
LOG_ERR,
"n_proxy: DNS lookup failed for %s:%d: %s",
8267 host,
port, gai_strerror(gai_rc));
8268 return INVALID_SOCKET;
8271 SOCKET fd = INVALID_SOCKET;
8272 struct addrinfo* rp = NULL;
8273 for (rp = res; rp; rp = rp->ai_next) {
8274 fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
8275 if (fd == INVALID_SOCKET)
continue;
8276 if (connect(fd, rp->ai_addr, (socklen_t)rp->ai_addrlen) == 0)
break;
8278 fd = INVALID_SOCKET;
8282 if (fd == INVALID_SOCKET) {
8289 if (!url)
return NULL;
8292 const char* sep = strstr(url,
"://");
8294 n_log(
LOG_ERR,
"n_proxy_cfg_parse: no scheme in '%s'", url);
8298 size_t scheme_len = (size_t)(sep - url);
8299 if (scheme_len == 0 || scheme_len > 16)
return NULL;
8302 memcpy(scheme, url, scheme_len);
8303 scheme[scheme_len] =
'\0';
8306 if (strcmp(scheme,
"http") != 0 && strcmp(scheme,
"https") != 0 && strcmp(scheme,
"socks5") != 0) {
8307 n_log(
LOG_ERR,
"n_proxy_cfg_parse: unsupported scheme '%s'", scheme);
8311 const char* after = sep + 3;
8312 if (!*after)
return NULL;
8315 const char* at = strchr(after,
'@');
8316 const char* host_start = NULL;
8317 char* username = NULL;
8318 char* password = NULL;
8322 const char* colon = strchr(after,
':');
8323 if (colon && colon < at) {
8324 username = strndup(after, (
size_t)(colon - after));
8325 password = strndup(colon + 1, (
size_t)(at - colon - 1));
8327 username = strndup(after, (
size_t)(at - after));
8329 host_start = at + 1;
8336 char* hostname = NULL;
8339 if (host_start[0] ==
'[') {
8340 const char* bracket = strchr(host_start,
']');
8341 if (!bracket)
goto fail;
8342 hostname = strndup(host_start + 1, (
size_t)(bracket - host_start - 1));
8343 if (bracket[1] ==
':') {
8344 port = atoi(bracket + 2);
8347 const char* colon = strchr(host_start,
':');
8349 hostname = strndup(host_start, (
size_t)(colon - host_start));
8350 port = atoi(colon + 1);
8352 hostname = strdup(host_start);
8356 if (!hostname || !hostname[0])
goto fail;
8359 if (strcmp(scheme,
"http") == 0 || strcmp(scheme,
"https") == 0)
8361 else if (strcmp(scheme,
"socks5") == 0)
8367 if (!cfg)
goto fail;
8368 memset(cfg, 0,
sizeof(*cfg));
8369 cfg->
scheme = strdup(scheme);
8370 cfg->
host = hostname;
8384 if (!cfg || !*cfg)
return;
8395 const char* target_host,
8397 if (!proxy || !target_host)
return -1;
8400 if (fd == INVALID_SOCKET)
return -1;
8403 char connect_req[2048];
8406 len = snprintf(connect_req,
sizeof(connect_req),
8407 "CONNECT %s:%d HTTP/1.1\r\n"
8409 target_host, target_port,
8410 target_host, target_port);
8415 snprintf(cred,
sizeof(cred),
"%s:%s",
8420 if (b64 && b64->
data) {
8421 len += snprintf(connect_req + len,
8422 sizeof(connect_req) - (
size_t)len,
8423 "Proxy-Authorization: Basic %s\r\n",
8431 len += snprintf(connect_req + len,
sizeof(connect_req) - (
size_t)len,
8435 ssize_t sent = send(fd, connect_req, NETW_BUFLEN_CAST(len), 0);
8436 if (sent != (ssize_t)len) {
8437 n_log(
LOG_ERR,
"n_proxy_connect_tunnel: send CONNECT failed");
8443 char resp_buf[4096];
8444 ssize_t nr = recv(fd, resp_buf, NETW_BUFLEN_CAST(
sizeof(resp_buf) - 1), 0);
8446 n_log(
LOG_ERR,
"n_proxy_connect_tunnel: no response from proxy");
8450 resp_buf[nr] =
'\0';
8453 if (strstr(resp_buf,
" 200 ") == NULL) {
8454 n_log(
LOG_ERR,
"n_proxy_connect_tunnel: proxy rejected CONNECT: %.*s",
8455 (
int)(strchr(resp_buf,
'\r') ? strchr(resp_buf,
'\r') - resp_buf : nr),
8461 n_log(
LOG_DEBUG,
"n_proxy_connect_tunnel: tunnel established to %s:%d via %s:%d",
8462 target_host, target_port, proxy->
host, proxy->
port);
8468 const char* target_host,
8470 if (!proxy || !target_host)
return -1;
8473 if (fd == INVALID_SOCKET)
return -1;
8478#if OPENSSL_VERSION_NUMBER >= 0x10100000L
8479 const SSL_METHOD* method = TLS_client_method();
8481 const SSL_METHOD* method = TLSv1_2_client_method();
8483 SSL_CTX* ctx = SSL_CTX_new(method);
8485 n_log(
LOG_ERR,
"n_proxy_connect_tunnel_ssl: SSL_CTX_new failed");
8489 SSL_CTX_set_default_verify_paths(ctx);
8491 SSL* ssl = SSL_new(ctx);
8493 n_log(
LOG_ERR,
"n_proxy_connect_tunnel_ssl: SSL_new failed");
8498 SSL_set_fd(ssl, (
int)fd);
8499 SSL_set_tlsext_host_name(ssl, proxy->
host);
8501 if (SSL_connect(ssl) <= 0) {
8502 n_log(
LOG_ERR,
"n_proxy_connect_tunnel_ssl: TLS handshake to proxy %s:%d failed",
8510 n_log(
LOG_DEBUG,
"n_proxy_connect_tunnel_ssl: TLS established to proxy %s:%d",
8514 char connect_req[2048];
8517 len = snprintf(connect_req,
sizeof(connect_req),
8518 "CONNECT %s:%d HTTP/1.1\r\n"
8520 target_host, target_port,
8521 target_host, target_port);
8526 snprintf(cred,
sizeof(cred),
"%s:%s",
8531 if (b64 && b64->
data) {
8532 len += snprintf(connect_req + len,
8533 sizeof(connect_req) - (
size_t)len,
8534 "Proxy-Authorization: Basic %s\r\n",
8542 len += snprintf(connect_req + len,
sizeof(connect_req) - (
size_t)len,
8546 if (SSL_write(ssl, connect_req, len) != len) {
8547 n_log(
LOG_ERR,
"n_proxy_connect_tunnel_ssl: send CONNECT failed");
8555 char resp_buf[4096];
8556 int nr = SSL_read(ssl, resp_buf, (
int)(
sizeof(resp_buf) - 1));
8558 n_log(
LOG_ERR,
"n_proxy_connect_tunnel_ssl: no response from proxy");
8564 resp_buf[nr] =
'\0';
8567 if (strstr(resp_buf,
" 200 ") == NULL) {
8568 n_log(
LOG_ERR,
"n_proxy_connect_tunnel_ssl: proxy rejected CONNECT: %.*s",
8569 (
int)(strchr(resp_buf,
'\r') ? strchr(resp_buf,
'\r') - resp_buf : nr),
8582 SSL_set_quiet_shutdown(ssl, 1);
8587 n_log(
LOG_DEBUG,
"n_proxy_connect_tunnel_ssl: tunnel established to %s:%d via https://%s:%d",
8588 target_host, target_port, proxy->
host, proxy->
port);
8594 const char* target_host,
8596 if (!proxy || !target_host)
return -1;
8599 if (fd == INVALID_SOCKET)
return -1;
8610 if (send(fd, greeting, 4, 0) != 4)
goto fail;
8615 if (send(fd, greeting, 3, 0) != 3)
goto fail;
8619 char method_resp[2];
8620 if (recv(fd, method_resp, 2, 0) != 2)
goto fail;
8621 if (method_resp[0] != 0x05)
goto fail;
8623 if (method_resp[1] == 0x02 && use_auth) {
8625 size_t ulen = strlen(proxy->
username);
8627 if (ulen > 255 || plen > 255)
goto fail;
8631 auth_req[pos++] = 0x01;
8632 auth_req[pos++] = (char)ulen;
8633 memcpy(auth_req + pos, proxy->
username, ulen);
8635 auth_req[pos++] = (char)plen;
8637 memcpy(auth_req + pos, proxy->
password, plen);
8644 if (send(fd, auth_req, NETW_BUFLEN_CAST(pos), 0) != (ssize_t)pos)
goto fail;
8647 if (recv(fd, auth_resp, 2, 0) != 2)
goto fail;
8648 if (auth_resp[1] != 0x00) {
8649 n_log(
LOG_ERR,
"n_proxy_connect_socks5: auth rejected");
8652 }
else if (method_resp[1] != 0x00) {
8653 n_log(
LOG_ERR,
"n_proxy_connect_socks5: no acceptable method");
8659 size_t hlen = strlen(target_host);
8660 if (hlen > 255)
goto fail;
8664 conn_req[pos++] = 0x05;
8665 conn_req[pos++] = 0x01;
8666 conn_req[pos++] = 0x00;
8667 conn_req[pos++] = 0x03;
8668 conn_req[pos++] = (char)hlen;
8669 memcpy(conn_req + pos, target_host, hlen);
8671 conn_req[pos++] = (char)((target_port >> 8) & 0xFF);
8672 conn_req[pos++] = (char)(target_port & 0xFF);
8674 if (send(fd, conn_req, NETW_BUFLEN_CAST(pos), 0) != (ssize_t)pos)
goto fail;
8681 ssize_t nr = recv(fd, conn_resp, NETW_BUFLEN_CAST(
sizeof(conn_resp)), 0);
8682 if (nr < 4)
goto fail;
8683 if (conn_resp[0] != 0x05 || conn_resp[1] != 0x00) {
8684 n_log(
LOG_ERR,
"n_proxy_connect_socks5: connect failed, reply=%02x",
8690 if (conn_resp[3] == 0x03 && nr < 5) {
8693 recv(fd, extra, NETW_BUFLEN_CAST(
sizeof(extra)), 0);
8694 }
else if (conn_resp[3] == 0x04 && nr < 10) {
8697 size_t need = 22 - (size_t)nr;
8698 if (need <=
sizeof(extra)) {
8699 recv(fd, extra, NETW_BUFLEN_CAST(need), 0);
8704 n_log(
LOG_DEBUG,
"n_proxy_connect_socks5: tunnel established to %s:%d via %s:%d",
8705 target_host, target_port, proxy->
host, proxy->
port);
8709 n_log(
LOG_ERR,
"n_proxy_connect_socks5: handshake failed");
8734 n_log(
LOG_ERR,
"netw_adopt_client_fd: target NETWORK must be empty");
8737 if (fd == INVALID_SOCKET) {
8738 n_log(
LOG_ERR,
"netw_adopt_client_fd: invalid socket");
8742 n_log(
LOG_ERR,
"netw_adopt_client_fd: unable to load WSA dll's");
8748 (*netw)->link.sock = fd;
8749 (*netw)->link.ip = strdup(host ? host :
"");
8750 if (!(*netw)->link.ip) {
8754 (*netw)->link.port = strdup(
port ?
port :
"");
8755 if (!(*netw)->link.port) {
8761 n_log(
LOG_DEBUG,
"netw_adopt_client_fd: adopted socket %d for %s:%s", (
int)fd, (*netw)->link.ip, (*netw)->link.port);
static NETWORK_POOL * pool
static void type(N_GUI_CTX *gui, const char *s)
Type a whole string into whatever has focus.
static int on_request(void *ud, N_H2_SERVER *srv, const N_H2_REQUEST *req)
NETWORK * netw
Network for server mode, accepting incomming.
static int eq(double a, double b)
void line(BITMAP *bmp, int x1, int y1, int x2, int y2, int color)
draw a line
volatile char key[227]
TRUE while the key is held.
#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
#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 container.
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 N_PRINTF_FORMAT(fmt, args)
declare a function printf-like; nothing on a compiler without attributes
#define LOG_WARNING
warning conditions
#define LOG_INFO
informational
N_STR * zip4_nstr(N_STR *src)
Compress src with LZ4 block format.
N_STR * unzip4_nstr(N_STR *src)
Decompress an N_STR produced by zip4_nstr.
size_t written
number of meaningful bytes in data, excluding the null terminator; the size including the null termin...
size_t length
total allocation (in bytes) of the data buffer, padding included
void free_nstr_ptr(void *ptr)
Free a N_STR pointer structure.
N_STR * n_str_url_encode(const char *src)
Percent-encode a C string per RFC 3986 (unreserved set kept as-is).
size_t NSTRBYTE
N_STR base unit.
#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.
int char_to_nstr_ex(const char *from, NSTRBYTE nboct, N_STR **to)
Convert a char into a N_STR, extended version.
A box including a string and his lenght.
void u_sleep(unsigned int usec)
wrapper around usleep for API consistency
int start_HiTimer(N_TIME *timer)
Initialize or restart from zero any N_TIME HiTimer.
time_t get_usec(N_TIME *timer)
Poll any N_TIME HiTimer, returning usec, and moving currentTime to startTime.
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)
N_SOCKET link
networking socket
size_t content_length
Store content length.
char * type
Type of request.
char * filename
Content-Disposition filename, or "".
int port
port number (0 if not specified)
int masked
MASK bit (client-to-server frames are masked).
char * host
proxy hostname
pthread_rwlock_t rwlock
thread safety
NETWORK * netw
underlying network connection
char * path
path starting with "/", or "/" if empty
char * content_type
the part's Content-Type, or ""
N_STR * body
response body
char * scheme
"http" or "https"
int opcode
opcode (N_WS_OP_*)
NETWORK * netw
underlying network connection
void(* on_event)(N_SSE_EVENT *event, struct N_SSE_CONN *conn, void *user_data)
callback
N_STR * body
the part's raw body bytes (binary-safe), or NULL
int fin
FIN bit (1 = final fragment).
N_STR * event
event type (or NULL for default)
int connected
1 if handshake completed
N_STR * body
request body (or NULL)
uint64_t payload_len
payload length in bytes
char * password
NULL if no auth.
char * query
raw query string without leading '?
N_URL_PARAM params[64]
parsed key=value pairs
char * body
Pointer to the body data.
LIST * headers
list of char* "Name: Value" strings
char * value
parameter value
char * host
remote hostname
SOCKET sock
a normal socket
char path[2048]
request path
char * scheme
"http", "https", or "socks5"
char * port
port of socket
int nb_params
number of parsed parameters
char * name
Content-Disposition form-field name, or "".
SSL_CTX * ctx
SSL context holder.
const unsigned char * payload
pointer into the input buffer (still masked)
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)
int rsv
the three reserved bits, rsv1<<2 | rsv2<<1 | rsv3
N_STR * payload
message payload
char method[16]
HTTP method.
unsigned char mask[4]
masking key when masked, else zeroed
char * disposition_extra
extra Content-Disposition parameters, appended verbatim after name (and filename) as written,...
char * username
NULL if no auth.
int status_code
HTTP status code.
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
void * user_data
user data for callback
#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.
ssize_t send_ssl_data_once(void *netw, char *buf, uint32_t n)
single-attempt TLS send for non-blocking sockets (reactor use).
#define N_URL_MAX_PARAMS
maximum number of parsed query parameters
#define NETW_IO_WANT_READ
single-attempt I/O (send_data_once / recv_data_once): the operation cannot progress until the socket ...
#define NETW_COMPRESS_THRESHOLD
Opportunistic compression policy.
N_STR * netw_get_msg(NETWORK *netw)
Get a message from aimed NETWORK.
N_STR * n_http_build_multipart(LIST *parts, const char *boundary)
build a multipart/form-data body from a LIST of N_HTTP_MULTIPART_PART* using boundary (without "--");...
int netw_add_msg(NETWORK *netw, N_STR *msg)
Add a message to send in aimed NETWORK.
int netw_ssl_get_verify_result(NETWORK *netw, const char *expected_host, char *errbuf, size_t errsz)
evaluate the peer certificate of a completed TLS client connection
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_set_crypto_pem_ctx(NETWORK *netw, const N_STR *key_pem, const N_STR *cert_pem)
install an in-memory cert/key on a single accepted connection's SSL
#define N_WS_MAX_PAYLOAD
Largest WebSocket payload n_ws_recv() will accept from a peer, in bytes.
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
ssize_t recv_ssl_data_once(void *netw, char *buf, uint32_t n)
single-attempt TLS recv for non-blocking sockets (reactor use).
#define NETW_THR_EXIT_ERROR
Internal DONE value indicating a send/recv thread observed NETW_ERROR without an in-flight local shut...
#define NETW_COMPRESS_MIN_RATIO
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
#define NETW_MSG_MAX_FRAME_SIZE
Maximum accepted size, in bytes, of a single received message frame payload.
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 (delegates to n_str_url_encode())
int n_http_status_class(int status_code)
leading digit of an HTTP status code (1..5), or 0 when outside 100..599
#define SSL_get1_peer_certificate(ssl)
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.
#define N_ENUM_netw_code_type(_)
Network codes definition.
int netw_set_crypto(NETWORK *netw, char *key, char *certificate)
activate SSL encryption on selected network, using key and certificate
int netw_ssl_server_handshake_ex(NETWORK *netw, netw_sni_pick_cb pick, void *user_data, const char *const *alpn, size_t alpn_count)
upgrade an already-accepted plaintext connection to a TLS server, selecting the certificate per clien...
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_adopt_client_fd(NETWORK **netw, SOCKET fd, const char *host, const char *port)
Adopt an already-connected socket fd into a fresh client NETWORK.
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.
ssize_t recv_data_once(void *netw, char *buf, uint32_t n)
single-attempt recv for non-blocking sockets (reactor use).
#define NETWORK_IPV6
Flag to force IPV6.
void n_http_multipart_free(LIST **parts)
free a LIST returned by n_http_parse_multipart and NULL the pointer
void n_ws_unmask(unsigned char *dst, const unsigned char *src, size_t len, const unsigned char mask[4])
XOR-unmask len bytes of src into dst using a 4-byte WebSocket mask key.
int netw_connect_ex_to(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, int connect_timeout_ms)
Use this to connect a NETWORK to any listening one.
#define NETWORK_UDP
Flag for UDP transport.
N_STR * n_url_canonicalize_string(const char *url)
parse and canonicalize a URL string in one call
N_STR * n_url_resolve(const char *base, const char *ref)
resolve a (possibly relative) URL reference against an absolute base
ssize_t send_data_once(void *netw, char *buf, uint32_t n)
single-attempt send for non-blocking sockets (reactor use).
int netw_ssl_server_handshake_once(NETWORK *netw, SSL_CTX *ctx)
non-blocking, resumable TLS server handshake against a shared ctx
int netw_get_http_date(char *buffer, size_t buffer_size)
helper function to generate the current date in HTTP format
int netw_connect_nonblock(NETWORK **netw, char *host, char *port, int ip_version)
begin a non-blocking TCP connect without waiting for it to complete
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_ssl_handshake_once(NETWORK *netw, const char *sni_hostname)
non-blocking, resumable TLS client handshake
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.
int netw_set_compression_mode(NETWORK *netw, int mode)
Pick send-side payload compression algorithm.
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)
int n_netw_get_error_count(const NETWORK *netw)
Get number of captured errors on a NETWORK handle.
int n_ws_frame_parse(const unsigned char *buf, size_t len, N_WS_FRAME *frame, size_t *consumed)
Parse one WebSocket frame from a byte buffer (stateless, no allocation).
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://).
int n_http_status_is_server_error(int status_code)
1 when status_code is a server error (5xx), else 0
#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.
const char * netw_ssl_get_sni(NETWORK *netw)
get the SNI server_name the peer requested in its ClientHello
int n_http_status_is_client_error(int status_code)
1 when status_code is a client error (4xx), else 0
#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.
#define netw_atomic_read_reactor_mode(netw)
Lock-free atomic read of the reactor_mode flag.
size_t netw_pool_nbclients(NETWORK_POOL *netw_pool)
return the number of networks in netw_pool
int(* netw_sni_pick_cb)(const char *sni, N_STR **cert_pem, N_STR **key_pem, void *user_data)
SNI cert-pick callback for netw_accept_ssl_with_sni_cb: given the client SNI host (may be NULL),...
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 netw_pool_broadcast(NETWORK_POOL *netw_pool, const NETWORK *from, N_STR *net_msg)
add net_msg to all network in netork pool
int n_http_decompress_body(const N_STR *body, const char *encoding, N_STR **out)
decompress an HTTP body per its Content-Encoding ("gzip"/"deflate"; identity/NULL/unknown copies thro...
int netw_ssl_connect_client_to(NETWORK **netw, char *host, char *port, int ip_version, int connect_timeout_ms)
Connect as an SSL client without providing a client certificate.
const char * n_netw_get_error(const NETWORK *netw, int index)
Get captured error message by index (0 = oldest).
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 n_http_multipart_boundary(const char *content_type, char *out, size_t outsz)
copy the boundary token from a Content-Type value into out (without the leading "--").
int netw_ssl_set_alpn(NETWORK *netw, const char *const *protos, size_t count)
offer an ALPN protocol list on a TLS client connection, before its handshake
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
NETWORK * netw_accept_ssl_with_sni_cb(NETWORK *listen, netw_sni_pick_cb pick, void *user_data)
accept a TLS connection, selecting the server certificate per client SNI
int netw_get_state(NETWORK *netw, uint32_t *state, int *thr_engine_status)
Get the state of a network.
N_SSE_CONN * n_sse_connect(const char *host, const char *port, const char *path, int use_ssl, const char *user_agent, void(*on_event)(N_SSE_EVENT *, N_SSE_CONN *, void *), void *user_data)
Connect to an SSE endpoint and start reading events.
N_URL * n_url_parse(const char *url)
parse a URL string into components
int n_http_status_is_informational(int status_code)
1 when status_code is informational (1xx), else 0
void netw_pool_netw_close(void *netw_ptr)
close a network from a network pool
int netw_connect_check(NETWORK *netw)
advance a non-blocking connect started by netw_connect_nonblock()
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
#define netw_atomic_read_reactor_handle(netw)
Same contract for the back-pointer to the reactor.
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' .
static void SSL_set_hostflags(SSL *ssl, unsigned int flags)
1.0.2 stand-in for SSL_set_hostflags(): set the X509_CHECK_FLAG_* host-check flags of ssl
N_STR * n_url_canonicalize(const N_URL *u)
produce a canonical URL string for deduplication
#define NETW_IO_WANT_WRITE
single-attempt I/O: the operation cannot progress until the socket is WRITABLE.
#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.
int n_http_status_is_success(int status_code)
1 when status_code is success (2xx), else 0
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.
#define NETW_THR_EXIT_OK
Internal DONE value indicating a send/recv thread reached end-of-life cleanly (peer QUIT or local NET...
int netw_ssl_set_ciphers(NETWORK *netw, const char *cipher_list, const char *ciphersuites)
set the cipher list (TLS 1.2 and below) and/or the ciphersuites (TLS 1.3)
int netw_ssl_connect_client(NETWORK **netw, char *host, char *port, int ip_version)
Connect as an SSL client without providing a client certificate.
int netw_ssl_start_client(NETWORK *netw)
Set up a client SSL_CTX on an already-connected NETWORK.
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 n_http_status_is_redirect(int status_code)
1 when status_code is a redirect (3xx), else 0
LIST * n_http_parse_multipart(const N_STR *body, const char *boundary)
parse a multipart body (boundary given without "--") into a LIST of N_HTTP_MULTIPART_PART*; free with...
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
size_t n_ws_frame_build(int fin, int opcode, int do_mask, const unsigned char *payload, size_t len, const unsigned char mask_key[4], unsigned char *out, size_t out_cap)
build a WebSocket frame into out (optionally masking the payload).
int netw_ssl_server_handshake(NETWORK *netw, netw_sni_pick_cb pick, void *user_data)
upgrade an already-accepted plaintext connection to a TLS server, selecting the certificate per clien...
#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_connect_to(NETWORK **netw, char *host, char *port, int ip_version, int connect_timeout_ms)
Connect a NETWORK with a bounded connection-establishment time.
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.
#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
int netw_ssl_get_alpn(NETWORK *netw, char *out, size_t out_len)
get the ALPN protocol the completed handshake selected
int netw_ssl_set_versions(NETWORK *netw, int min_version, int max_version)
set the minimum (and optionally maximum) TLS protocol version on a connection's SSL context
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
static int SSL_set1_host(SSL *ssl, const char *host)
1.0.2 stand-in for SSL_set1_host(): set the expected peer name of ssl, returning 1 on success and 0 o...
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
void netw_set_connect_abort_cb(int(*cb)(void *ctx), void *ctx)
Register a process-wide callback polled while a connect is in progress.
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_COMPRESS_NONE
no automatic compression on send, still decompresses inbound
@ NETW_COMPRESS_ZLIB
compress on send with zlib, decompress either on recv
@ NETW_COMPRESS_LZ4
compress on send with LZ4, decompress either on recv
@ NETW_THR_ENGINE_STARTED
@ NETW_THR_ENGINE_STOPPED
a single part of a parsed multipart/form-data body
parsed HTTP request for mock server callback
HTTP response to send from mock server callback.
Parsed proxy URL components.
SSE event received from server.
A single parsed WebSocket frame (RFC 6455).
structure for splitting HTTP requests
structure of a network pool
N_STR * unzip_nstr(N_STR *src)
return an uncompressed version of src
N_STR * zip_nstr(N_STR *src)
return a compressed version of src
Base64 encoding and decoding functions using N_STR.
Hash functions and table.
LZ4 block-compression handler.
static char * netstrerror(int code)
BSD style errno string NO WORKING ON REDHAT.
#define NETW_CALL_RETRY(__retvar, __expression, __max_tries)
network-aware retry macro: retries on EINTR and EAGAIN/EWOULDBLOCK
static size_t _netw_alpn_encode(const char *const *protos, size_t count, unsigned char *out, size_t out_cap)
static void _n_url_normalize_path(const char *path, char *out, size_t outsz)
normalize a path by removing "." and ".." segments (RFC 3986 style)
static int _ssl_use_pem(SSL *ssl, const char *key_pem, const char *cert_pem)
static char * mp_strndup(const char *s, size_t len)
static __thread int s_connect_err_next
static int _netw_sni_servername_cb(SSL *ssl, int *al, void *arg)
#define N_WS_FRAME_MAX_PAYLOAD
upper bound on a single parsed WebSocket frame payload, to bound a proxy's reassembly buffer against ...
static ssize_t _ws_read(N_WS_CONN *conn, void *buf, size_t len)
read exactly len bytes from a WebSocket connection
static void mp_put(char *buf, size_t *pos, const void *src, size_t n)
static void netw_init_locks(void)
void netw_ssl_print_errors(SOCKET socket)
print the queued 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 int(* _netw_connect_abort_cb)(void *)
Optional connect-abort callback, or NULL.
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)
static int _netw_ssl_handshake_step(NETWORK *netw)
advance an in-flight TLS handshake by a single non-blocking step
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).
static int _netw_start_nonblock_connect(NETWORK *netw)
create a non-blocking socket for netw->link.connect_rp and start connect()
static SOCKET _proxy_tcp_connect(const char *host, int port)
Helper: connect a plain TCP socket to host:port.
long long g_netw_bytes_sent
Add a message to send in aimed NETWORK.
static void * _netw_connect_abort_ctx
Opaque context handed to _netw_connect_abort_cb on each poll.
static void mp_emit(char *buf, size_t *pos, LIST *parts, const char *dash)
static int n_http_inflate(const unsigned char *src, size_t len, int window_bits, N_STR **out)
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 mp_part_free(void *ptr)
static void _netw_capture_connect_error(const char *fmt,...)
capture a pre-connection error (thread-local)
#define NETW_CONNECT_ABORT_POLL_MS
Poll interval (milliseconds) at which an in-progress connect wakes to check the registered connect-ab...
static long mp_find(const unsigned char *hay, size_t hlen, const char *needle, size_t nlen)
char * get_in_addr(struct sockaddr *sa)
get sockaddr, IPv4 or IPv6
static int _netw_timed_connect(NETWORK *netw, SOCKET sock, struct addrinfo *rp, int connect_timeout_ms)
Connect a socket, optionally bounding the attempt by a timeout.
static const char * mp_header_value(const char *block, size_t blen, const char *name, size_t *vlen)
static void _n_mock_request_clean(N_HTTP_REQUEST *req)
Free the contents of an N_HTTP_REQUEST (does not free the struct itself).
static int _n_url_has_scheme(const char *s)
test whether a reference string begins with its own URI scheme
long long g_netw_bytes_recv
static char * mp_disp_param(const char *disp, size_t dlen, const char *param)
char * netw_get_openssl_error_string()
get the OpenSSL error string
static __thread char s_connect_errors[8][512]
static int _netw_alpn_select_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
static void _netw_capture_error(NETWORK *netw, const char *fmt,...)
capture an error into a NETWORK handle's ring buffer
static void mp_puts(char *buf, size_t *pos, const char *s)
static void netw_kill_locks(void)
static int netw_is_ip_literal(const char *host)
is host a numeric IP literal rather than a DNS name?
static void _netw_crypto_setup_failed(NETWORK *netw)
Network messages , serialization tools.
void n_reactor_notify_send(NETWORK *netw)
Producer-side wake-up after a netw_add_msg.
void n_reactor_close_netw_sync(NETWORK *netw)
Synchronously close a reactor-registered NETWORK from the game thread.
Single-threaded epoll reactor for n_network connections.
ZLIB compression handler.