diff --git a/.gitignore b/.gitignore index 87cbe7f..a959c49 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ __test__*__ /salty-ircd source/ircd/packageVersion.d motd +config.sdl diff --git a/config.template.sdl b/config.template.sdl new file mode 100644 index 0000000..dc78bed --- /dev/null +++ b/config.template.sdl @@ -0,0 +1,10 @@ +listen { + type "plaintext" + address "::" + port 6667 +} + +# No password set +pass ""; + +# vim:ft= diff --git a/dub.sdl b/dub.sdl index 7e85075..23f9177 100644 --- a/dub.sdl +++ b/dub.sdl @@ -6,5 +6,6 @@ license "NCSA" targetType "executable" dependency "vibe-d:core" version="~>0.7.30" dependency "gen-package-version" version="~>1.0.5" +dependency "sdlang-d" version="~>0.10.1" preGenerateCommands "dub run gen-package-version -- ircd --src=source/" versions "VibeDefaultMain" diff --git a/dub.selections.json b/dub.selections.json index a775187..4ef43a2 100644 --- a/dub.selections.json +++ b/dub.selections.json @@ -6,10 +6,13 @@ "gen-package-version": "1.0.5", "libasync": "0.8.3", "libevent": "2.0.2+2.0.16", + "libinputvisitor": "1.2.2", "memutils": "0.4.9", "openssl": "1.1.5+1.0.1g", "scriptlike": "0.9.7", + "sdlang-d": "0.10.1", "taggedalgebraic": "0.10.5", + "unit-threaded": "0.6.36", "vibe-d": "0.7.31" } } diff --git a/source/ircd/app.d b/source/ircd/app.d index 73cea0e..a18c4f0 100644 --- a/source/ircd/app.d +++ b/source/ircd/app.d @@ -1,10 +1,73 @@ module ircd.app; +import std.algorithm; +import std.traits; +import std.string; + +import sdlang; + import ircd.server; +static T tagValueOrNull(T)(Tag tag, string childName) +{ + if(childName !in tag.tags) + { + return null; + } + else + { + return tagValue!T(tag, childName); + } +} + +static T tagValue(T)(Tag tag, string childName) +{ + static if(isArray!T && !isSomeString!T) + { + template U(T : T[]) + { + alias U = T; + } + + T array = []; + + foreach(value; tag.tags[childName][0].values) + { + array ~= value.get!(U!T); + } + + return array; + } + else static if(isIntegral!T && !is(T == int)) + { + return cast(T)tagValue!int(tag, childName); + } + else + { + return tag.tags[childName][0].values[0].get!T; + } +} + shared static this() { auto server = new Server(); - server.listen(); + + auto config = parseFile("config.sdl"); + + auto pass = config.tagValue!string("pass"); + server.setPass(pass.empty ? null : pass); + + foreach(listenBlock; config.tags.filter!(t => t.getFullName.toString == "listen")) + { + assert(listenBlock.tagValue!string("type") == "plaintext"); + + auto addresses = listenBlock.tagValue!(string[])("address"); + auto port = listenBlock.tagValue!ushort("port"); + + foreach(address; addresses) + { + server.listen(port, address); + } + } } diff --git a/source/ircd/connection.d b/source/ircd/connection.d index 03747db..536584b 100644 --- a/source/ircd/connection.d +++ b/source/ircd/connection.d @@ -34,10 +34,12 @@ class Connection bool connected; + string pass = null; + @property auto channels() { return _server.channels.filter!(c => c.members.canFind(this)); } @property string prefix() { return nick ~ "!" ~ user ~ "@" ~ hostname; } - @property bool registered() { return nick !is null && user !is null; } + @property bool registered() { return nick !is null && user !is null && _server.isPassCorrect(pass); } @property bool isOperator() { return modes.canFind('o') || modes.canFind('O'); } @property string servername() { return _server.name; } //TODO: Support server linking @@ -145,7 +147,7 @@ class Connection writeln("C> " ~ message.toString); - if(!registered && !["NICK", "USER", "PING", "PONG", "QUIT"].canFind(message.command)) + if(!registered && !["NICK", "USER", "PASS", "PING", "PONG", "QUIT"].canFind(message.command)) { sendErrNotRegistered(); continue; @@ -159,6 +161,9 @@ class Connection case "USER": onUser(message); break; + case "PASS": + onPass(message); + break; case "PING": //TODO: Connection timeout when we don't get a PONG send(Message(_server.name, "PONG", [_server.name, message.parameters[0]], true)); @@ -307,6 +312,31 @@ class Connection } } + void onPass(Message message) + { + //TODO: Make sure PASS is sent before the NICK/USER combination + + if(message.parameters.length < 1) + { + sendErrNeedMoreParams(message.command); + return; + } + + if(registered) + { + send(Message(_server.name, "462", [nick, "Unauthorized command (already registered)"], true)); + return; + } + + pass = message.parameters[0]; + + if(!_server.isPassCorrect(pass)) + { + //NOTE: The RFCs don't allow ERR_PASSWDMISMATCH as a response to PASS + //TODO: If RFC-strictness is off, do send ERR_PASSWDMISMATCH + } + } + void onQuit(Message message) { connected = false; diff --git a/source/ircd/server.d b/source/ircd/server.d index 7270e5c..4a5ddc1 100644 --- a/source/ircd/server.d +++ b/source/ircd/server.d @@ -35,6 +35,8 @@ class Server private uint[string] _commandUsage; private ulong[string] _commandBytes; + private string _pass = null; + private SysTime _startTime; this() @@ -441,6 +443,16 @@ class Server connection.send(Message(name, "242", [connection.nick, uptimeString], true)); } + void setPass(string pass) + { + _pass = pass; + } + + bool isPassCorrect(string pass) + { + return pass == _pass; + } + void listen(ushort port = 6667) { listenTCP(port, &acceptConnection);