Basic connection handling (using previous version of client)

This commit is contained in:
Barra Ó Catháin 2023-08-22 02:02:29 +01:00
parent 9801be3622
commit 0814e437cd
3 changed files with 313 additions and 11 deletions

205
source/server/connections.c Normal file
View File

@ -0,0 +1,205 @@
// =========================================
// | SilverMUD Server - connections.c |
// | Copyright (C) 2023, Barra Ó Catháin |
// | See end of file for copyright notice. |
// =========================================
#include <stdlib.h>
#include <gnutls/gnutls.h>
#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 <https://www.gnu.org/licenses/>.

View File

@ -6,10 +6,12 @@
#ifndef CONNECTIONS_H #ifndef CONNECTIONS_H
#define CONNECTIONS_H #define CONNECTIONS_H
#include <stddef.h> #include <stddef.h>
#include <gnutls/gnutls.h>
struct ClientConnection struct ClientConnection
{ {
// TODO: Pointer to player struct. // TODO: Pointer to player struct.
gnutls_session_t * tlsSession;
int fileDescriptor; int fileDescriptor;
}; };
@ -17,6 +19,7 @@ struct ClientConnectionNode
{ {
struct ClientConnection * connection; struct ClientConnection * connection;
struct ClientConnectionNode * next; struct ClientConnectionNode * next;
struct ClientConnectionNode * previous;
}; };
struct ClientConnectionList 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 #endif
// =================================================== // ===================================================
// | End of connections.h, copyright notice follows. | // | End of connections.h, copyright notice follows. |

View File

@ -3,6 +3,7 @@
// | Copyright (C) 2023, Barra Ó Catháin | // | Copyright (C) 2023, Barra Ó Catháin |
// | See end of file for copyright notice. | // | See end of file for copyright notice. |
// ========================================= // =========================================
#include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <netdb.h> #include <netdb.h>
#include <stdlib.h> #include <stdlib.h>
@ -16,7 +17,9 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <gnutls/gnutls.h>
#include "connections.h"
#include "scheme-integration.h" #include "scheme-integration.h"
static const int PORT = 5000; 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: // 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_REUSEADDR, &(int){1}, sizeof(int));
//setsockopt(masterSocket, SOL_SOCKET, SO_REUSEPORT, &(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: // Setup the server address struct to bind the master socket to:
struct sockaddr_in serverAddress; struct sockaddr_in serverAddress;
@ -86,32 +89,116 @@ int main (int argc, char ** argv)
int eventsCount = 0; int eventsCount = 0;
struct epoll_event events[1024]; 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: // Start a REPL thread:
pthread_t schemeREPLThread; //pthread_t schemeREPLThread;
pthread_create(&schemeREPLThread, NULL, schemeREPLHandler, NULL); //pthread_create(&schemeREPLThread, NULL, schemeREPLHandler, NULL);
while (true) while (true)
{ {
eventsCount = epoll_wait(connectedClients, events, 1024, -1); do
{
eventsCount = epoll_wait(connectedClients, events, 1024, -1);
} while (eventsCount < 0 && errno == EINTR);
if (eventsCount == -1) if (eventsCount == -1)
{ {
fprintf(stderr, "epoll_wait() failed. Aborting.\n"); fprintf(stderr, "epoll_wait() failed. Aborting.\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
for (int index = 0; index < eventsCount; index++) for (int index = 0; index < eventsCount; index++)
{ {
// If it's the master socket, it's a new client connecting: // If it's the master socket, it's a new client connecting:
if (events[index].data.fd == masterSocket) 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); int newSocket = accept(masterSocket, NULL, NULL);
send(newSocket, "Hello, world!", 13, 0); gnutls_transport_set_int(*tlsSession, newSocket);
close(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: // 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 a successful status code to the operating system:
return 0; return 0;