commit 8ecc334becef5d55c2e804e830902fe7eb02613e
parent 5696a76b5f636da3b52bafbb027454a99bef5041
Author: typable <contact@typable.dev>
Date: Wed, 18 Sep 2024 11:05:01 +0200
feat: Added multi-port/host binding
Diffstat:
M | libhttp.c | | | 201 | ++++++++++++++++++++++++++++++++++++++++++++++++------------------------------- |
M | libhttp.h | | | 25 | ++++++++++++++++++++----- |
2 files changed, 142 insertions(+), 84 deletions(-)
diff --git a/libhttp.c b/libhttp.c
@@ -13,13 +13,14 @@
#include "libhttp.h"
-#define MAX_REQUEST_HEAD_SIZE 40960
+#define HTTP_MAX_REQUEST_HEAD_SIZE 1000000
-int http_sockfd;
+http_bind_t *http_binds = NULL;
+int http_binds_len = 0;
pthread_t http_thread;
bool http_quit = false;
bool http_error = false;
-SSL_CTX *http_ctx = NULL;
+SSL_CTX *http_ssl_ctx = NULL;
http_response_t (*on_request_fn)(http_request_t *) = NULL;
@@ -168,14 +169,14 @@ char *http_status_str(http_status_t status) {
}
}
-ssize_t http_read(conn_t *conn, void *buffer, size_t len) {
+ssize_t http_read(http_conn_t *conn, void *buffer, size_t len) {
if (conn->ssl != NULL) {
return SSL_read(conn->ssl, buffer, len);
}
return read(conn->sockfd, buffer, len);
}
-ssize_t http_write(conn_t *conn, void *buffer, size_t len) {
+ssize_t http_write(http_conn_t *conn, void *buffer, size_t len) {
if (conn->ssl != NULL) {
return SSL_write(conn->ssl, buffer, len);
}
@@ -298,11 +299,11 @@ void http_response_free(http_response_t *response) {
}
}
-http_request_t http_read_request(conn_t *conn) {
+http_request_t http_read_request(http_conn_t *conn) {
int len = 0;
int ln = 0;
int start = 0;
- char buffer[MAX_REQUEST_HEAD_SIZE];
+ char buffer[HTTP_MAX_REQUEST_HEAD_SIZE];
http_request_t request = {
.conn = conn,
.method = NULL,
@@ -321,7 +322,7 @@ http_request_t http_read_request(conn_t *conn) {
break;
}
// break if too long
- if (len == MAX_REQUEST_HEAD_SIZE) {
+ if (len == HTTP_MAX_REQUEST_HEAD_SIZE) {
perror("request is too long");
break;
}
@@ -413,7 +414,7 @@ http_request_t http_read_request(conn_t *conn) {
return request;
}
-void http_write_response(conn_t *conn, http_response_t response) {
+void http_write_response(http_conn_t *conn, http_response_t response) {
http_write(conn, response.version, strlen(response.version));
http_write(conn, " ", 1);
char status[4];
@@ -442,27 +443,17 @@ void *http_handle_client(void *ptr) {
if (!ptr) {
pthread_exit(0);
}
- conn_t *conn = (conn_t *) ptr;
- if (http_ctx != NULL) {
- conn->ssl = SSL_new(http_ctx);
+ http_conn_t *conn = (http_conn_t *) ptr;
+ if (conn->bind->hosts_len > 0) {
+ SSL_CTX_set_tlsext_servername_arg(http_ssl_ctx, (void *) conn);
+ conn->ssl = SSL_new(http_ssl_ctx);
SSL_set_fd(conn->ssl, conn->sockfd);
if (SSL_accept(conn->ssl) <= 0) {
- perror("cannot SSL handshake");
pthread_exit(0);
}
}
http_request_t request = http_read_request(conn);
if (request.method != NULL && request.url != NULL) {
- // long addr = ((struct sockaddr_in *) &request.conn->addr)->sin_addr.s_addr;
- // printf(
- // "%ld.%ld.%ld.%ld -> %s %s\n",
- // (addr ) & 0xff,
- // (addr >> 8) & 0xff,
- // (addr >> 16) & 0xff,
- // (addr >> 24) & 0xff,
- // request.method,
- // request.url
- // );
if (on_request_fn != NULL) {
http_response_t response = on_request_fn(&request);
http_write_response(conn, response);
@@ -470,7 +461,7 @@ void *http_handle_client(void *ptr) {
}
}
http_request_free(&request);
- if (http_ctx != NULL) {
+ if (conn->ssl != NULL) {
SSL_shutdown(conn->ssl);
SSL_free(conn->ssl);
}
@@ -480,68 +471,67 @@ void *http_handle_client(void *ptr) {
}
void *http_handle_server(void *ptr) {
- if (!ptr) {
- pthread_exit(0);
+ (void)(ptr);
+ struct pollfd fds[http_binds_len];
+ for (int i = 0; i < http_binds_len; i++) {
+ fds[i].fd = http_binds[i].sockfd;
+ fds[i].events = POLLIN | POLLPRI;
}
- int sockfd = *((int *) ptr);
- struct pollfd fds[1];
- fds[0].fd = sockfd;
- fds[0].events = POLLIN | POLLPRI;
while (!http_quit) {
struct timespec sleep_req, sleep_rem;
sleep_req.tv_sec = 0;
sleep_req.tv_nsec = 100000000;
nanosleep(&sleep_req, &sleep_rem);
- if (poll(fds, 1, 100)) {
- conn_t *conn = malloc(sizeof(conn_t));
- conn->addr_len = sizeof(conn->addr);
- conn->sockfd = accept(sockfd, &conn->addr, (socklen_t *) &conn->addr_len);
- conn->ssl = NULL;
- if (conn->sockfd <= 0) {
- free(conn);
- }
- else {
- pthread_t thread;
- pthread_attr_t thread_attr;
- pthread_attr_init(&thread_attr);
- pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
- pthread_create(&thread, &thread_attr, http_handle_client, (void *) conn);
- pthread_attr_destroy(&thread_attr);
+ if (poll(fds, http_binds_len, 100)) {
+ for (int i = 0; i < http_binds_len; i++) {
+ if (fds[i].revents & POLLIN) {
+ http_conn_t *conn = malloc(sizeof(http_conn_t));
+ conn->addr_len = sizeof(conn->addr);
+ conn->sockfd = accept(http_binds[i].sockfd, &conn->addr, (socklen_t *) &conn->addr_len);
+ conn->bind = &http_binds[i];
+ conn->ssl = NULL;
+ if (conn->sockfd <= 0) {
+ free(conn);
+ }
+ else {
+ pthread_t thread;
+ pthread_attr_t thread_attr;
+ pthread_attr_init(&thread_attr);
+ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+ pthread_create(&thread, &thread_attr, http_handle_client, (void *) conn);
+ pthread_attr_destroy(&thread_attr);
+ }
+ }
}
}
}
pthread_exit(0);
}
-void http_tls_enable(char *certificate, char *private_key) {
- const SSL_METHOD *method = TLS_server_method();
- http_ctx = SSL_CTX_new(method);
- if (!http_ctx) {
- perror("cannot create SSL context");
- errno = HTTP_ERR_NO_SSL_CONTEXT;
- http_error = true;
- return;
- }
- if (SSL_CTX_use_certificate_file(http_ctx, certificate, SSL_FILETYPE_PEM) <= 0) {
- perror("cannot use certificate file");
- errno = HTTP_ERR_INVALID_CERTIFICATE;
- http_error = true;
- }
- if (SSL_CTX_use_PrivateKey_file(http_ctx, private_key, SSL_FILETYPE_PEM) <= 0) {
- perror("cannot use private key file");
- errno = HTTP_ERR_INVALID_PRIVATE_KEY;
- http_error = true;
+int http_tls_sni_callback(SSL *ssl, int *al, void *arg) {
+ (void)(al);
+ http_conn_t *conn = (http_conn_t *) arg;
+ const char *hostname = SSL_get_servername(ssl, 0);
+ if (hostname != NULL) {
+ for (int i = 0; i < http_binds_len; i++) {
+ for (int j = 0; j < http_binds[i].hosts_len; j++) {
+ http_host_t *http_host = &http_binds[i].hosts[j];
+ if (strcmp(http_host->hostname, hostname) == 0) {
+ conn->curr_host = http_host;
+ SSL_set_SSL_CTX(ssl, http_host->ssl_ctx);
+ return SSL_TLSEXT_ERR_OK;
+ }
+ }
+ }
}
+ return SSL_TLSEXT_ERR_NOACK;
}
-void http_listen(char *hostname, int port) {
- signal(SIGPIPE, SIG_IGN);
- if (http_error) {
- return;
- }
- errno = 0;
- http_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (http_sockfd <= 0) {
+void http_bind(char *hostname, int port, http_host_t *hosts, int hosts_len) {
+ http_binds = realloc(http_binds, sizeof(http_bind_t) * (http_binds_len + 1));
+ http_bind_t *http_bind = &http_binds[http_binds_len];
+ http_bind->sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (http_bind->sockfd <= 0) {
perror("cannot create socket");
errno = HTTP_ERR_NO_SOCKET;
http_error = true;
@@ -559,34 +549,87 @@ void http_listen(char *hostname, int port) {
memcpy(&addr.sin_addr, host->h_addr_list[0], host->h_length);
addr.sin_port = htons(port);
int enable = 1;
- if (setsockopt(http_sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) {
+ if (setsockopt(http_bind->sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) {
perror("cannot reuse address");
errno = HTTP_ERR_NO_ADDR_REUSE;
http_error = true;
return;
}
- if (bind(http_sockfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) < 0) {
+ if (bind(http_bind->sockfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) < 0) {
perror("cannot bind socket to port");
errno = HTTP_ERR_ADDR_IN_USE;
http_error = true;
return;
}
- if (listen(http_sockfd, 5) < 0) {
+ if (listen(http_bind->sockfd, 5) < 0) {
perror("cannot listen on port");
errno = HTTP_ERR_NO_LISTEN;
http_error = true;
return;
}
- pthread_create(&http_thread, 0, http_handle_server, (void *) &http_sockfd);
+ for (int i = 0; i < hosts_len; i++) {
+ http_host_t *http_host = &hosts[i];
+ if (http_host->certificate != NULL && http_host->private_key != NULL) {
+ const SSL_METHOD *method = TLS_server_method();
+ http_host->ssl_ctx = SSL_CTX_new(method);
+ if (!http_host->ssl_ctx) {
+ perror("cannot create SSL context");
+ errno = HTTP_ERR_NO_SSL_CONTEXT;
+ http_error = true;
+ return;
+ }
+ if (SSL_CTX_use_certificate_file(http_host->ssl_ctx, http_host->certificate, SSL_FILETYPE_PEM) <= 0) {
+ perror("cannot use certificate file");
+ errno = HTTP_ERR_INVALID_CERTIFICATE;
+ http_error = true;
+ }
+ if (SSL_CTX_use_PrivateKey_file(http_host->ssl_ctx, http_host->private_key, SSL_FILETYPE_PEM) <= 0) {
+ perror("cannot use private key file");
+ errno = HTTP_ERR_INVALID_PRIVATE_KEY;
+ http_error = true;
+ }
+ }
+ else {
+ http_host->ssl_ctx = NULL;
+ }
+ }
+ http_bind->hosts = hosts;
+ http_bind->hosts_len = hosts_len;
+ http_binds_len++;
}
-void http_close() {
+void http_listen(void) {
+ signal(SIGPIPE, SIG_IGN);
+ if (http_error) {
+ return;
+ }
+ errno = 0;
+ const SSL_METHOD *method = TLS_server_method();
+ http_ssl_ctx = SSL_CTX_new(method);
+ if (!http_ssl_ctx) {
+ perror("cannot create SSL context");
+ errno = HTTP_ERR_NO_SSL_CONTEXT;
+ http_error = true;
+ return;
+ }
+ SSL_CTX_set_tlsext_servername_callback(http_ssl_ctx, http_tls_sni_callback);
+ pthread_create(&http_thread, 0, http_handle_server, NULL);
+}
+
+void http_close(void) {
http_quit = true;
if (!http_error) {
pthread_join(http_thread, NULL);
}
- close(http_sockfd);
- if (http_ctx != NULL) {
- SSL_CTX_free(http_ctx);
+ for (int i = 0; i < http_binds_len; i++) {
+ close(http_binds[i].sockfd);
+ for (int j = 0; j < http_binds[i].hosts_len; j++) {
+ if (http_binds[i].hosts[j].ssl_ctx != NULL) {
+ SSL_CTX_free(http_binds[i].hosts[j].ssl_ctx);
+ }
+ }
+ }
+ if (http_ssl_ctx != NULL) {
+ free(http_ssl_ctx);
}
}
diff --git a/libhttp.h b/libhttp.h
@@ -79,11 +79,26 @@ typedef enum {
} http_status_t;
typedef struct {
+ char *hostname;
+ char *certificate;
+ char *private_key;
+ SSL_CTX *ssl_ctx;
+} http_host_t;
+
+typedef struct {
+ int sockfd;
+ http_host_t *hosts;
+ int hosts_len;
+} http_bind_t;
+
+typedef struct {
int sockfd;
struct sockaddr addr;
int addr_len;
SSL *ssl;
-} conn_t;
+ http_bind_t *bind;
+ http_host_t *curr_host;
+} http_conn_t;
typedef struct {
char *key;
@@ -91,7 +106,7 @@ typedef struct {
} http_header_t;
typedef struct {
- conn_t *conn;
+ http_conn_t *conn;
char *method;
char *url;
char *version;
@@ -120,6 +135,6 @@ http_response_t http_response_create(http_status_t status);
void http_response_body(http_response_t *response, char *body, long len);
void http_response_file(http_response_t *response, char *filename);
-void http_tls_enable(char *certificate, char *private_key);
-void http_listen(char *hostname, int port);
-void http_close();
+void http_bind(char *hostname, int port, http_host_t *hosts, int hosts_len);
+void http_listen(void);
+void http_close(void);