/*** * _____ * / ____| * | (___ ___ _ ____ _____ _ __ * \___ \ / _ \ '__\ \ / / _ \ '__| * ____) | __/ | \ V / __/ | * |_____/ \___|_| \_/ \___|_| * * */ #include "server.h" #include "tls/tls.h" #include "tls/tls_parser.h" #include "common.h" #include #include #include #include #include #include #include #include #include #include "logger.h" #include "http/http_parser.h" #define RAWHTTPS_SERVER_MAX_QUEUE_SERVER_PENDING_CONNECTIONS 5 typedef struct { rawhttps_server* server; struct sockaddr_in client_address; int connected_socket; } rawhttps_connection; int rawhttps_server_init(rawhttps_server* server, int port, const char* certificate_path, const char* private_key_path) { if (rawhttps_handler_tree_create(&server->handlers, 16 /* change me */)) return -1; server->sockfd = socket(AF_INET, SOCK_STREAM, 0); if (server->sockfd == -1) { rawhttps_logger_log_error("Error creating server socket: %s", strerror(errno)); return -1; } // workaround for dev purposes (avoiding error binding socket: Address already in use) int option = 1; setsockopt(server->sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); option = 1; setsockopt(server->sockfd, IPPROTO_TCP, TCP_NODELAY, &option, sizeof(option)); int keepalive = 1; setsockopt(server->sockfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive , sizeof(keepalive )); struct sockaddr_in server_address; memset(&server_address, 0, sizeof(struct sockaddr_in)); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = INADDR_ANY; server_address.sin_port = htons(port); if (bind(server->sockfd, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) { rawhttps_logger_log_error("Error binding server socket: %s", strerror(errno)); return -1; } server->port = port; server->initialized = true; int err = 0; server->certificate = asn1_parse_pem_certificate_from_file(certificate_path, 0); if (err) { rawhttps_logger_log_error("Error parsing PEM certificate from file (path: %s)", certificate_path); return -1; } server->private_key = asn1_parse_pem_private_certificate_key_from_file(private_key_path, &err); if (err) { rawhttps_logger_log_error("Error parsing PEM private key from file (path: %s)", private_key_path); asn1_pem_certificate_free(server->certificate); return -1; } return 0; } int rawhttps_server_destroy(rawhttps_server* server) { // Note: Calling this function will not kill running threads // The server socket will be released, causing the listen thread to eventually stop // But there may be other threads running with opened connections // @TODO: This should be fixed asap. Then the log mutex can also be destroyed. if (server->initialized) { asn1_pem_certificate_free(server->certificate); private_key_free(server->private_key); shutdown(server->sockfd, SHUT_RDWR); close(server->sockfd); rawhttps_handler_tree_destroy(&server->handlers); } return 0; } static void* rawhttps_server_new_connection_callback(void* arg) { rawhttps_connection* connection = (rawhttps_connection*)arg; char* client_ip_ascii = inet_ntoa(connection->client_address.sin_addr); rawhttps_logger_log_info("Connection established with client %s.", client_ip_ascii); rawhttps_tls_state ts; if (rawhttps_tls_state_create(&ts, connection->server->certificate, connection->server->private_key)) { rawhttps_logger_log_error("Error creating tls state"); return NULL; } if (rawhttps_tls_handshake(&ts, connection->connected_socket)) { rawhttps_logger_log_error("Error in TLS handshake. Connection with client %s will be destroyed.", client_ip_ascii); rawhttps_tls_state_destroy(&ts); return NULL; } // In the future, if we want to support keep-alive, we need to add a loop here. // We would need to differ between a CLOSE_NOTIFY and a real error rawhttps_http_parser_state hps; rawhttps_request request; rawhttps_http_parser_state_create(&hps, &ts); if (rawhttps_parser_parse(&hps, &request, connection->connected_socket)) { rawhttps_logger_log_error("Error parsing HTTP packet. Connection was dropped or syntax was invalid. Connection with client %s will be destroyed.", client_ip_ascii); return NULL; } const rawhttps_server_handler* handler = rawhttps_handler_tree_get(&connection->server->handlers, request.uri, request.uri_size); if (handler) { rawhttps_response response; if (rawhttps_response_new(&response)) { rawhttps_header_destroy(&request.header); rawhttps_http_parser_state_destroy(&hps); rawhttps_tls_state_destroy(&ts); rawhttps_logger_log_error("Error creating new HTTP response"); return NULL; } rawhttps_response_connection_information rci; rci.connected_socket = connection->connected_socket; rci.ts = &ts; handler->handle(&rci, &request, &response); if (rawhttps_response_destroy(&response)) { rawhttps_header_destroy(&request.header); rawhttps_http_parser_state_destroy(&hps); rawhttps_tls_state_destroy(&ts); rawhttps_logger_log_error("Error destroying HTTP response"); return NULL; } } else { char buf[] = "HTTP/1.0 404 Not Found\n" "Connection: Keep-Alive\n" "Content-Length: 9\n" "\n" "404 Error"; write(request.connected_socket, buf, sizeof(buf) - 1); } if (rawhttps_tls_close(&ts, connection->connected_socket)) rawhttps_logger_log_error("Error closing TLS connection"); rawhttps_header_destroy(&request.header); rawhttps_http_parser_state_destroy(&hps); rawhttps_tls_state_destroy(&ts); close(connection->connected_socket); free(connection); rawhttps_logger_log_info("Connection with client %s was closed successfully.", client_ip_ascii); return NULL; } int rawhttps_server_listen(rawhttps_server* server) { if (listen(server->sockfd, RAWHTTPS_SERVER_MAX_QUEUE_SERVER_PENDING_CONNECTIONS) == -1) { rawhttps_logger_log_error("Error listening to server socket: %s", strerror(errno)); return -1; } struct sockaddr_in client_address; //long number_of_processors = sysconf(_SC_NPROCESSORS_ONLN); //if (number_of_processors == -1) number_of_processors = 1; //rawhttps_logger_log_info("Processors available: %d", number_of_processors); while (1) { socklen_t client_address_length = sizeof(client_address); int connected_socket = accept(server->sockfd, (struct sockaddr*)&client_address, &client_address_length); if (connected_socket == -1) { if (errno == EBADF) return 0; // Server socket was closed. Exiting gracefully.. rawhttps_logger_log_error("Error accepting connected socket: %s", strerror(errno)); return -1; } pthread_t connection_thread; rawhttps_connection* connection = malloc(sizeof(rawhttps_connection)); connection->server = server; connection->connected_socket = connected_socket; connection->client_address = client_address; if (pthread_create(&connection_thread, NULL, rawhttps_server_new_connection_callback, connection)) { rawhttps_logger_log_error("Error creating thread for new connection: %s", strerror(errno)); return -1; } } return 0; } int rawhttps_server_register_handle(rawhttps_server* server, const char* pattern, long long pattern_size, rawhttps_server_handle_func handle) { return rawhttps_handler_tree_put(&server->handlers, pattern, pattern_size, handle); }