[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/felipeek/rawhttps/master/src/server.c [Back]  [Original]

/***
 *       _____                          
 *      / ____|                         
 *     | (___   ___ _ ____   _____ _ __ 
 *      \___ \ / _ \ '__\ \ / / _ \ '__|
 *      ____) |  __/ |   \ 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);
}

Web Proxy Viewer  |  New URL  |  Original Page