diff --git a/src/client.c b/src/client.c index 619e8ce..9ce8281 100644 --- a/src/client.c +++ b/src/client.c @@ -16,7 +16,7 @@ int keep_alive = 1; char *client_addr_str, *client_addr_str_ptr, *server_addr_str, *server_addr_str_ptr, - *log_conn_prefix, *log_req_prefix; + *log_client_prefix, *log_conn_prefix, *log_req_prefix; struct timeval client_timeout = {.tv_sec = CLIENT_TIMEOUT, .tv_usec = 0}; @@ -34,6 +34,7 @@ int client_request_handler(sock *client, int req_num) { struct timespec begin, end; int ret; char buf[16]; + char *host; char *msg = "HTTP/1.1 501 Not Implemented\r\n" "Connection: keep-alive\r\n" "Keep-Alive: timeout=3600, max=100\r\n" @@ -58,6 +59,13 @@ int client_request_handler(sock *client, int req_num) { return ret; } + host = http_get_header_field(&req.hdr, "Host"); + sprintf(log_req_prefix, "[%s%24s%s]%s ", (host != NULL) ? BLD_STR : "", (host != NULL) ? host : server_addr_str, + (host != NULL) ? CLR_STR : "", log_client_prefix); + log_prefix = log_req_prefix; + + print(BLD_STR "%s %s" CLR_STR, req.method, req.uri); + if (client->enc) { SSL_write(client->ssl, msg, (int) strlen(msg)); } else { @@ -68,6 +76,7 @@ int client_request_handler(sock *client, int req_num) { unsigned long micros = (end.tv_nsec - begin.tv_nsec) / 1000 + (end.tv_sec - begin.tv_sec) * 1000000; print(ERR_STR "501 Not Implemented (%s)" CLR_STR, format_duration(micros, buf)); + http_free_req(&req); return 0; } @@ -151,10 +160,14 @@ int client_handler(sock *client, long client_num, struct sockaddr_in6 *client_ad server_addr_str = server_addr_str_ptr; } + log_req_prefix = malloc(256); + log_client_prefix = malloc(256); + sprintf(log_client_prefix, "[%s%4i%s]%s[%*s][%5i]%s", client->enc ? HTTPS_STR : HTTP_STR, + ntohs(server_addr->sin6_port), CLR_STR, color_table[client_num % 6], INET_ADDRSTRLEN, client_addr_str, + ntohs(client_addr->sin6_port), CLR_STR); + log_conn_prefix = malloc(256); - sprintf(log_conn_prefix, "[%24s][%s%4i%s]%s[%*s][%5i]%s ", - server_addr_str, client->enc ? HTTPS_STR : HTTP_STR, ntohs(server_addr->sin6_port), CLR_STR, - color_table[client_num % 6], INET_ADDRSTRLEN, client_addr_str, ntohs(client_addr->sin6_port), CLR_STR); + sprintf(log_conn_prefix, "[%24s]%s ", server_addr_str, log_client_prefix); log_prefix = log_conn_prefix; ret = client_connection_handler(client); @@ -162,5 +175,6 @@ int client_handler(sock *client, long client_num, struct sockaddr_in6 *client_ad free(server_addr_str_ptr); free(log_conn_prefix); free(log_req_prefix); + free(log_client_prefix); return ret; } diff --git a/src/necronda-server.h b/src/necronda-server.h index d24243b..c096b66 100644 --- a/src/necronda-server.h +++ b/src/necronda-server.h @@ -28,6 +28,7 @@ #define ERR_STR "\x1B[1;31m" #define CLR_STR "\x1B[0m" +#define BLD_STR "\x1B[1m" #define HTTP_STR "\x1B[1;31m" #define HTTPS_STR "\x1B[1;32m" diff --git a/src/net/http.c b/src/net/http.c index ce10a9e..d11a864 100644 --- a/src/net/http.c +++ b/src/net/http.c @@ -9,27 +9,151 @@ #include "utils.h" +void http_to_camel_case(char *str) { + char last = '-'; + char ch; + for (int i = 0; i < strlen(str); i++) { + ch = str[i]; + if (last == '-' && ch >= 'a' && ch <= 'z') { + str[i] = (char) ((int) ch & 0x5F); + } else if (last != '-' && ch >= 'A' && ch <= 'Z') { + str[i] = (char) ((int) ch | 0x20); + } + last = str[i]; + } +} + + +void http_free_hdr(http_hdr *hdr) { + for (int i = 0; i < hdr->field_num; i++) { + free(hdr->fields[i][0]); + free(hdr->fields[i][1]); + } +} + +void http_free_req(http_req *req) { + free(req->uri); + http_free_hdr(&req->hdr); +} + int http_receive_request(sock *client, http_req *req) { + ssize_t rcv_len, len; + char *ptr, *pos0, *pos1, *pos2; char *buf = malloc(CLIENT_MAX_HEADER_SIZE); - ssize_t len; - memset(buf, 0, CLIENT_MAX_HEADER_SIZE); + memset(buf, 0, sizeof(&buf)); + memset(req->method, 0, sizeof(req->method)); + memset(req->version, 0, sizeof(req->version)); + req->uri = NULL; + req->hdr.field_num = 0; while (1) { if (client->enc) { - len = SSL_read(client->ssl, buf, CLIENT_MAX_HEADER_SIZE); - if (len < 0) { - print(ERR_STR "Unable to receive: %s" CLR_STR, ssl_get_error(client->ssl, len)); + rcv_len = SSL_read(client->ssl, buf, CLIENT_MAX_HEADER_SIZE); + if (rcv_len < 0) { + print(ERR_STR "Unable to receive: %s" CLR_STR, ssl_get_error(client->ssl, rcv_len)); continue; } } else { - len = recv(client->socket, buf, CLIENT_MAX_HEADER_SIZE, 0); - if (len < 0) { + rcv_len = recv(client->socket, buf, CLIENT_MAX_HEADER_SIZE, 0); + if (rcv_len < 0) { print(ERR_STR "Unable to receive: %s" CLR_STR, strerror(errno)); continue; } } - break; - } - return 0; + ptr = buf; + while (1) { + pos0 = memchr(ptr, '\r', rcv_len - (ptr - buf)); + if (pos0 == NULL || pos0[1] != '\n') { + print(ERR_STR "Unable to parse header: Invalid header format" CLR_STR); + free(buf); + return -1; + } else if (pos0[2] == '\r' && pos0[3] == '\n') { + free(buf); + return 0; + } + + if (ptr == buf) { + if (memcmp(ptr, "GET ", 4) == 0) { + sprintf(req->method, "GET"); + } else if (memcmp(ptr, "HEAD ", 5) == 0) { + sprintf(req->method, "HEAD"); + } else if (memcmp(ptr, "POST ", 5) == 0) { + sprintf(req->method, "POST"); + } else if (memcmp(ptr, "PUT ", 4) == 0) { + sprintf(req->method, "PUT"); + } else if (memcmp(ptr, "DELETE ", 7) == 0) { + sprintf(req->method, "DELETE"); + } else if (memcmp(ptr, "CONNECT ", 7) == 0) { + sprintf(req->method, "CONNECT"); + } else if (memcmp(ptr, "OPTIONS ", 7) == 0) { + sprintf(req->method, "OPTIONS"); + } else if (memcmp(ptr, "TRACE ", 6) == 0) { + sprintf(req->method, "TRACE"); + } else { + print(ERR_STR "Unable to parse header: Invalid method" CLR_STR); + free(buf); + return -1; + } + + pos1 = memchr(ptr, ' ', rcv_len - (ptr - buf)) + 1; + if (pos1 == NULL) goto err_hdr_fmt; + pos2 = memchr(pos1, ' ', rcv_len - (pos1 - buf)) + 1; + if (pos2 == NULL) { + err_hdr_fmt: + print(ERR_STR "Unable to parse header: Invalid header format" CLR_STR); + free(buf); + return -1; + } + + if (memcmp(pos2, "HTTP/", 5) != 0 || memcmp(pos2 + 8, "\r\n", 2) != 0) { + print(ERR_STR "Unable to parse header: Invalid version" CLR_STR); + free(buf); + return -1; + } + + len = pos2 - pos1 - 1; + req->uri = malloc(len + 1); + sprintf(req->uri, "%.*s", (int) len, pos1); + sprintf(req->version, "%.3s", pos2 + 5); + } else { + pos1 = memchr(ptr, ':', pos0 - ptr); + if (pos1 == NULL) { + print(ERR_STR "Unable to parse header: Invalid version" CLR_STR); + free(buf); + return -1; + } + + len = pos1 - ptr; + req->hdr.fields[req->hdr.field_num][0] = malloc(len + 1); + sprintf(req->hdr.fields[req->hdr.field_num][0], "%.*s", (int) len, ptr); + http_to_camel_case(req->hdr.fields[req->hdr.field_num][0]); + + pos1++; + pos2 = pos0 - 1; + while (pos1[0] == ' ') pos1++; + while (pos2[0] == ' ') pos2--; + len = pos2 - pos1 + 1; + req->hdr.fields[req->hdr.field_num][1] = malloc(len + 1); + sprintf(req->hdr.fields[req->hdr.field_num][1], "%.*s", (int) len, pos1); + + req->hdr.field_num++; + } + + ptr = pos0 + 2; + } + } +} + +char *http_get_header_field(http_hdr *hdr, const char *field_name) { + size_t len = strlen(field_name); + char *_field_name = malloc(len + 1); + sprintf(_field_name, "%s", field_name); + http_to_camel_case(_field_name); + for (int i = 0; i < hdr->field_num; i++) { + if (strncmp(hdr->fields[i][0], _field_name, len) == 0) { + return hdr->fields[i][1]; + } + } + return NULL; } diff --git a/src/net/http.h b/src/net/http.h index 8a9c87d..decfa71 100644 --- a/src/net/http.h +++ b/src/net/http.h @@ -8,12 +8,16 @@ #ifndef NECRONDA_SERVER_HTTP_H #define NECRONDA_SERVER_HTTP_H +typedef struct { + char field_num; + char *fields[64][2]; +} http_hdr; + typedef struct { char method[8]; char *uri; char version[3]; - char field_num; - char *fields[64][2]; + http_hdr hdr; } http_req; int http_receive_request(sock *client, http_req *req);