From 59a134f2d68a6b27e8bd4e2c31794bd8f2dad6e8 Mon Sep 17 00:00:00 2001 From: moon Date: Sat, 13 Sep 2025 00:51:54 +0330 Subject: new things --- omen.c | 226 +++++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 170 insertions(+), 56 deletions(-) (limited to 'omen.c') diff --git a/omen.c b/omen.c index 2f60a8c..98da86a 100644 --- a/omen.c +++ b/omen.c @@ -14,29 +14,30 @@ #include // #include "common.c" +#include "ratelimit.c" typedef uint8_t byte; typedef void (*tome_ptr)( char *ip_addr, size_t addr_size, byte *in, size_t in_len, byte **out, size_t *out_len); -struct Task { - int fd; - struct Task *next; +struct server_task { + int fd; + struct server_task *next; }; #define MAX_BUFFER 1 << 18 -#define MAX_BODY 1 << 17 +#define MAX_BODY 1 << 22 #define MAX_CONNECTIONS 2048 // vars -int worker_count = 16; +int worker_count = 64; -pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER; -pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER; -struct Task *head = NULL; -struct Task *tail = NULL; -size_t queue_size = 0; -tome_ptr global_handler = NULL; +pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER; +struct server_task *head = NULL; +struct server_task *tail = NULL; +size_t queue_size = 0; +tome_ptr global_handler = NULL; // utils @@ -56,11 +57,15 @@ static ssize_t write_all(int fd, const void *buf, size_t len) { } static int read_until_headers(int fd, char **out_buf, size_t *out_len) { + size_t total_tries = 0; + size_t cap = 4096; size_t len = 0; char *buf = malloc(cap); if (!buf) return -1; while (1) { + if (total_tries++ > 128) break; + if (cap - len < 1024) { size_t ncap = cap * 2; char *n = realloc(buf, ncap); @@ -81,7 +86,18 @@ static int read_until_headers(int fd, char **out_buf, size_t *out_len) { break; } len += (size_t) r; - if (len >= 4 && memmem(buf, len, "\r\n\r\n", 4) != NULL) break; + if (len >= 4 && memmem(buf, len, "\r\n\r\n", 4) != NULL) { + if (len >= 19 && memmem(buf, len, "Transfer-Encoding: chunked", 19) != NULL) { + const char *err = "HTTP/1.1 501 Not Implemented\r\n" + "Content-Length: 31\r\n" + "Connection: close\r\n\r\n" + "chunked encoding not supported"; + write_all(fd, err, strlen(err)); + free(buf); + return -1; + } + break; + } if (len > MAX_BUFFER) break; } *out_buf = buf; @@ -153,14 +169,14 @@ static int send_http_response(int fd, const byte *payload, size_t payload_len) { void *omen_bound(void *arg) { (void) arg; while (1) { - int client_fd = -1; - struct sockaddr_in client_addr; - socklen_t client_addr_len = sizeof(client_addr); + int client_fd = -1; + struct sockaddr_storage client_addr; + socklen_t client_addr_len = sizeof(client_addr); pthread_mutex_lock(&queue_mutex); while (head == NULL) { pthread_cond_wait(&queue_cond, &queue_mutex); } - struct Task *task = head; - head = head->next; + struct server_task *task = head; + head = head->next; if (head == NULL) tail = NULL; queue_size--; client_fd = task->fd; @@ -172,9 +188,36 @@ void *omen_bound(void *arg) { free(task); pthread_mutex_unlock(&queue_mutex); - char client_ip[ INET_ADDRSTRLEN ] = { 0 }; - if (client_addr.sin_family == AF_INET) { - inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN); + char client_ip[ INET6_ADDRSTRLEN ] = { 0 }; + if (client_addr.ss_family == AF_INET) { + struct sockaddr_in *addr4 = (struct sockaddr_in *) &client_addr; + inet_ntop(AF_INET, &addr4->sin_addr, client_ip, INET_ADDRSTRLEN); + } else if (client_addr.ss_family == AF_INET6) { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &client_addr; + inet_ntop(AF_INET6, &addr6->sin6_addr, client_ip, INET6_ADDRSTRLEN); + } + + if (!ratelimit_check(client_ip)) { + usleep(250 * 1000); + + struct server_task *new_task = malloc(sizeof(struct server_task)); + if (new_task) { + new_task->fd = client_fd; + new_task->next = NULL; + + pthread_mutex_lock(&queue_mutex); + if (tail) tail->next = new_task; + else + head = new_task; + tail = new_task; + queue_size++; + pthread_cond_signal(&queue_cond); + pthread_mutex_unlock(&queue_mutex); + } else { + close(client_fd); + } + + continue; } struct timeval timeout = { 5, 0 }; @@ -264,19 +307,39 @@ int omen(int port, tome_ptr handler) { global_handler = handler; signal(SIGPIPE, SIG_IGN); - int server_fd = socket(AF_INET, SOCK_STREAM, 0); - if (server_fd < 0) return -1; + int server_fd = socket(AF_INET6, SOCK_STREAM, 0); + if (server_fd < 0) { + perror("failed to create IPv6 socket"); + return -1; + } + + int opt = 0; + if (setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) < 0) { + perror("failed to disable IPV6_V6ONLY"); + close(server_fd); + return -1; + } + + opt = 1; + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { + perror("failed to set SO_REUSEADDR"); + close(server_fd); + return -1; + } - int opt = 1; - setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + struct sockaddr_in6 addr = { 0 }; + addr.sin6_family = AF_INET6; + addr.sin6_addr = in6addr_any; + addr.sin6_port = htons(port); - struct sockaddr_in addr = { 0 }; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = htons(port); + if (bind(server_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind failed"); + close(server_fd); + return -1; + } - if (bind(server_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0 - || listen(server_fd, MAX_CONNECTIONS) < 0) { + if (listen(server_fd, MAX_CONNECTIONS) < 0) { + perror("Listen failed"); close(server_fd); return -1; } @@ -290,33 +353,37 @@ int omen(int port, tome_ptr handler) { for (int i = 0; i < worker_count; i++) { if (pthread_create(&threads[ i ], NULL, omen_bound, NULL) != 0) { perror("pthread_create failed"); - } else { - pthread_detach(threads[ i ]); + free(threads); + close(server_fd); + return -1; } + pthread_detach(threads[ i ]); } while (1) { - struct sockaddr_in client_addr; - socklen_t len = sizeof(client_addr); - int client_fd = accept(server_fd, (struct sockaddr *) &client_addr, &len); - if (client_fd < 0) continue; + struct sockaddr_storage client_addr; + socklen_t len = sizeof(client_addr); + int client_fd = accept(server_fd, (struct sockaddr *) &client_addr, &len); + if (client_fd < 0) { + if (errno != EINTR) { perror("Accept failed"); } + continue; + } + + printf("request\n"); pthread_mutex_lock(&queue_mutex); if (queue_size >= (size_t) MAX_CONNECTIONS) { - // note: The server converts into a teapot if the number of concurrent connections - // reaches the max amount. this can be mitigated by disconnecting some clients, - // which will turn the teapot back to an actual server. char err_resp[] = "HTTP/1.1 418 I'm a teapot\r\n" "Content-Length: 24\r\n" "Connection: close\r\n\r\n" "Max connections reached"; - write_all(client_fd, err_resp, 92); + write_all(client_fd, err_resp, strlen(err_resp)); close(client_fd); pthread_mutex_unlock(&queue_mutex); continue; } - struct Task *new_task = malloc(sizeof(struct Task)); + struct server_task *new_task = malloc(sizeof(struct server_task)); if (!new_task) { close(client_fd); pthread_mutex_unlock(&queue_mutex); @@ -333,6 +400,7 @@ int omen(int port, tome_ptr handler) { pthread_mutex_unlock(&queue_mutex); } + free(threads); close(server_fd); return 0; } @@ -346,41 +414,82 @@ int study( size_t data_len, byte **response, size_t *resp_len) { - int sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) return -1; + bool is_ipv6 = strchr(ip, ':') != NULL; + int domain = is_ipv6 ? AF_INET6 : AF_INET; + printf("Attempting %s connection to %s:%d\n", is_ipv6 ? "IPv6" : "IPv4", ip, port); - struct sockaddr_in addr = { 0 }; - addr.sin_family = AF_INET; - if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1) { + int sock = socket(domain, SOCK_STREAM, 0); + if (sock < 0) { + printf("Socket creation failed: %s\n", strerror(errno)); + return -1; + } + + struct sockaddr_storage addr = { 0 }; + addr.ss_family = domain; + void *addr_ptr = NULL; + size_t addr_len = 0; + if (domain == AF_INET) { + struct sockaddr_in *addr4 = (struct sockaddr_in *) &addr; + addr_ptr = &addr4->sin_addr; + addr_len = sizeof(struct sockaddr_in); + addr4->sin_port = htons(port); + } else { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &addr; + addr_ptr = &addr6->sin6_addr; + addr_len = sizeof(struct sockaddr_in6); + addr6->sin6_port = htons(port); + } + + if (inet_pton(domain, ip, addr_ptr) != 1) { + printf("Invalid %s address: %s\n", is_ipv6 ? "IPv6" : "IPv4", ip); close(sock); return -1; } - addr.sin_port = htons(port); struct timeval timeout = { 5, 0 }; - setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); - setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0 + || setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) { + printf("Setsockopt failed: %s\n", strerror(errno)); + close(sock); + return -1; + } - if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + printf("Connecting to %s:%d\n", ip, port); + if (connect(sock, (struct sockaddr *) &addr, addr_len) < 0) { + printf("Connect failed: %s\n", strerror(errno)); close(sock); return -1; } if (data_len > MAX_BODY) data_len = MAX_BODY; char req_hdr[ 256 ]; - int hdr_n = snprintf( - req_hdr, - sizeof(req_hdr), - "POST /data HTTP/1.1\r\nHost: %s\r\nContent-Length: %zu\r\nConnection: close\r\n\r\n", - ip, - data_len); + // Use [ ] for IPv6 in Host header + int hdr_n; + if (is_ipv6) { + hdr_n = snprintf( + req_hdr, + sizeof(req_hdr), + "POST /data HTTP/1.1\r\nHost: [%s]\r\nContent-Length: %zu\r\nConnection: close\r\n\r\n", + ip, + data_len); + } else { + hdr_n = snprintf( + req_hdr, + sizeof(req_hdr), + "POST /data HTTP/1.1\r\nHost: %s\r\nContent-Length: %zu\r\nConnection: close\r\n\r\n", + ip, + data_len); + } if (hdr_n < 0 || (size_t) hdr_n >= sizeof(req_hdr)) { + printf("Failed to format HTTP header\n"); close(sock); return -1; } + printf("Sending request: %.*s\n", hdr_n, req_hdr); if (write_all(sock, req_hdr, (size_t) hdr_n) < 0 || (data_len > 0 && write_all(sock, data, data_len) < 0)) { + printf("Write failed: %s\n", strerror(errno)); close(sock); return -1; } @@ -388,12 +497,14 @@ int study( char *hdr_buf = NULL; size_t hdr_len = 0; if (read_until_headers(sock, &hdr_buf, &hdr_len) < 0) { + printf("Failed to read headers: %s\n", strerror(errno)); close(sock); free(hdr_buf); return -1; } if (memmem(hdr_buf, hdr_len, "HTTP/1.1 418 I'm a teapot", 34) != NULL) { + printf("Server responded with 418 I'm a teapot\n"); close(sock); free(hdr_buf); return -2; @@ -410,6 +521,7 @@ int study( if ((size_t) cl > MAX_BODY) cl = MAX_BODY; body = malloc((size_t) cl); if (!body) { + printf("Failed to allocate body buffer\n"); free(hdr_buf); close(sock); return -1; @@ -422,6 +534,7 @@ int study( } if ((size_t) body_len < (size_t) cl) { if (read_exact(sock, body + body_len, (size_t) cl - body_len) < 0) { + printf("Failed to read body: %s\n", strerror(errno)); free(body); free(hdr_buf); close(sock); @@ -463,5 +576,6 @@ int study( *resp_len = 0; free(body); } + printf("Response received, length: %zu\n", body_len); return 0; } \ No newline at end of file -- cgit v1.2.3