2023-08-24 23:34:05 +00:00
|
|
|
// =========================================
|
|
|
|
// | SilverMUD Client - main.c |
|
|
|
|
// | Copyright (C) 2023, Barra Ó Catháin |
|
|
|
|
// | See end of file for copyright notice. |
|
|
|
|
// =========================================
|
|
|
|
#include <stdio.h>
|
2024-03-15 01:29:34 +00:00
|
|
|
#include <netdb.h>
|
|
|
|
#include <string.h>
|
2023-08-24 23:34:05 +00:00
|
|
|
#include <stdlib.h>
|
2024-03-15 01:29:34 +00:00
|
|
|
#include <getopt.h>
|
2023-08-24 23:34:05 +00:00
|
|
|
#include <limits.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <ncurses.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <gnutls/gnutls.h>
|
|
|
|
|
2023-08-28 01:29:21 +00:00
|
|
|
#include "../config.h"
|
2023-08-25 23:48:28 +00:00
|
|
|
#include "../messages.h"
|
2023-08-31 00:44:17 +00:00
|
|
|
#include "client-drawing.h"
|
|
|
|
#include "receiving-thread.h"
|
2023-08-25 23:48:28 +00:00
|
|
|
|
2024-03-18 03:09:32 +00:00
|
|
|
static char serverPort[HOST_NAME_MAX] = "5050";
|
|
|
|
static char serverHostname[HOST_NAME_MAX] = "127.0.0.1";
|
|
|
|
static bool hostSpecified = false, portSpecified = false;
|
|
|
|
|
2023-08-24 23:34:05 +00:00
|
|
|
int main (int argc, char ** argv)
|
2024-03-18 03:09:32 +00:00
|
|
|
{
|
2023-08-24 23:34:05 +00:00
|
|
|
// Print a welcome message:
|
|
|
|
printf("SilverMUD Client - Starting Now.\n"
|
|
|
|
"================================\n");
|
|
|
|
|
2024-03-18 03:09:32 +00:00
|
|
|
struct addrinfo * serverInformation;
|
|
|
|
|
2024-03-15 01:29:34 +00:00
|
|
|
// Configure command-line options:
|
|
|
|
static struct option longOptions[] =
|
|
|
|
{
|
|
|
|
{"host", required_argument, 0, 'h' },
|
|
|
|
{"port", required_argument, 0, 'p' }
|
|
|
|
};
|
|
|
|
|
|
|
|
// Parse command-line options:
|
|
|
|
int selectedOption = 0, optionIndex = 0;
|
|
|
|
|
|
|
|
while ((selectedOption = getopt_long(argc, argv, "h:p:", longOptions, &optionIndex)) != -1)
|
|
|
|
{
|
|
|
|
switch (selectedOption)
|
|
|
|
{
|
|
|
|
case 'h':
|
|
|
|
{
|
|
|
|
printf("Connecting to host: %s\n", optarg);
|
|
|
|
hostSpecified = true;
|
|
|
|
strncpy(serverHostname, optarg, HOST_NAME_MAX);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'p':
|
|
|
|
{
|
|
|
|
printf("Connecting to port: %s\n", optarg);
|
|
|
|
portSpecified = true;
|
|
|
|
strncpy(serverPort, optarg, HOST_NAME_MAX);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-24 23:34:05 +00:00
|
|
|
// Create a socket for communicating with the server:
|
|
|
|
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if (serverSocket == -1)
|
|
|
|
{
|
|
|
|
printf("Socket creation failed. Aborting.\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up the server address structure to point to the server:
|
2024-03-15 01:29:34 +00:00
|
|
|
if (getaddrinfo(serverHostname, serverPort, NULL, &serverInformation) != 0)
|
|
|
|
{
|
|
|
|
printf("Server lookup failed. Aborting.\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2024-03-18 03:09:32 +00:00
|
|
|
// Connect to the server, iterating through addresses until we get SilverMUD:
|
|
|
|
struct addrinfo * currentAddress;
|
|
|
|
for (currentAddress = serverInformation; currentAddress != NULL; currentAddress = currentAddress->ai_next)
|
|
|
|
{
|
|
|
|
if (connect(serverSocket, serverInformation->ai_addr, serverInformation->ai_addrlen) != -1)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (currentAddress == NULL)
|
2023-08-24 23:34:05 +00:00
|
|
|
{
|
|
|
|
printf("Failed to connect to the server. Aborting.\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2024-03-18 03:09:32 +00:00
|
|
|
freeaddrinfo(serverInformation);
|
|
|
|
|
2023-08-24 23:34:05 +00:00
|
|
|
// Set up a GnuTLS session and handshake with the server:
|
|
|
|
gnutls_session_t tlsSession = NULL;
|
|
|
|
if (gnutls_init(&tlsSession, GNUTLS_CLIENT) < 0)
|
|
|
|
{
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
gnutls_anon_client_credentials_t clientKey = NULL;
|
|
|
|
gnutls_anon_allocate_client_credentials(&clientKey);
|
|
|
|
gnutls_transport_set_int(tlsSession, serverSocket);
|
2024-03-18 03:09:32 +00:00
|
|
|
gnutls_credentials_set(tlsSession, GNUTLS_CRD_ANON, &clientKey);
|
2023-08-24 23:34:05 +00:00
|
|
|
gnutls_handshake_set_timeout(tlsSession, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
2024-03-18 03:09:32 +00:00
|
|
|
gnutls_priority_set_direct(tlsSession, "PERFORMANCE:+ANON-ECDH:+ANON-DH", NULL);
|
|
|
|
gnutls_server_name_set(tlsSession, GNUTLS_NAME_DNS, serverHostname, strlen(serverHostname));
|
2023-08-24 23:34:05 +00:00
|
|
|
|
2024-03-18 03:09:32 +00:00
|
|
|
int returnValue = -1, connectionAttempts = 0;
|
2023-08-24 23:34:05 +00:00
|
|
|
do
|
|
|
|
{
|
|
|
|
returnValue = gnutls_handshake(tlsSession);
|
2024-03-18 03:09:32 +00:00
|
|
|
connectionAttempts++;
|
|
|
|
if (connectionAttempts == 50)
|
|
|
|
{
|
|
|
|
printf("Failed to establish a TLS session. Aborting.\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2023-08-24 23:34:05 +00:00
|
|
|
}
|
|
|
|
while (returnValue < 0 && gnutls_error_is_fatal(returnValue) == 0);
|
|
|
|
|
2024-03-18 03:09:32 +00:00
|
|
|
if (returnValue < 0)
|
|
|
|
{
|
|
|
|
printf("Failed to establish a TLS session. Aborting.\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2023-08-28 01:29:21 +00:00
|
|
|
// Initialize ncurses:
|
|
|
|
initscr();
|
|
|
|
keypad(stdscr, TRUE);
|
|
|
|
|
|
|
|
if (!has_colors())
|
|
|
|
{
|
|
|
|
endwin();
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enable colours:
|
|
|
|
start_color();
|
|
|
|
use_default_colors();
|
2023-08-31 00:44:17 +00:00
|
|
|
init_pair(1, COLOR_GREEN, -1);
|
|
|
|
init_pair(2, COLOR_YELLOW, -1);
|
|
|
|
init_pair(3, COLOR_RED, -1);
|
|
|
|
init_pair(4, COLOR_BLUE, -1);
|
|
|
|
init_pair(5, COLOR_CYAN, -1);
|
|
|
|
init_pair(6, COLOR_MAGENTA, -1);
|
|
|
|
|
2023-08-28 01:29:21 +00:00
|
|
|
// Variables needed for the main loop:
|
|
|
|
int height, width;
|
|
|
|
getmaxyx(stdscr, height, width);
|
2023-08-25 23:48:28 +00:00
|
|
|
struct ClientToServerMessage message;
|
2023-08-28 01:29:21 +00:00
|
|
|
|
2023-08-31 00:44:17 +00:00
|
|
|
WINDOW * chatWindow, * gameWindow, * inputWindow;
|
|
|
|
inputWindow = newwin(1, width - 2, height - 1, 1);
|
2023-08-28 01:29:21 +00:00
|
|
|
gameWindow = newwin((height / 2) - 1, width - 2, 1, 1);
|
|
|
|
chatWindow = newwin((height / 2) - 3, width - 2, (height / 2) + 1, 1);
|
|
|
|
|
|
|
|
scrollok(gameWindow, TRUE);
|
|
|
|
scrollok(chatWindow, TRUE);
|
2023-08-31 00:44:17 +00:00
|
|
|
scrollok(inputWindow, TRUE);
|
|
|
|
|
|
|
|
redrawClientLayout(gameWindow, chatWindow, inputWindow);
|
|
|
|
|
|
|
|
struct ReceivingThreadArguments receivingThreadArguments;
|
|
|
|
receivingThreadArguments.chatWindow = chatWindow;
|
|
|
|
receivingThreadArguments.gameWindow = gameWindow;
|
|
|
|
receivingThreadArguments.inputWindow = inputWindow;
|
|
|
|
receivingThreadArguments.session = tlsSession;
|
|
|
|
|
|
|
|
pthread_t receivingThread;
|
|
|
|
pthread_create(&receivingThread, NULL, receivingThreadHandler, &receivingThreadArguments);
|
2023-08-28 01:29:21 +00:00
|
|
|
|
2023-08-24 23:34:05 +00:00
|
|
|
while (true)
|
2023-08-31 00:44:17 +00:00
|
|
|
{
|
|
|
|
wgetnstr(inputWindow, message.content, 1024);
|
|
|
|
|
2023-08-28 01:29:21 +00:00
|
|
|
if (message.content[0] != '\0')
|
|
|
|
{
|
|
|
|
gnutls_record_send(tlsSession, &message, 1024);
|
|
|
|
}
|
2023-08-24 23:34:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return a successful status code to the operating system:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ============================================
|
|
|
|
// | End of main.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/>.
|