Add HTTPS support

This commit is contained in:
2025-11-21 19:52:28 +01:00
parent 3f76d1827e
commit ccdd573e90
8 changed files with 857 additions and 83 deletions
+202 -18
View File
@@ -189,8 +189,8 @@ int socket_manager_listen_tcp(SocketManager *sm,
}
int socket_manager_listen_tls(SocketManager *sm,
HTTP_String addr, Port port, HTTP_String cert_file_name,
HTTP_String key_file_name)
HTTP_String addr, Port port, HTTP_String cert_file,
HTTP_String key_file)
{
if (sm->secure_sock != NATIVE_SOCKET_INVALID)
return -1;
@@ -201,7 +201,8 @@ int socket_manager_listen_tls(SocketManager *sm,
if (sm->secure_sock == NATIVE_SOCKET_INVALID)
return -1;
if (server_secure_context_init(&sm->server_secure_context) < 0) {
if (server_secure_context_init(&sm->server_secure_context,
cert_file, key_file) < 0) {
CLOSE_NATIVE_SOCKET(sm->secure_sock);
sm->secure_sock = NATIVE_SOCKET_INVALID;
return -1;
@@ -227,7 +228,7 @@ int socket_manager_add_certificate(SocketManager *sm,
static bool is_secure(Socket *s)
{
#ifdef HTTPS_ENABLED
return (s->ssl != NULL);
return (s->server_secure_context != NULL);
#else
return false;
#endif
@@ -269,6 +270,24 @@ connect_failed_because_of_peer(void)
return connect_failed_because_of_peer_2(err);
}
static void free_addr_list(AddressAndPort *addrs, int num_addr)
{
#ifdef HTTPS_ENABLED
for (int i = 0; i < num_addr; i++) {
RegisteredName *name = addrs[i].name;
if (name) {
assert(name->refs > 0);
name->refs--;
if (name->refs == 0)
free(name);
}
}
#else
(void) addrs;
(void) num_addr;
#endif
}
// This function moves the socket state machine
// to the next state until an I/O event would
// be required to continue.
@@ -297,6 +316,13 @@ static void socket_update(Socket *s)
if (s->sock != NATIVE_SOCKET_INVALID) {
// This is not the first attempt
#ifdef HTTPS_ENABLED
if (s->ssl) {
SSL_free(s->ssl);
s->ssl = NULL;
}
#endif
CLOSE_NATIVE_SOCKET(s->sock);
s->next_addr++;
@@ -389,18 +415,76 @@ static void socket_update(Socket *s)
case SOCKET_STATE_CONNECTED:
{
// We managed to connect to the peer.
// We can free the target array if it
// was allocated dynamically.
if (s->num_addr > 1)
free(s->addrs);
if (!is_secure(s)) {
// We managed to connect to the peer.
// We can free the target array if it
// was allocated dynamically.
if (s->num_addr > 1)
free(s->addrs);
s->events = 0;
s->state = SOCKET_STATE_ESTABLISHED_READY;
} else {
#ifdef HTTPS_ENABLED
assert(0); // TODO
if (s->ssl == NULL) {
s->ssl = SSL_new(s->server_secure_context->p);
if (s->ssl == NULL) {
s->state = SOCKET_STATE_DIED;
s->events = 0;
break;
}
if (SSL_set_fd(s->ssl, s->sock) != 1) {
s->state = SOCKET_STATE_DIED;
s->events = 0;
break;
}
AddressAndPort addr;
if (s->num_addr > 1)
addr = s->addrs[s->next_addr];
else
addr = s->addr;
if (addr.name)
SSL_set_tlsext_host_name(s->ssl, addr.name->data);
}
int ret = SSL_connect(s->ssl);
if (ret == 1) {
// Handshake done
// We managed to connect to the peer.
// We can free the target array if it
// was allocated dynamically.
if (s->num_addr == 1)
free_addr_list(&s->addr, 1);
else {
assert(s->num_addr > 1);
free_addr_list(s->addrs, s->num_addr);
free(s->addrs);
}
s->state = SOCKET_STATE_ESTABLISHED_READY;
s->events = 0;
break;
}
int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
s->events = POLLIN;
break;
}
if (err == SSL_ERROR_WANT_WRITE) {
s->events = POLLOUT;
break;
}
s->state = SOCKET_STATE_PENDING;
s->events = 0;
again = true;
#endif
}
}
@@ -413,7 +497,45 @@ static void socket_update(Socket *s)
s->events = 0;
} else {
#ifdef HTTPS_ENABLED
assert(0); // TODO
// Start server-side SSL handshake
if (!s->ssl) {
s->ssl = SSL_new(s->server_secure_context->p);
if (s->ssl == NULL) {
s->state = SOCKET_STATE_DIED;
s->events = 0;
break;
}
if (SSL_set_fd(s->ssl, s->sock) != 1) {
s->state = SOCKET_STATE_DIED;
s->events = 0;
break;
}
}
int ret = SSL_accept(s->ssl);
if (ret == 1) {
// Handshake done
s->state = SOCKET_STATE_ESTABLISHED_READY;
s->events = 0;
break;
}
int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
s->events = POLLIN;
break;
}
if (err == SSL_ERROR_WANT_WRITE) {
s->events = POLLOUT;
break;
}
// Server socket error - close the connection
s->state = SOCKET_STATE_DIED;
s->events = 0;
#endif
}
}
@@ -431,7 +553,26 @@ static void socket_update(Socket *s)
s->events = 0;
} else {
#ifdef HTTPS_ENABLED
assert(0); // TODO
int ret = SSL_shutdown(s->ssl);
if (ret == 1) {
s->state = SOCKET_STATE_DIED;
s->events = 0;
break;
}
int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
s->events = POLLIN;
break;
}
if (err == SSL_ERROR_WANT_WRITE) {
s->events = POLLOUT;
break;
}
s->state = SOCKET_STATE_DIED;
s->events = 0;
#endif
}
}
@@ -593,6 +734,9 @@ static int socket_manager_translate_events_nolock(
s->sock = sock;
s->events = 0;
s->user = NULL;
#ifdef HTTPS_ENABLED
s->ssl = NULL;
#endif
socket_update(s);
if (s->state == SOCKET_STATE_DIED) {
@@ -678,23 +822,35 @@ static int resolve_connect_targets(ConnectTarget *targets,
{
char portstr[16];
int len = snprintf(portstr, sizeof(portstr), "%u", targets[i].port);
if (len < 0 || len >= (int) sizeof(portstr))
return -1;
assert(len > 1 && len < (int) sizeof(portstr));
struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
#ifdef HTTPS_ENABLED
RegisteredName *name = malloc(sizeof(RegisteredName) + targets[i].name.len + 1);
if (name == NULL) {
free_addr_list(resolved, num_resolved);
return -1;
}
name->refs = 0;
memcpy(name->data, targets[i].name.ptr, targets[i].name.len);
name->data[targets[i].name.len] = '\0';
char *hostname = name->data;
#else
char hostname[1<<9]; // TODO: Is this a good length?
if (targets[i].name.len >= (int) sizeof(hostname))
return -1;
memcpy(hostname, targets[i].name.ptr, targets[i].name.len);
hostname[targets[i].name.len] = '\0';
#endif
struct addrinfo *res = NULL;
int ret = getaddrinfo(hostname, portstr, &hints, &res);
if (ret != 0)
return -1;
if (ret != 0) {
free_addr_list(resolved, num_resolved);
return -1; // TODO: free all allocated registered names
}
for (struct addrinfo *rp = res; rp; rp = rp->ai_next) {
if (rp->ai_family == AF_INET) {
@@ -703,6 +859,10 @@ static int resolve_connect_targets(ConnectTarget *targets,
resolved[num_resolved].is_ipv4 = true;
resolved[num_resolved].ipv4 = ipv4;
resolved[num_resolved].port = targets[i].port;
#ifdef HTTPS_ENABLED
resolved[num_resolved].name = name;
name->refs++;
#endif
num_resolved++;
}
} else if (rp->ai_family == AF_INET6) {
@@ -711,11 +871,20 @@ static int resolve_connect_targets(ConnectTarget *targets,
resolved[num_resolved].is_ipv4 = false;
resolved[num_resolved].ipv6 = ipv6;
resolved[num_resolved].port = targets[i].port;
#ifdef HTTPS_ENABLED
resolved[num_resolved].name = name;
name->refs++;
#endif
num_resolved++;
}
}
}
#ifdef HTTPS_ENABLED
if (name->refs == 0)
free(name);
#endif
freeaddrinfo(res);
}
break;
@@ -724,6 +893,9 @@ static int resolve_connect_targets(ConnectTarget *targets,
resolved[num_resolved].is_ipv4 = true;
resolved[num_resolved].ipv4 = targets[i].ipv4;
resolved[num_resolved].port = targets[i].port;
#ifdef HTTPS_ENABLED
resolved[num_resolved].name = NULL;
#endif
num_resolved++;
}
break;
@@ -732,6 +904,9 @@ static int resolve_connect_targets(ConnectTarget *targets,
resolved[num_resolved].is_ipv4 = false;
resolved[num_resolved].ipv6 = targets[i].ipv6;
resolved[num_resolved].port = targets[i].port;
#ifdef HTTPS_ENABLED
resolved[num_resolved].name = NULL;
#endif
num_resolved++;
}
break;
@@ -748,6 +923,11 @@ int socket_connect(SocketManager *sm, int num_targets,
if (sm->num_used == sm->max_used)
return -1;
#ifndef HTTPS_ENABLED
if (secure)
return -1;
#endif
AddressAndPort resolved[MAX_CONNECT_TARGETS];
int num_resolved = resolve_connect_targets(
targets, num_targets, resolved, MAX_CONNECT_TARGETS);
@@ -778,6 +958,10 @@ int socket_connect(SocketManager *sm, int num_targets,
s->state = SOCKET_STATE_PENDING;
s->sock = NATIVE_SOCKET_INVALID;
s->user = user;
#ifdef HTTPS_ENABLED
s->server_secure_context = &sm->server_secure_context;
s->ssl = NULL;
#endif
sm->num_used++;
return 0;
}