From 0814e437cdc0c00fe3474d86dc8bf7ccd8f1d607 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Tue, 22 Aug 2023 02:02:29 +0100 Subject: [PATCH] Basic connection handling (using previous version of client) --- source/server/connections.c | 205 ++++++++++++++++++++++++++++++++++++ source/server/connections.h | 12 ++- source/server/main.c | 107 +++++++++++++++++-- 3 files changed, 313 insertions(+), 11 deletions(-) create mode 100644 source/server/connections.c diff --git a/source/server/connections.c b/source/server/connections.c new file mode 100644 index 0000000..77ee0a4 --- /dev/null +++ b/source/server/connections.c @@ -0,0 +1,205 @@ +// ========================================= +// | SilverMUD Server - connections.c | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#include +#include + +#include "connections.h" + +struct ClientConnectionNode * findMiddle(struct ClientConnectionNode * start, struct ClientConnectionNode * end) +{ + while (start != end) + { + start = start->next; + if(start == end) + { + return start; + } + end = end->previous; + } + + return start; +} + +struct ClientConnection * findConnectionByFileDescriptor(struct ClientConnectionList * list, int fileDescriptor) +{ + struct ClientConnectionNode * start = list->head, * end = list->tail, * middle = findMiddle(start, end); + while (start != end) + { + if (middle->connection->fileDescriptor == fileDescriptor) + { + return middle->connection; + } + else if (middle->connection->fileDescriptor > fileDescriptor) + { + end = middle->previous; + middle = findMiddle(start, end); + } + else + { + start = middle->next; + middle = findMiddle(start, end); + } + } + if (start->connection->fileDescriptor == fileDescriptor) + { + return start->connection; + } + else + { + return NULL; + } +} + +struct ClientConnection * findConnectionByTlsSession(struct ClientConnectionList * list, gnutls_session_t * tlsSession) +{ + +} + +int removeConnectionByFileDescriptor(struct ClientConnectionList * list, int fileDescriptor) +{ + struct ClientConnectionNode * start = list->head, * end = list->tail, * middle = findMiddle(start, end), * toDelete = NULL; + + // Find the node that is to be deleted: + while (start != end && toDelete == NULL) + { + if (middle->connection->fileDescriptor == fileDescriptor) + { + toDelete = middle; + } + else if (middle->connection->fileDescriptor > fileDescriptor) + { + end = middle->previous; + middle = findMiddle(start, end); + } + else + { + start = middle->next; + middle = findMiddle(start, end); + } + } + if (start->connection->fileDescriptor == fileDescriptor) + { + toDelete = start; + } + + if (toDelete == NULL) + { + return -1; + } + + // Set the appropriate pointers on other nodes: + if (toDelete->previous != NULL) + { + toDelete->previous->next = toDelete->next; + } + if (toDelete->next != NULL) + { + toDelete->next->previous = toDelete->previous; + } + + // Set the appropriate pointers on the list: + if (list->head == toDelete) + { + list->head = toDelete->next; + } + if (list->tail == toDelete) + { + list->tail = toDelete->previous; + } + + + list->clientCount--; + + // Free the connection: + free(toDelete->connection->tlsSession); + free(toDelete->connection); + free(toDelete); + + return 0; +} + +int addNewConnection(struct ClientConnectionList * list, int fileDescriptor, gnutls_session_t * tlsSession) +{ + // Allocate memory for the structures: + struct ClientConnectionNode * newConnectionNode = calloc(1, sizeof(struct ClientConnectionNode)); + newConnectionNode->connection = calloc(1, sizeof(struct ClientConnection)); + + // Set the appropriate data in the structure: + newConnectionNode->next = NULL; + newConnectionNode->previous = NULL; + newConnectionNode->connection->tlsSession = tlsSession; + newConnectionNode->connection->fileDescriptor = fileDescriptor; + + // If it's the first node in the list: + if (list->head == NULL && list->tail == NULL) + { + list->head = newConnectionNode; + list->tail = newConnectionNode; + + list->clientCount++; + + return 0; + } + + // Insert it in the appropriate place in the list: + else + { + struct ClientConnectionNode * currentNode = list->head; + + // Seek through the list until we find the appropriate spot to insert the new connection: + while (currentNode->connection->fileDescriptor < fileDescriptor) + { + // If we've reached the end of the list: + if (currentNode->next == NULL) + { + currentNode->next = newConnectionNode; + newConnectionNode->previous = currentNode; + list->tail = newConnectionNode; + + list->clientCount++; + + return 0; + } + else + { + currentNode = currentNode->next; + } + } + + newConnectionNode->previous = currentNode->previous; + newConnectionNode->next = currentNode; + currentNode->previous = newConnectionNode; + + if (newConnectionNode->previous == NULL) + { + list->head = newConnectionNode; + } + if (newConnectionNode->next == NULL) + { + list->tail = newConnectionNode; + } + list->clientCount++; + + return 0; + } +} + +// =================================================== +// | End of connections.c, copyright notice follows. | +// =================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/server/connections.h b/source/server/connections.h index 0ecc0c2..943d9d0 100644 --- a/source/server/connections.h +++ b/source/server/connections.h @@ -6,17 +6,20 @@ #ifndef CONNECTIONS_H #define CONNECTIONS_H #include +#include struct ClientConnection { // TODO: Pointer to player struct. - int fileDescriptor; + gnutls_session_t * tlsSession; + int fileDescriptor; }; struct ClientConnectionNode { struct ClientConnection * connection; struct ClientConnectionNode * next; + struct ClientConnectionNode * previous; }; struct ClientConnectionList @@ -27,6 +30,13 @@ struct ClientConnectionList }; +//struct ClientConnection * findConnectionByPlayer(struct ClientConnectionList * list); +struct ClientConnection * findConnectionByFileDescriptor(struct ClientConnectionList * list, int fileDescriptor); +struct ClientConnection * findConnectionByTlsSession(struct ClientConnectionList * list, gnutls_session_t * tlsSession); + +int removeConnectionByFileDescriptor(struct ClientConnectionList * list, int fileDescriptor); +int addNewConnection(struct ClientConnectionList * list, int fileDescriptor, gnutls_session_t * tlsSession); + #endif // =================================================== // | End of connections.h, copyright notice follows. | diff --git a/source/server/main.c b/source/server/main.c index 1b676c5..057a54b 100644 --- a/source/server/main.c +++ b/source/server/main.c @@ -3,6 +3,7 @@ // | Copyright (C) 2023, Barra Ó Catháin | // | See end of file for copyright notice. | // ========================================= +#include #include #include #include @@ -16,7 +17,9 @@ #include #include #include +#include +#include "connections.h" #include "scheme-integration.h" static const int PORT = 5000; @@ -45,8 +48,8 @@ int main (int argc, char ** argv) } // Allow reusing the address so that quick re-launching doesn't fail: - //setsockopt(masterSocket, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)); - //setsockopt(masterSocket, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)); + setsockopt(masterSocket, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)); + setsockopt(masterSocket, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)); // Setup the server address struct to bind the master socket to: struct sockaddr_in serverAddress; @@ -86,32 +89,116 @@ int main (int argc, char ** argv) int eventsCount = 0; struct epoll_event events[1024]; + // Setup the needed anonymous certificate for TLS: + gnutls_global_init(); + gnutls_anon_server_credentials_t serverKey; + gnutls_anon_allocate_server_credentials(&serverKey); + gnutls_anon_set_server_known_dh_params(serverKey, GNUTLS_SEC_PARAM_MEDIUM); + + // Create a client connection list to allow us to associate TLS sessions and sockets and players: + struct ClientConnectionList clientConnections; + // Start a REPL thread: - pthread_t schemeREPLThread; - pthread_create(&schemeREPLThread, NULL, schemeREPLHandler, NULL); + //pthread_t schemeREPLThread; + //pthread_create(&schemeREPLThread, NULL, schemeREPLHandler, NULL); while (true) { - eventsCount = epoll_wait(connectedClients, events, 1024, -1); - if (eventsCount == -1) + do { + eventsCount = epoll_wait(connectedClients, events, 1024, -1); + } while (eventsCount < 0 && errno == EINTR); + + if (eventsCount == -1) + { fprintf(stderr, "epoll_wait() failed. Aborting.\n"); exit(EXIT_FAILURE); } + for (int index = 0; index < eventsCount; index++) - { + { // If it's the master socket, it's a new client connecting: if (events[index].data.fd == masterSocket) { + // Setup a TLS Session: + gnutls_session_t * tlsSession = calloc(1, sizeof(gnutls_session_t)); + gnutls_init(tlsSession, GNUTLS_SERVER); + gnutls_priority_set_direct(*tlsSession, "NORMAL:+ANON-ECDH:+ANON-DH", NULL); + gnutls_credentials_set(*tlsSession, GNUTLS_CRD_ANON, serverKey); + gnutls_handshake_set_timeout(*tlsSession, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); + + // Accept the connection: int newSocket = accept(masterSocket, NULL, NULL); - send(newSocket, "Hello, world!", 13, 0); - close(newSocket); + gnutls_transport_set_int(*tlsSession, newSocket); + + // Perform a TLS handshake: + volatile int handshakeReturnValue = 0; + do + { + handshakeReturnValue = gnutls_handshake(*tlsSession); + } while (handshakeReturnValue < 0 && gnutls_error_is_fatal(handshakeReturnValue) == 0); + + if (handshakeReturnValue < 0) + { + printf("%d", handshakeReturnValue); + fflush(stdout); + gnutls_bye(*tlsSession, 2); + shutdown(newSocket, 2); + close(newSocket); + break; + } + + watchedEvents.events = EPOLLIN; + watchedEvents.data.fd = newSocket; + + // Add the completed file descriptor to the set: + epoll_ctl(connectedClients, EPOLL_CTL_ADD, newSocket, &watchedEvents); + + // Add the connection to the list: + addNewConnection(&clientConnections, newSocket, tlsSession); + + // Print a message: + printf("New connection established! %d clients, session ID %d.\n", clientConnections.clientCount, tlsSession); + + } + else + { + // Find the corresponding TLS session: + struct ClientConnection * connection = findConnectionByFileDescriptor(&clientConnections, events[index].data.fd); + if (connection != NULL) + { + char buffer[2048]; + int returnValue = gnutls_record_recv(*connection->tlsSession, &buffer, 2048); + if (returnValue == 0 || returnValue == -10) + { + printf("Closing session ID: %d\n", *connection->tlsSession); + epoll_ctl(connectedClients, EPOLL_CTL_DEL, connection->fileDescriptor, &watchedEvents); + gnutls_bye(*connection->tlsSession, 2); + shutdown(connection->fileDescriptor, 2); + close(connection->fileDescriptor); + removeConnectionByFileDescriptor(&clientConnections, connection->fileDescriptor); + } + else if (returnValue == 2048) + { + printf("%s\n", buffer); + fflush(stdout); + } + } + else + { + printf("Didn't find associated TLS Session!\n"); + fflush(stdout); + // Remove the file descriptor from our watched set and close it: + epoll_ctl(connectedClients, EPOLL_CTL_DEL, events[index].data.fd, &watchedEvents); + close(events[index].data.fd); + removeConnectionByFileDescriptor(&clientConnections, events[index].data.fd); + } } } } // Wait for all other threads to terminate: - pthread_join(schemeREPLThread, NULL); + //pthread_join(schemeREPLThread, NULL); // Return a successful status code to the operating system: return 0;