From 6c685d6a9642d1f29474973916d8968a85adbcee Mon Sep 17 00:00:00 2001 From: icedream Date: Fri, 9 May 2014 14:07:00 +0200 Subject: [PATCH] npserv and npmotd now work together! :) --- libnpsharp.sln.DotSettings | 1 + .../DummyAuthenticationHandler.cs | 51 ++++++++ ...lper.cs => SessionAuthenticationClient.cs} | 7 +- .../SessionAuthenticationServer.cs | 117 ++++++++++++++++++ src/libnpsharp/NPServer.cs | 21 ++-- src/libnpsharp/NPServerClient.cs | 6 +- .../AuthenticateValidateTicketMessage.cs | 2 +- src/libnpsharp/RPC/Messages/RPCMessage.cs | 28 ++++- src/libnpsharp/Steam/CSteamID.cs | 2 +- src/libnpsharp/libnpsharp.csproj | 47 ++++++- src/npfile/Program.cs | 2 +- src/npmotd/Program.cs | 2 +- src/npserv/App.config | 10 +- src/npserv/DummyAuthenticationHandler.cs | 17 +-- src/npserv/DummyFileServingHandler.cs | 35 ++++++ src/npserv/Program.cs | 17 ++- src/npserv/npserv.csproj | 42 +++++++ src/npserv/packages.config | 3 + 18 files changed, 377 insertions(+), 33 deletions(-) create mode 100644 src/libnpsharp/Authentication/DummyAuthenticationHandler.cs rename src/libnpsharp/Authentication/{AuthenticationHelper.cs => SessionAuthenticationClient.cs} (94%) create mode 100644 src/libnpsharp/Authentication/SessionAuthenticationServer.cs create mode 100644 src/npserv/DummyFileServingHandler.cs diff --git a/libnpsharp.sln.DotSettings b/libnpsharp.sln.DotSettings index 39361ad..cde13c7 100644 --- a/libnpsharp.sln.DotSettings +++ b/libnpsharp.sln.DotSettings @@ -1,6 +1,7 @@  HTTP ID + IP NP NPID RPC diff --git a/src/libnpsharp/Authentication/DummyAuthenticationHandler.cs b/src/libnpsharp/Authentication/DummyAuthenticationHandler.cs new file mode 100644 index 0000000..3c6b703 --- /dev/null +++ b/src/libnpsharp/Authentication/DummyAuthenticationHandler.cs @@ -0,0 +1,51 @@ +using System; +using System.Threading; +using NPSharp.Steam; +using Raven.Abstractions.Data; +using Raven.Client.Connection; +using Raven.Client.Linq; +using Raven.Database; +using Raven.Database.Linq.PrivateExtensions; + +namespace NPSharp.Authentication +{ + class DummyAuthenticationHandler : IAuthenticationHandler + { + // TODO: RavenDB integration + + private uint _userID; + + private DocumentDatabase _db; + + public DummyAuthenticationHandler(DocumentDatabase db) + { + _db = db; + } + + public AuthenticationResult AuthenticateUser(NPServerClient client, string username, string password) + { + return new AuthenticationResult(new CSteamID() + { + AccountID = _userID++ + }); + } + + public AuthenticationResult AuthenticateUser(NPServerClient client, string token) + { + return new AuthenticationResult(new CSteamID() + { + AccountID = _userID++ + }); + } + + public AuthenticationResult AuthenticateServer(NPServerClient client, string licenseKey) + { + throw new NotImplementedException(); + } + + public TicketValidationResult ValidateTicket(NPServerClient client, NPServerClient server) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/libnpsharp/Authentication/AuthenticationHelper.cs b/src/libnpsharp/Authentication/SessionAuthenticationClient.cs similarity index 94% rename from src/libnpsharp/Authentication/AuthenticationHelper.cs rename to src/libnpsharp/Authentication/SessionAuthenticationClient.cs index e5df6c9..3838bfa 100644 --- a/src/libnpsharp/Authentication/AuthenticationHelper.cs +++ b/src/libnpsharp/Authentication/SessionAuthenticationClient.cs @@ -10,19 +10,19 @@ namespace NPSharp.Authentication /// Represents a client which can communicate with an authentication endpoint in order to retrieve session /// information, including tokens for authentication with NP servers. /// - public class AuthenticationHelper + public class SessionAuthenticationClient { private readonly string _host; private readonly string _path; private readonly ushort _port; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Hostname of the authentication endpoint. /// Port of the authentication endpoint. /// Path of the authentication endpoint. - public AuthenticationHelper(string host, ushort port = 12003, string path = "/authenticate") + public SessionAuthenticationClient(string host, ushort port = 12003, string path = "/authenticate") { _host = host; _port = port; @@ -74,6 +74,7 @@ namespace NPSharp.Authentication req.Method = "POST"; req.ContentType = "application/x-www-form-urlencoded"; req.AllowAutoRedirect = true; + req.KeepAlive = false; using (Stream reqStream = req.GetRequestStream()) { byte[] buffer = Encoding.UTF8.GetBytes(post); diff --git a/src/libnpsharp/Authentication/SessionAuthenticationServer.cs b/src/libnpsharp/Authentication/SessionAuthenticationServer.cs new file mode 100644 index 0000000..8b08c5f --- /dev/null +++ b/src/libnpsharp/Authentication/SessionAuthenticationServer.cs @@ -0,0 +1,117 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; +using log4net; +using uhttpsharp; +using uhttpsharp.Handlers; +using uhttpsharp.Headers; +using uhttpsharp.Listeners; +using uhttpsharp.RequestProviders; + +namespace NPSharp.Authentication +{ + public class SessionAuthenticationServer + { + private HttpServer _http; + + private readonly ILog _log; + + public SessionAuthenticationServer() + { + SupportOldAuthentication = true; + _log = LogManager.GetLogger("Auth"); + } + + public event Func Authenticating; + + protected virtual SessionAuthenticationResult OnAuthenticating(string username, string password) + { + var handler = Authenticating; + return handler != null ? handler(username, password) : new SessionAuthenticationResult { Reason = "Login currently disabled" }; + } + + public bool SupportOldAuthentication { get; set; } + + public void Start(ushort port = 12003) + { + if (_http != null) + { + throw new InvalidOperationException("This server is already running"); + } + _http = new HttpServer(new HttpRequestProvider()); + _http.Use(new TcpListenerAdapter(new TcpListener(IPAddress.IPv6Any, port))); + _http.Use(new HttpRouter().With("authenticate", new AuthenticateHandler(this))); + _http.Use(new AnonymousHttpRequestHandler((ctx, task) => + { + ctx.Response = HttpResponse.CreateWithMessage(HttpResponseCode.NotFound, "Not found", ctx.Request.Headers.KeepAliveConnection()); + return Task.Factory.GetCompleted(); + })); + _http.Start(); + } + + protected class AuthenticateHandler : IHttpRequestHandler + { + private readonly SessionAuthenticationServer _authServer; + + public AuthenticateHandler(SessionAuthenticationServer sessionAuthenticationServer) + { + _authServer = sessionAuthenticationServer; + } + + public Task Handle(IHttpContext context, Func next) + { + SessionAuthenticationResult sar; + + var login = Encoding.UTF8.GetString(context.Request.Post.Raw) + .Split(new[] {"&&"}, StringSplitOptions.None); + if (login.Length != 2) + { + sar = new SessionAuthenticationResult{Reason = @"Invalid login data"}; + } + else + { + try + { + sar = _authServer.OnAuthenticating(login[0], login[1]); + } + catch (Exception error) + { + _authServer._log.Error(@"Authentication handler error", error); + sar = new SessionAuthenticationResult { Reason = @"Internal server error" }; + } + } + + context.Response = new HttpResponse(HttpResponseCode.Ok, sar.ToString(), context.Request.Headers.KeepAliveConnection()); + return Task.Factory.GetCompleted(); + } + } + + public void Stop() + { + _http.Dispose(); + } + } + + public class SessionAuthenticationResult + { + public bool Success { get; set; } + public string Reason { get; set; } + public uint UserID { get; set; } + public string SessionToken { get; set; } + public string UserName { get; set; } + public string UserMail { get; set; } + public override string ToString() + { + return String.Join("#", + Success ? "ok" : "fail", + String.IsNullOrEmpty(Reason) ? (Success ? "Success" : "Unknown error") : Reason, + UserID, + UserName, + UserMail, + SessionToken, + String.Empty); + } + } +} diff --git a/src/libnpsharp/NPServer.cs b/src/libnpsharp/NPServer.cs index fdeb0b0..4571596 100644 --- a/src/libnpsharp/NPServer.cs +++ b/src/libnpsharp/NPServer.cs @@ -93,7 +93,7 @@ namespace NPSharp /// /// The handler to use for file requests to this NP server. /// - public IFileServingHandler FileHandler { get; set; } + public IFileServingHandler FileServingHandler { get; set; } /// /// The handler to use for user avatar requests to this NP server. @@ -366,7 +366,7 @@ namespace NPSharp client.RPC.AttachHandlerForMessageType(msg => { - if (FileHandler == null) + if (FileServingHandler == null) { client.RPC.Send(new StoragePublisherFileMessage { @@ -391,7 +391,7 @@ namespace NPSharp return; } - byte[] data = FileHandler.ReadPublisherFile(client, msg.FileName); + var data = FileServingHandler.ReadPublisherFile(client, msg.FileName); if (data == null) { client.RPC.Send(new StoragePublisherFileMessage @@ -408,7 +408,8 @@ namespace NPSharp { MessageId = msg.MessageId, Result = 0, - FileName = msg.FileName + FileName = msg.FileName, + FileData = data }); _log.DebugFormat("Sent publisher file {0}", msg.FileName); } @@ -426,7 +427,7 @@ namespace NPSharp client.RPC.AttachHandlerForMessageType(msg => { - if (FileHandler == null) + if (FileServingHandler == null) { client.RPC.Send(new StorageUserFileMessage { @@ -452,7 +453,7 @@ namespace NPSharp return; } - byte[] data = FileHandler.ReadUserFile(client, msg.FileName); + byte[] data = FileServingHandler.ReadUserFile(client, msg.FileName); if (data == null) { client.RPC.Send(new StorageUserFileMessage @@ -495,7 +496,7 @@ namespace NPSharp client.RPC.AttachHandlerForMessageType(msg => { - if (FileHandler == null) + if (FileServingHandler == null) { client.RPC.Send(new StorageWriteUserFileResultMessage { @@ -521,7 +522,7 @@ namespace NPSharp return; } - FileHandler.WriteUserFile(client, msg.FileName, msg.FileData); + FileServingHandler.WriteUserFile(client, msg.FileName, msg.FileData); client.RPC.Send(new StorageWriteUserFileResultMessage { @@ -552,8 +553,10 @@ namespace NPSharp #endregion _clients.Add(client); +#if !DEBUG try { +#endif _log.Debug("Client connected"); OnClientConnected(client); while (true) @@ -564,6 +567,7 @@ namespace NPSharp } _log.Debug("Client disconnected"); OnClientDisconnected(client); +#if !DEBUG } catch (Exception error) { @@ -571,6 +575,7 @@ namespace NPSharp client.RPC.Send(new CloseAppMessage {Reason = "Server-side error occurred, try again later."}); client.RPC.Close(); } +#endif _clients.Remove(client); } diff --git a/src/libnpsharp/NPServerClient.cs b/src/libnpsharp/NPServerClient.cs index 3b4def0..63ce923 100644 --- a/src/libnpsharp/NPServerClient.cs +++ b/src/libnpsharp/NPServerClient.cs @@ -26,7 +26,11 @@ namespace NPSharp public IEnumerable Friends { - get { return NP.FriendsHandler.GetFriends(this).ToArray(); } + get { + return NP.FriendsHandler == null + ? new FriendDetails[0] + : NP.FriendsHandler.GetFriends(this).ToArray(); + } } public IEnumerable FriendConnections diff --git a/src/libnpsharp/RPC/Messages/AuthenticateValidateTicketMessage.cs b/src/libnpsharp/RPC/Messages/AuthenticateValidateTicketMessage.cs index c6bc931..3b8e883 100644 --- a/src/libnpsharp/RPC/Messages/AuthenticateValidateTicketMessage.cs +++ b/src/libnpsharp/RPC/Messages/AuthenticateValidateTicketMessage.cs @@ -3,7 +3,7 @@ using ProtoBuf; namespace NPSharp.RPC.Messages { - [Packet(1003)] + [Packet(1004)] [ProtoContract] public sealed class AuthenticateValidateTicketMessage : RPCClientMessage { diff --git a/src/libnpsharp/RPC/Messages/RPCMessage.cs b/src/libnpsharp/RPC/Messages/RPCMessage.cs index d6b066f..24eda52 100644 --- a/src/libnpsharp/RPC/Messages/RPCMessage.cs +++ b/src/libnpsharp/RPC/Messages/RPCMessage.cs @@ -44,17 +44,33 @@ namespace NPSharp.RPC.Messages return null; } - int l = sock.Receive(header); - if (l == 0) + try { - Log.Debug("Received 0 bytes"); + var l = sock.Receive(header); + if (l == 0) + { + Log.Debug("Received 0 bytes"); + return null; + } + if (l < 16) + { + Log.ErrorFormat("Received incomplete header ({0} bytes of 16 wanted bytes)", l); + throw new ProtocolViolationException("Received incomplete header"); + } + } + catch (SocketException) + { + if (sock.Connected) + throw; return null; } - if (l < 16) +#if !DEBUG + catch (Exception error) { - Log.ErrorFormat("Received incomplete header ({0} bytes of 16 wanted bytes)", l); - throw new ProtocolViolationException("Received incomplete header"); + Log.Error("Error while reading from network socket", error) + return null; } +#endif uint signature, length, type, mid; using (var ms = new MemoryStream(header)) diff --git a/src/libnpsharp/Steam/CSteamID.cs b/src/libnpsharp/Steam/CSteamID.cs index 924a0e0..a0bb78d 100644 --- a/src/libnpsharp/Steam/CSteamID.cs +++ b/src/libnpsharp/Steam/CSteamID.cs @@ -212,7 +212,7 @@ namespace NPSharp.Steam if (ReferenceEquals(a, b)) return true; - if ((a == null) || (b == null)) + if (Equals(a, null) || Equals(b, null)) return false; return a.steamid.Data == b.steamid.Data; diff --git a/src/libnpsharp/libnpsharp.csproj b/src/libnpsharp/libnpsharp.csproj index ef672be..22fe9f2 100644 --- a/src/libnpsharp/libnpsharp.csproj +++ b/src/libnpsharp/libnpsharp.csproj @@ -42,19 +42,64 @@ $(SolutionDir)\bin\$(Configuration)\$(Platform)\ + + + ..\..\packages\log4net.2.0.3\lib\net40-full\log4net.dll + + + False + ..\..\packages\Microsoft.Data.Edm.5.2.0\lib\net40\Microsoft.Data.Edm.dll + + + False + ..\..\packages\Microsoft.Data.OData.5.2.0\lib\net40\Microsoft.Data.OData.dll + + + ..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.1.8.0.0\lib\net35-full\Microsoft.WindowsAzure.Configuration.dll + + + ..\..\packages\WindowsAzure.Storage.2.0.6.1\lib\net40\Microsoft.WindowsAzure.Storage.dll + + + ..\..\packages\Newtonsoft.Json.5.0.8\lib\net45\Newtonsoft.Json.dll + + + ..\..\packages\RavenDB.Client.2.5.2878\lib\net45\Raven.Abstractions.dll + + + ..\..\packages\RavenDB.Embedded.2.5.2878\lib\net45\Raven.Client.Embedded.dll + + + ..\..\packages\RavenDB.Client.2.5.2878\lib\net45\Raven.Client.Lightweight.dll + + + ..\..\packages\RavenDB.Database.2.5.2878\lib\net45\Raven.Database.dll + + + + + + False + ..\..\packages\System.Spatial.5.2.0\lib\net40\System.Spatial.dll + ..\..\packages\log4net.2.0.3\lib\net40-full\log4net.dll ..\..\packages\protobuf-net.2.0.0.668\lib\net40\protobuf-net.dll + + ..\..\packages\uHttpSharp.0.1.4.7\lib\net40\uhttpsharp.dll + + + @@ -103,7 +148,7 @@ - + diff --git a/src/npfile/Program.cs b/src/npfile/Program.cs index 1e2c4a0..6b2936f 100644 --- a/src/npfile/Program.cs +++ b/src/npfile/Program.cs @@ -108,7 +108,7 @@ namespace NPSharp.CommandLine.File log.Info("NP connection successful, authenticating..."); // Get session token - var ah = new AuthenticationHelper(hostname); + var ah = new SessionAuthenticationClient(hostname); try { ah.Authenticate(username, password); diff --git a/src/npmotd/Program.cs b/src/npmotd/Program.cs index bb54f63..329e1ef 100644 --- a/src/npmotd/Program.cs +++ b/src/npmotd/Program.cs @@ -94,7 +94,7 @@ namespace NPSharp.CommandLine.MOTD } // Get session token - var ah = new AuthenticationHelper(hostname); + var ah = new SessionAuthenticationClient(hostname); try { ah.Authenticate(username, password); diff --git a/src/npserv/App.config b/src/npserv/App.config index 8e15646..a2dd99c 100644 --- a/src/npserv/App.config +++ b/src/npserv/App.config @@ -1,6 +1,14 @@ - + + + + + + + + + \ No newline at end of file diff --git a/src/npserv/DummyAuthenticationHandler.cs b/src/npserv/DummyAuthenticationHandler.cs index bc98bf2..1e44fe2 100644 --- a/src/npserv/DummyAuthenticationHandler.cs +++ b/src/npserv/DummyAuthenticationHandler.cs @@ -9,18 +9,16 @@ namespace NPSharp.CommandLine.Server { class DummyAuthenticationHandler : IAuthenticationHandler { - private uint _userID = 0; - - public DummyAuthenticationHandler() - { - // TODO: Listener on port 12003 accepting HTTP token retrievals - } + private uint _userID = 1; public AuthenticationResult AuthenticateUser(NPServerClient client, string username, string password) { return new AuthenticationResult(new CSteamID() { - AccountID = _userID++ + AccountID = _userID++, + AccountInstance = 1, + AccountType = EAccountType.Individual, + AccountUniverse = EUniverse.Public }); } @@ -28,7 +26,10 @@ namespace NPSharp.CommandLine.Server { return new AuthenticationResult(new CSteamID() { - AccountID = _userID++ + AccountID = _userID++, + AccountInstance = 1, + AccountType = EAccountType.Individual, + AccountUniverse = EUniverse.Public }); } diff --git a/src/npserv/DummyFileServingHandler.cs b/src/npserv/DummyFileServingHandler.cs new file mode 100644 index 0000000..df9dbcc --- /dev/null +++ b/src/npserv/DummyFileServingHandler.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NPSharp.CommandLine.Server +{ + class DummyFileServingHandler : IFileServingHandler + { + public byte[] ReadUserFile(NPServerClient client, string file) + { + return new byte[0]; + } + + public byte[] ReadPublisherFile(NPServerClient client, string file) + { + switch (file.ToLower()) + { + case "hello_world.txt": + return Encoding.UTF8.GetBytes("Hi, this is a test hello_world.txt."); + case "motd-english.txt": + return + Encoding.UTF8.GetBytes( + "Hello, this is a test NP server written in C#. Thanks for visiting this server."); + } + return null; + } + + public void WriteUserFile(NPServerClient client, string file, byte[] data) + { + // Ignore stuff + } + } +} diff --git a/src/npserv/Program.cs b/src/npserv/Program.cs index 32fd45c..720eb2e 100644 --- a/src/npserv/Program.cs +++ b/src/npserv/Program.cs @@ -5,6 +5,7 @@ using log4net.Appender; using log4net.Config; using log4net.Core; using log4net.Layout; +using NPSharp.Authentication; namespace NPSharp.CommandLine.Server { @@ -70,14 +71,28 @@ namespace NPSharp.CommandLine.Server var log = LogManager.GetLogger("Main"); + log.Info("Now starting authentication server..."); + var auth = new SessionAuthenticationServer(); + auth.Authenticating += (user, pw) => new SessionAuthenticationResult() + { + SessionToken = Guid.NewGuid().ToString("N"), + UserID = 1, + Success = true, + UserMail = "anonymous@localhost", + UserName = "anonymous" + }; + auth.Start(); + log.Info("Authentication server started up successfully."); + log.Info("Now starting NP server..."); var np = new NPServer(3036) { AuthenticationHandler = new DummyAuthenticationHandler(), + FileServingHandler = new DummyFileServingHandler() // TODO: Implement the other handlers }; np.Start(); - log.Info("NP server started up and is now ready."); + log.Info("NP server started up successfully."); Thread.Sleep(Timeout.Infinite); } diff --git a/src/npserv/npserv.csproj b/src/npserv/npserv.csproj index 43c3d62..678892d 100644 --- a/src/npserv/npserv.csproj +++ b/src/npserv/npserv.csproj @@ -42,11 +42,53 @@ ..\..\packages\log4net.2.0.3\lib\net40-full\log4net.dll + + False + ..\..\packages\Microsoft.Data.Edm.5.2.0\lib\net40\Microsoft.Data.Edm.dll + + + False + ..\..\packages\Microsoft.Data.OData.5.2.0\lib\net40\Microsoft.Data.OData.dll + + + ..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.1.8.0.0\lib\net35-full\Microsoft.WindowsAzure.Configuration.dll + + + ..\..\packages\WindowsAzure.Storage.2.0.6.1\lib\net40\Microsoft.WindowsAzure.Storage.dll + + + ..\..\packages\Newtonsoft.Json.5.0.8\lib\net45\Newtonsoft.Json.dll + + + ..\..\packages\RavenDB.Client.2.5.2878\lib\net45\Raven.Abstractions.dll + + + ..\..\packages\RavenDB.Embedded.2.5.2878\lib\net45\Raven.Client.Embedded.dll + + + ..\..\packages\RavenDB.Client.2.5.2878\lib\net45\Raven.Client.Lightweight.dll + + + ..\..\packages\RavenDB.Database.2.5.2878\lib\net45\Raven.Database.dll + + + + + + False + ..\..\packages\System.Spatial.5.2.0\lib\net40\System.Spatial.dll + + + + + ..\..\packages\uHttpSharp.0.1.4.7\lib\net40\uhttpsharp.dll + + diff --git a/src/npserv/packages.config b/src/npserv/packages.config index 3a86f25..3c5f228 100644 --- a/src/npserv/packages.config +++ b/src/npserv/packages.config @@ -1,4 +1,7 @@  + + + \ No newline at end of file