diff --git a/chttp.c b/chttp.c index 8d471a0..9c0f04a 100644 --- a/chttp.c +++ b/chttp.c @@ -66,7 +66,6 @@ static bool is_printable(char c) return c >= ' ' && c <= '~'; } -#include void print_bytes(HTTP_String prefix, HTTP_String src) { if (src.len == 0) @@ -1934,7 +1933,8 @@ int socket_manager_add_certificate(SocketManager *sm, static bool is_secure(Socket *s) { #ifdef HTTPS_ENABLED - return (s->server_secure_context != NULL); + return s->server_secure_context != NULL + || s->client_secure_context != NULL; #else return false; #endif @@ -2061,13 +2061,13 @@ static void socket_update(Socket *s) struct sockaddr_in buf; buf.sin_family = AF_INET; buf.sin_port = htons(addr.port); - memcpy(&buf.sin_addr, &addr.ipv4, sizeof(IPv4)); + memcpy(&buf.sin_addr, &addr.ipv4, sizeof(HTTP_IPv4)); ret = connect(sock, (struct sockaddr*) &buf, sizeof(buf)); } else { struct sockaddr_in6 buf; buf.sin6_family = AF_INET6; buf.sin6_port = htons(addr.port); - memcpy(&buf.sin6_addr, &addr.ipv6, sizeof(IPv6)); + memcpy(&buf.sin6_addr, &addr.ipv6, sizeof(HTTP_IPv6)); ret = connect(sock, (struct sockaddr*) &buf, sizeof(buf)); } @@ -2145,7 +2145,7 @@ static void socket_update(Socket *s) } else { #ifdef HTTPS_ENABLED if (s->ssl == NULL) { - s->ssl = SSL_new(s->server_secure_context->p); + s->ssl = SSL_new(s->client_secure_context->p); if (s->ssl == NULL) { s->state = SOCKET_STATE_DIED; s->events = 0; @@ -2370,7 +2370,7 @@ static int socket_manager_register_events_nolock( // be processed immediately. for (int i = 0, j = 0; j < sm->num_used; i++) { Socket *s = &sm->sockets[i]; - if (s->state = SOCKET_STATE_FREE) + if (s->state == SOCKET_STATE_FREE) continue; j++; @@ -2463,6 +2463,10 @@ static int socket_manager_translate_events_nolock( s->user = NULL; #ifdef HTTPS_ENABLED s->ssl = NULL; + s->server_secure_context = NULL; + s->client_secure_context = NULL; + if (secure) + &s->server_secure_context = sm->server_secure_context; #endif socket_update(s); @@ -2592,7 +2596,7 @@ static int resolve_connect_targets(ConnectTarget *targets, for (struct addrinfo *rp = res; rp; rp = rp->ai_next) { if (rp->ai_family == AF_INET) { - IPv4 ipv4 = *(IPv4*) &((struct sockaddr_in*)rp->ai_addr)->sin_addr; + HTTP_IPv4 ipv4 = *(HTTP_IPv4*) &((struct sockaddr_in*)rp->ai_addr)->sin_addr; if (num_resolved < max_resolved) { resolved[num_resolved].is_ipv4 = true; resolved[num_resolved].ipv4 = ipv4; @@ -2604,7 +2608,7 @@ static int resolve_connect_targets(ConnectTarget *targets, num_resolved++; } } else if (rp->ai_family == AF_INET6) { - IPv6 ipv6 = *(IPv6*) &((struct sockaddr_in6*)rp->ai_addr)->sin6_addr; + HTTP_IPv6 ipv6 = *(HTTP_IPv6*) &((struct sockaddr_in6*)rp->ai_addr)->sin6_addr; if (num_resolved < max_resolved) { resolved[num_resolved].is_ipv4 = false; resolved[num_resolved].ipv6 = ipv6; @@ -2661,7 +2665,13 @@ int socket_connect(SocketManager *sm, int num_targets, if (sm->num_used == sm->max_used) return -1; -#ifndef HTTPS_ENABLED +#ifdef HTTPS_ENABLED + if (!sm->at_least_one_secure_connect) { + if (client_secure_context_init(&sm->client_secure_context) < 0) + return -1; + sm->at_least_one_secure_connect = true; + } +#else if (secure) return -1; #endif @@ -2697,8 +2707,11 @@ int socket_connect(SocketManager *sm, int num_targets, s->sock = NATIVE_SOCKET_INVALID; s->user = user; #ifdef HTTPS_ENABLED - s->server_secure_context = &sm->server_secure_context; + s->server_secure_context = NULL; + s->client_secure_context = NULL; s->ssl = NULL; + if (secure) + s->client_secure_context = &sm->client_secure_context; #endif sm->num_used++; return 0; @@ -2751,7 +2764,7 @@ static int socket_recv_nolock(SocketManager *sm, SocketHandle handle, } ret = 0; } - return 0; + return ret; } else { #ifdef HTTPS_ENABLED int ret = SSL_read(s->ssl, dst, max); @@ -3640,17 +3653,13 @@ int http_request_builder_send(HTTP_RequestBuilder builder) // Set up target based on host type if (conn->url.authority.host.mode == HTTP_HOST_MODE_NAME) { target.type = CONNECT_TARGET_NAME; - target.name = (String) { - conn->url.authority.host.name.ptr, - conn->url.authority.host.name.len - }; + target.name = conn->url.authority.host.name; } else if (conn->url.authority.host.mode == HTTP_HOST_MODE_IPV4) { target.type = CONNECT_TARGET_IPV4; - target.ipv4 = (IPv4) { conn->url.authority.host.ipv4.data }; + target.ipv4 = conn->url.authority.host.ipv4; } else if (conn->url.authority.host.mode == HTTP_HOST_MODE_IPV6) { target.type = CONNECT_TARGET_IPV6; - for (int i = 0; i < 8; i++) - target.ipv6.data[i] = conn->url.authority.host.ipv6.data[i]; + target.ipv6 = conn->url.authority.host.ipv6; } else { // Invalid host mode - clean up connection http_client_conn_free(conn); @@ -3861,7 +3870,7 @@ void http_server_free(HTTP_Server *server) for (int i = 0, j = 0; j < server->num_conns; i++) { HTTP_ServerConn *conn = &server->conns[i]; - if (conn->state != HTTP_SERVER_CONN_FREE) + if (conn->state == HTTP_SERVER_CONN_FREE) continue; j++; @@ -4031,7 +4040,7 @@ int http_server_process_events(HTTP_Server *server, int num = 0; ByteView src = byte_queue_read_buf(&conn->output); - if (src.len) num = socket_recv(&server->sockets, conn->handle, src.ptr, src.len); + if (src.len) num = socket_send(&server->sockets, conn->handle, src.ptr, src.len); byte_queue_read_ack(&conn->output, num); if (byte_queue_error(&conn->output)) @@ -4249,7 +4258,7 @@ static void patch_special_headers(HTTP_ServerConn *conn) byte_queue_patch(&conn->output, conn->content_length_value_offset, tmp, len); } -void http_response_builder_body(HTTP_ResponseBuilder builder, String str) +void http_response_builder_body(HTTP_ResponseBuilder builder, HTTP_String str) { HTTP_ServerConn *conn = builder_to_conn(builder); if (conn == NULL) @@ -4266,7 +4275,7 @@ void http_response_builder_body(HTTP_ResponseBuilder builder, String str) byte_queue_write(&conn->output, str.ptr, str.len); } -void http_response_builder_send(HTTP_ResponseBuilder builder, String str) +void http_response_builder_send(HTTP_ResponseBuilder builder, HTTP_String str) { HTTP_ServerConn *conn = builder_to_conn(builder); if (conn == NULL) @@ -4291,3 +4300,28 @@ void http_response_builder_send(HTTP_ResponseBuilder builder, String str) conn->state = HTTP_SERVER_CONN_FLUSHING; conn->gen++; } + +//////////////////////////////////////////////////////////////////////////////////////// +// Copyright 2025 Francesco Cozzuto +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall +// be included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +//////////////////////////////////////////////////////////////////////////////////////// diff --git a/chttp.h b/chttp.h index 7bb7be1..fecc7e5 100644 --- a/chttp.h +++ b/chttp.h @@ -7,6 +7,7 @@ // src/includes.h //////////////////////////////////////////////////////////////////////////////////////// +#include #include #include #include @@ -25,7 +26,7 @@ #include #include #include -#include +#include - #include #include #include @@ -317,21 +318,8 @@ int server_secure_context_add_certificate(ServerSecureContext *ctx, typedef uint32_t SocketHandle; #define SOCKET_HANDLE_INVALID ((SocketHandle) 0) -typedef struct { - char *ptr; - int len; -} String; - typedef uint16_t Port; -typedef struct { - uint32_t data; -} IPv4; - -typedef struct { - uint16_t data[8]; -} IPv6; - typedef enum { SOCKET_EVENT_READY, SOCKET_EVENT_DISCONNECT, @@ -392,8 +380,8 @@ typedef struct { // Internal use only typedef struct { union { - IPv4 ipv4; - IPv6 ipv6; + HTTP_IPv4 ipv4; + HTTP_IPv6 ipv6; }; bool is_ipv4; Port port; @@ -439,6 +427,7 @@ typedef struct { }; #ifdef HTTPS_ENABLED + ClientSecureContext *client_secure_context; ServerSecureContext *server_secure_context; SSL *ssl; #endif @@ -569,9 +558,9 @@ typedef struct { ConnectTargetType type; Port port; union { - IPv4 ipv4; - IPv6 ipv6; - String name; + HTTP_IPv4 ipv4; + HTTP_IPv6 ipv6; + HTTP_String name; }; } ConnectTarget; @@ -1127,11 +1116,11 @@ void http_response_builder_status(HTTP_ResponseBuilder builder, int status); void http_response_builder_header(HTTP_ResponseBuilder builder, HTTP_String str); // Append some bytes to the response's body -void http_response_builder_body(HTTP_ResponseBuilder builder, String str); +void http_response_builder_body(HTTP_ResponseBuilder builder, HTTP_String str); // Mark the response as complete. This will invalidate // the response builder handle. -void http_response_builder_send(HTTP_ResponseBuilder builder, String str); +void http_response_builder_send(HTTP_ResponseBuilder builder, HTTP_String str); //////////////////////////////////////////////////////////////////////////////////////// // Copyright 2025 Francesco Cozzuto diff --git a/misc/TODO.txt b/misc/TODO.txt new file mode 100644 index 0000000..bd49f6a --- /dev/null +++ b/misc/TODO.txt @@ -0,0 +1,4 @@ +- From Claude: + While peer verification is enabled, there's no hostname verification. An attacker with any valid certificate could MITM connections. Should add SSL_set1_host(ssl, hostname); +- Add connection timeouts +- Per RFC 6265, cookie values can be quoted. This parser would include the quotes in the value. diff --git a/misc/amalg.py b/misc/amalg.py index 537cae2..b2b912f 100644 --- a/misc/amalg.py +++ b/misc/amalg.py @@ -82,5 +82,5 @@ source.append_file("src/byte_queue.c") source.append_file("src/cert.c") source.append_file("src/client.c") source.append_file("src/server.c") -header.append_text(license) +source.append_text(license) source.save("chttp.c") diff --git a/src/basic.c b/src/basic.c index 961ed6b..28d5693 100644 --- a/src/basic.c +++ b/src/basic.c @@ -54,7 +54,6 @@ static bool is_printable(char c) return c >= ' ' && c <= '~'; } -#include void print_bytes(HTTP_String prefix, HTTP_String src) { if (src.len == 0) diff --git a/src/client.c b/src/client.c index a83d933..144a793 100644 --- a/src/client.c +++ b/src/client.c @@ -268,17 +268,13 @@ int http_request_builder_send(HTTP_RequestBuilder builder) // Set up target based on host type if (conn->url.authority.host.mode == HTTP_HOST_MODE_NAME) { target.type = CONNECT_TARGET_NAME; - target.name = (String) { - conn->url.authority.host.name.ptr, - conn->url.authority.host.name.len - }; + target.name = conn->url.authority.host.name; } else if (conn->url.authority.host.mode == HTTP_HOST_MODE_IPV4) { target.type = CONNECT_TARGET_IPV4; - target.ipv4 = (IPv4) { conn->url.authority.host.ipv4.data }; + target.ipv4 = conn->url.authority.host.ipv4; } else if (conn->url.authority.host.mode == HTTP_HOST_MODE_IPV6) { target.type = CONNECT_TARGET_IPV6; - for (int i = 0; i < 8; i++) - target.ipv6.data[i] = conn->url.authority.host.ipv6.data[i]; + target.ipv6 = conn->url.authority.host.ipv6; } else { // Invalid host mode - clean up connection http_client_conn_free(conn); diff --git a/src/includes.h b/src/includes.h index 2911f92..6bf0bbd 100644 --- a/src/includes.h +++ b/src/includes.h @@ -1,4 +1,5 @@ +#include #include #include #include @@ -17,7 +18,7 @@ #include #include #include -#include +#include - #include #include #include diff --git a/src/server.c b/src/server.c index 854971f..d01984a 100644 --- a/src/server.c +++ b/src/server.c @@ -42,7 +42,7 @@ void http_server_free(HTTP_Server *server) for (int i = 0, j = 0; j < server->num_conns; i++) { HTTP_ServerConn *conn = &server->conns[i]; - if (conn->state != HTTP_SERVER_CONN_FREE) + if (conn->state == HTTP_SERVER_CONN_FREE) continue; j++; @@ -212,7 +212,7 @@ int http_server_process_events(HTTP_Server *server, int num = 0; ByteView src = byte_queue_read_buf(&conn->output); - if (src.len) num = socket_recv(&server->sockets, conn->handle, src.ptr, src.len); + if (src.len) num = socket_send(&server->sockets, conn->handle, src.ptr, src.len); byte_queue_read_ack(&conn->output, num); if (byte_queue_error(&conn->output)) @@ -430,7 +430,7 @@ static void patch_special_headers(HTTP_ServerConn *conn) byte_queue_patch(&conn->output, conn->content_length_value_offset, tmp, len); } -void http_response_builder_body(HTTP_ResponseBuilder builder, String str) +void http_response_builder_body(HTTP_ResponseBuilder builder, HTTP_String str) { HTTP_ServerConn *conn = builder_to_conn(builder); if (conn == NULL) @@ -447,7 +447,7 @@ void http_response_builder_body(HTTP_ResponseBuilder builder, String str) byte_queue_write(&conn->output, str.ptr, str.len); } -void http_response_builder_send(HTTP_ResponseBuilder builder, String str) +void http_response_builder_send(HTTP_ResponseBuilder builder, HTTP_String str) { HTTP_ServerConn *conn = builder_to_conn(builder); if (conn == NULL) diff --git a/src/server.h b/src/server.h index be25990..3a86099 100644 --- a/src/server.h +++ b/src/server.h @@ -201,8 +201,8 @@ void http_response_builder_status(HTTP_ResponseBuilder builder, int status); void http_response_builder_header(HTTP_ResponseBuilder builder, HTTP_String str); // Append some bytes to the response's body -void http_response_builder_body(HTTP_ResponseBuilder builder, String str); +void http_response_builder_body(HTTP_ResponseBuilder builder, HTTP_String str); // Mark the response as complete. This will invalidate // the response builder handle. -void http_response_builder_send(HTTP_ResponseBuilder builder, String str); +void http_response_builder_send(HTTP_ResponseBuilder builder, HTTP_String str); diff --git a/src/socket.c b/src/socket.c index bcdec2f..12587f4 100644 --- a/src/socket.c +++ b/src/socket.c @@ -231,7 +231,8 @@ int socket_manager_add_certificate(SocketManager *sm, static bool is_secure(Socket *s) { #ifdef HTTPS_ENABLED - return (s->server_secure_context != NULL); + return s->server_secure_context != NULL + || s->client_secure_context != NULL; #else return false; #endif @@ -358,7 +359,7 @@ static void socket_update(Socket *s) struct sockaddr_in buf; buf.sin_family = AF_INET; buf.sin_port = htons(addr.port); - memcpy(&buf.sin_addr, &addr.ipv4, sizeof(IPv4)); + memcpy(&buf.sin_addr, &addr.ipv4, sizeof(HTTP_IPv4)); ret = connect(sock, (struct sockaddr*) &buf, sizeof(buf)); } else { struct sockaddr_in6 buf; @@ -442,7 +443,7 @@ static void socket_update(Socket *s) } else { #ifdef HTTPS_ENABLED if (s->ssl == NULL) { - s->ssl = SSL_new(s->server_secure_context->p); + s->ssl = SSL_new(s->client_secure_context->p); if (s->ssl == NULL) { s->state = SOCKET_STATE_DIED; s->events = 0; @@ -667,7 +668,7 @@ static int socket_manager_register_events_nolock( // be processed immediately. for (int i = 0, j = 0; j < sm->num_used; i++) { Socket *s = &sm->sockets[i]; - if (s->state = SOCKET_STATE_FREE) + if (s->state == SOCKET_STATE_FREE) continue; j++; @@ -760,6 +761,10 @@ static int socket_manager_translate_events_nolock( s->user = NULL; #ifdef HTTPS_ENABLED s->ssl = NULL; + s->server_secure_context = NULL; + s->client_secure_context = NULL; + if (secure) + &s->server_secure_context = sm->server_secure_context; #endif socket_update(s); @@ -889,7 +894,7 @@ static int resolve_connect_targets(ConnectTarget *targets, for (struct addrinfo *rp = res; rp; rp = rp->ai_next) { if (rp->ai_family == AF_INET) { - IPv4 ipv4 = *(IPv4*) &((struct sockaddr_in*)rp->ai_addr)->sin_addr; + HTTP_IPv4 ipv4 = *(HTTP_IPv4*) &((struct sockaddr_in*)rp->ai_addr)->sin_addr; if (num_resolved < max_resolved) { resolved[num_resolved].is_ipv4 = true; resolved[num_resolved].ipv4 = ipv4; @@ -901,7 +906,7 @@ static int resolve_connect_targets(ConnectTarget *targets, num_resolved++; } } else if (rp->ai_family == AF_INET6) { - IPv6 ipv6 = *(IPv6*) &((struct sockaddr_in6*)rp->ai_addr)->sin6_addr; + HTTP_IPv6 ipv6 = *(HTTP_IPv6*) &((struct sockaddr_in6*)rp->ai_addr)->sin6_addr; if (num_resolved < max_resolved) { resolved[num_resolved].is_ipv4 = false; resolved[num_resolved].ipv6 = ipv6; @@ -958,7 +963,13 @@ int socket_connect(SocketManager *sm, int num_targets, if (sm->num_used == sm->max_used) return -1; -#ifndef HTTPS_ENABLED +#ifdef HTTPS_ENABLED + if (!sm->at_least_one_secure_connect) { + if (client_secure_context_init(&sm->client_secure_context) < 0) + return -1; + sm->at_least_one_secure_connect = true; + } +#else if (secure) return -1; #endif @@ -994,8 +1005,11 @@ int socket_connect(SocketManager *sm, int num_targets, s->sock = NATIVE_SOCKET_INVALID; s->user = user; #ifdef HTTPS_ENABLED - s->server_secure_context = &sm->server_secure_context; + s->server_secure_context = NULL; + s->client_secure_context = NULL; s->ssl = NULL; + if (secure) + s->client_secure_context = &sm->client_secure_context; #endif sm->num_used++; return 0; @@ -1048,7 +1062,7 @@ static int socket_recv_nolock(SocketManager *sm, SocketHandle handle, } ret = 0; } - return 0; + return ret; } else { #ifdef HTTPS_ENABLED int ret = SSL_read(s->ssl, dst, max); diff --git a/src/socket.h b/src/socket.h index 049d3b1..46d5344 100644 --- a/src/socket.h +++ b/src/socket.h @@ -61,21 +61,8 @@ typedef uint32_t SocketHandle; #define SOCKET_HANDLE_INVALID ((SocketHandle) 0) -typedef struct { - char *ptr; - int len; -} String; - typedef uint16_t Port; -typedef struct { - uint32_t data; -} IPv4; - -typedef struct { - uint16_t data[8]; -} IPv6; - typedef enum { SOCKET_EVENT_READY, SOCKET_EVENT_DISCONNECT, @@ -136,8 +123,8 @@ typedef struct { // Internal use only typedef struct { union { - IPv4 ipv4; - IPv6 ipv6; + HTTP_IPv4 ipv4; + HTTP_IPv6 ipv6; }; bool is_ipv4; Port port; @@ -183,6 +170,7 @@ typedef struct { }; #ifdef HTTPS_ENABLED + ClientSecureContext *client_secure_context; ServerSecureContext *server_secure_context; SSL *ssl; #endif @@ -313,9 +301,9 @@ typedef struct { ConnectTargetType type; Port port; union { - IPv4 ipv4; - IPv6 ipv6; - String name; + HTTP_IPv4 ipv4; + HTTP_IPv6 ipv6; + HTTP_String name; }; } ConnectTarget;