Basic connection handling (using previous version of client)
This commit is contained in:
parent
9801be3622
commit
0814e437cd
|
@ -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/>.
|
|
@ -6,17 +6,20 @@
|
||||||
#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.
|
||||||
int fileDescriptor;
|
gnutls_session_t * tlsSession;
|
||||||
|
int fileDescriptor;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ClientConnectionNode
|
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. |
|
||||||
|
|
|
@ -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
|
||||||
if (eventsCount == -1)
|
|
||||||
{
|
{
|
||||||
|
eventsCount = epoll_wait(connectedClients, events, 1024, -1);
|
||||||
|
} while (eventsCount < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
Loading…
Reference in New Issue