#define _GNU_SOURCE #include #include #include #include #include #include #include "config.h" #include "client.h" #include "network.h" uv_loop_t *loop; SSL_CTX *ctx; void cleanup_client(uv_handle_t *handle) { struct client *client = (struct client*) handle; network_client_remove(client); if (client->ssl) { SSL_free(client->ssl); } free(client); } void on_write_end(uv_write_t *req, int status) { if (status < 0) { fprintf(stderr, "Write error %s\n", uv_strerror(status)); } free(req->data); free(req); } void flush_ssl_to_socket(struct client *client) { char buf[4096]; int pending; while ((pending = BIO_pending(client->wbio)) > 0) { int bytes_read = BIO_read(client->wbio, buf, sizeof(buf)); if (bytes_read > 0) { uv_write_t *req = malloc(sizeof(uv_write_t)); char *send_data = malloc(bytes_read); memcpy(send_data, buf, bytes_read); req->data = send_data; uv_buf_t uvbuf = uv_buf_init(send_data, bytes_read); uv_write(req, (uv_stream_t*)&client->handle, &uvbuf, 1, on_write_end); } } } void ssl_write_msg(struct client *client, const char *data, size_t len) { int written = SSL_write(client->ssl, data, len); if (written > 0) { flush_ssl_to_socket(client); } } void on_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { struct client *client = (struct client*) stream; if (nread > 0) { BIO_write(client->rbio, buf->base, nread); char plain_buf[4096]; int p; while ((p = SSL_read(client->ssl, plain_buf, sizeof(plain_buf))) > 0) { network_handle_data(client, plain_buf, p); } flush_ssl_to_socket(client); } else if (nread < 0) { if (nread != UV_EOF) fprintf(stderr, "Read error %s\n", uv_err_name(nread)); uv_close((uv_handle_t*) client, cleanup_client); } free(buf->base); } void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { buf->base = malloc(suggested_size); buf->len = suggested_size; } void on_new_connection(uv_stream_t *server, int status) { if (status < 0) { fprintf(stderr, "New connection error %s\n", uv_strerror(status)); return; } struct client *client = malloc(sizeof(struct client)); uv_tcp_init(loop, &client->handle); if (uv_accept(server, (uv_stream_t*) &client->handle) == 0) { client->ssl = SSL_new(ctx); client->rbio = BIO_new(BIO_s_mem()); client->wbio = BIO_new(BIO_s_mem()); SSL_set_bio(client->ssl, client->rbio, client->wbio); SSL_set_accept_state(client->ssl); client->user_id = 0; client->current_room_id = 0; client->is_authenticated = false; client->username[0] = '\0'; network_client_add(client); uv_read_start((uv_stream_t*) &client->handle, alloc_buffer, on_read); } else { uv_close((uv_handle_t*) &client->handle, cleanup_client); } } void init_openssl() { SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); ctx = SSL_CTX_new(TLS_server_method()); if (!ctx) { perror("Unable to create SSL context"); ERR_print_errors_fp(stderr); exit(1); } SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); if (SSL_CTX_use_certificate_file(ctx, CONFIG_CERT_FILE, SSL_FILETYPE_PEM) <= 0 || SSL_CTX_use_PrivateKey_file(ctx, CONFIG_KEY_FILE, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stderr); exit(1); } } int main() { init_openssl(); if (network_init() != 0) { fprintf(stderr, "Failed to initialize network/database\n"); return 1; } loop = uv_default_loop(); uv_tcp_t server; uv_tcp_init(loop, &server); struct sockaddr_in addr; uv_ip4_addr("0.0.0.0", 7000, &addr); uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0); int r = uv_listen((uv_stream_t*) &server, 128, on_new_connection); if (r) { fprintf(stderr, "Listen error %s\n", uv_strerror(r)); return 1; } printf("server listening on port 7000...\n"); return uv_run(loop, UV_RUN_DEFAULT); }