From ef90f1ba130d67835fa37f856a81ef0f3c597877 Mon Sep 17 00:00:00 2001 From: "Tristan B. Kildaire" Date: Fri, 2 Oct 2020 16:11:27 +0200 Subject: [PATCH] daemon now uses configuration file for network parameters, also added motd command and serverinfo command (not yet implemented) --- config.json | 5 +- protocol.md | 35 +++++++++- source/app.d | 77 +++++++++++++++++++-- source/dnetd/dconfig.d | 138 +++++++++++++++++++++++++++++++++++++ source/dnetd/dconnection.d | 38 ++++++++++ source/dnetd/dserver.d | 31 ++++++++- 6 files changed, 315 insertions(+), 9 deletions(-) diff --git a/config.json b/config.json index ddd0861..51969b7 100644 --- a/config.json +++ b/config.json @@ -1,8 +1,9 @@ { "general" : { - "address" : "0.0.0.0", + "addresses" : ["0.0.0.0"], "port" : "7777", "network" : "aBasedIRCNetwork", - "name" : "MyBrandSpankingNewIRCServer" + "name" : "MyBrandSpankingNewIRCServer", + "motd" : "Welcome to my generic dnet chat server!" } } \ No newline at end of file diff --git a/protocol.md b/protocol.md index 08375a4..63e7a36 100644 --- a/protocol.md +++ b/protocol.md @@ -143,13 +143,46 @@ Request format: |-- 9 --|-- channel --| ``` - Reply format: ``` |-- status (1 byte) --|-- channel members (CSV) --| ``` +### `serverinfo` + +Request format: + +``` +|-- 10 --| +``` + +Reply format: + +``` +|-- status (1 byte) --|-- server info(CSV) --| +``` + +#### Format of server info (CSV) + +``` +,,, +``` + +### `motd` + +Request format: + +``` +|-- 11 --| +``` + +Reply format: + +``` +|-- status (1 byte) --|-- motd --| +``` + ### `chanprop` diff --git a/source/app.d b/source/app.d index 482a5e6..29146c0 100644 --- a/source/app.d +++ b/source/app.d @@ -1,13 +1,82 @@ import std.stdio; import std.socket : parseAddress; import dnetd.dserver : DServer; +import dnetd.dconfig : DConfig; +import std.json; +import std.exception; -void main() +void main(string[] args) { - /* TODO: Args for bind */ + /* Configuration file */ + string configFilename; + + /* If there are no arguments */ + if(!args.length) + { + /* Use the default file */ + configFilename = "config.json"; + } + /* If there is one argument */ + else if(args.length == 1) + { + /* use the specified one */ + configFilename = args[0]; + } + /* Illegal amount of guns in one household (no such thing) */ + else + { + writeln("Invalid number of arguments"); + return; + } + + /* Configuration file contents */ + byte[] data; - - DServer dserver = new DServer(parseAddress("0.0.0.0", 7777)); + try + { + /* Open the file for reading */ + File config; + config.open(configFilename, "r"); + + /* Read the configuration file data */ + data.length = config.size(); + data = config.rawRead(data); + config.close(); + } + catch(ErrnoException e) + { + writeln("Failure to use configuration file'"~configFilename~"' with error:\n\n"~e.toString()); + return; + } + + /* The JSON */ + JSONValue json; + + try + { + /* Parse the configuration file */ + json = parseJSON(cast(string)data); + } + catch(JSONException e) + { + writeln("Failure to parse configuration file'"~configFilename~"' with error:\n\n"~e.toString()); + return; + } + + /* Create a new configuration file and check configuration parameters */ + DConfig config = DConfig.getConfig(json); + + /* If the configuration reading was successful (valid JSON) */ + if(config) + { + /* Start the server */ + DServer dserver = new DServer(config); + } + else + { + writeln("Failure to read a valid dnetd configuration file'"~configFilename~"'"); + } + } \ No newline at end of file diff --git a/source/dnetd/dconfig.d b/source/dnetd/dconfig.d index e69de29..6e255c5 100644 --- a/source/dnetd/dconfig.d +++ b/source/dnetd/dconfig.d @@ -0,0 +1,138 @@ +/** +* DConfig +* +* Represents all configuration parameters +*/ +module dnetd.dconfig; + +import std.json; +import std.conv; +import std.socket : Address, parseAddress; + +public final class DConfig +{ + /* General configuration */ + private DGeneralConfig generalConfig; + + /* Link configuration */ + private DLinkConfig linksConfig; + + private this() + { + /* TODO: */ + } + + public DGeneralConfig getGeneral() + { + return generalConfig; + } + + public DLinkConfig getLinks() + { + return linksConfig; + } + + public static DConfig getConfig(JSONValue json) + { + /* The newly created configuration */ + DConfig config = new DConfig(); + + try + { + /* TODO: Parse */ + + /* Get the `general` block */ + JSONValue generalBlock = json["general"]; + config.generalConfig = DGeneralConfig.getConfig(generalBlock); + + /* Get the `links` block */ + JSONValue linksBlock = json["links"]; + //config.linksConfig = DLinkConfig.getConfig(linksBlock); + } + catch(JSONException e) + { + /* Set config to null (signals an error) */ + config = null; + } + + return config; + } + + public JSONValue saveConfig() + { + JSONValue config; + + + return config; + } +} + +public final class DGeneralConfig +{ + + /* Addresses to bind sockets to */ + private string[] addresses; + private ushort port; + + /* Server information */ + private string network; + private string name; + private string motd; + + private this() + { + + } + + public static DGeneralConfig getConfig(JSONValue generalBlock) + { + /* The generated general config */ + DGeneralConfig config = new DGeneralConfig(); + + try + { + /* Set the addresses */ + foreach(JSONValue address; generalBlock["addresses"].array()) + { + config.addresses ~= [address.str()]; + } + + /* Set the ports */ + config.port = to!(ushort)(generalBlock["port"].str()); + + /* Set the network name */ + config.network = generalBlock["network"].str(); + + /* Set the server name */ + config.name = generalBlock["name"].str(); + + /* Set the message of the day */ + config.motd = generalBlock["motd"].str(); + + } + catch(JSONException e) + { + /* Set the config to null (signals an error) */ + config = null; + } + + + return config; + } + + public string getMotd() + { + return motd; + } + + public Address getAddress() + { + /* TODO: Add multi address support later */ + return parseAddress(addresses[0], port); + } +} + +public final class DLinkConfig +{ + +} \ No newline at end of file diff --git a/source/dnetd/dconnection.d b/source/dnetd/dconnection.d index 1206d97..f2108da 100644 --- a/source/dnetd/dconnection.d +++ b/source/dnetd/dconnection.d @@ -42,6 +42,8 @@ public class DConnection : Thread MSG, MEMBER_COUNT, MEMBER_LIST, + SERVER_INFO, + MOTD, UNKNOWN } @@ -244,6 +246,16 @@ public class DConnection : Thread { command = Command.MEMBER_LIST; } + else if(commandByte == cast(ulong)10) + { + command = Command.SERVER_INFO; + } + else if(commandByte == cast(ulong)11) + { + command = Command.MOTD; + } + + return command; @@ -541,6 +553,32 @@ public class DConnection : Thread + } + /* If `serverinfo` command (requires: authed, !unspec) */ + else if(command == Command.SERVER_INFO && hasAuthed && connType != ConnectionType.UNSPEC) + { + /* Status */ + bool status = true; + + /* Get the server info */ + string serverInfo = server.getServerInfo(); + + /* Encode the reply */ + reply ~= [status]; + reply ~= serverInfo; + } + /* If `motd` command (requires: _nothing_) */ + else if(command == Command.MOTD) + { + /* Status */ + bool status = true; + + /* Get the message of the day */ + string motd = server.getConfig().getGeneral().getMotd(); + + /* Encode the reply */ + reply ~= [status]; + reply ~= motd; } /* If no matching built-in command was found */ else diff --git a/source/dnetd/dserver.d b/source/dnetd/dserver.d index 9281544..fe093fd 100644 --- a/source/dnetd/dserver.d +++ b/source/dnetd/dserver.d @@ -18,6 +18,7 @@ import std.string : cmp; import core.sync.mutex : Mutex; import std.stdio; import std.conv : to; +import dnetd.dconfig; public class DServer : Thread { @@ -28,6 +29,9 @@ public class DServer : Thread private Address sockAddress; + /* Server configuration */ + private DConfig config; + /** * Connection queue */ @@ -40,13 +44,17 @@ public class DServer : Thread private DChannel[] channels; private Mutex channelLock; - this(Address sockAddress) + /* TODO: Implement new constructor */ + this(DConfig config) { /* Set the function to be called on thread start */ super(&dequeueLoop); + + /* Set the server's config */ + this.config = config; /* Set the listening address */ - this.sockAddress = sockAddress; + this.sockAddress = config.getGeneral().getAddress(); /* Initialize the server */ init(); @@ -55,6 +63,11 @@ public class DServer : Thread startServer(); } + public DConfig getConfig() + { + return config; + } + private void init() { /* Setup socket */ @@ -309,4 +322,18 @@ public class DServer : Thread return currentChannels; } + + public string getServerInfo() + { + /* The server information */ + string serverInfo; + + /* TODO: Fetch serverName */ + /* TODO: Fetch networkName */ + /* TODO: Fetch userCount */ + /* TODO: Fetch channelCount */ + + + return serverInfo; + } } \ No newline at end of file