diff --git a/libnpsharp.sln b/libnpsharp.sln index 0ce2d38..26f0ce2 100644 --- a/libnpsharp.sln +++ b/libnpsharp.sln @@ -18,8 +18,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{F8 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NPSharp.Client", "src\client\NPSharp.Client.csproj", "{C6F941A5-82AF-456A-9B3A-752E5B001035}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NPSharp.Server", "src\server\NPSharp.Server.csproj", "{1A5AC63A-250E-4BC8-B81A-822AC31F5E37}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/src/server/Authentication/SessionAuthenticationServer.cs b/src/server/Authentication/SessionAuthenticationServer.cs deleted file mode 100644 index ace5a6f..0000000 --- a/src/server/Authentication/SessionAuthenticationServer.cs +++ /dev/null @@ -1,137 +0,0 @@ -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 -{ - /// - /// Represents a session authentication server which uses the HTTP protocol to send out session tokens to - /// authenticating NP clients. - /// - public class SessionAuthenticationServer - { - private readonly ILog _log; - private HttpServer _http; - - /// - /// Constructs a new session authentication server. - /// - public SessionAuthenticationServer() - { - SupportOldAuthentication = true; - _log = LogManager.GetLogger("Auth"); - } - - /// - /// Support oldskool "user&&pass" authentication format. - /// - public bool SupportOldAuthentication { get; set; } - - /// - /// Will be triggered whenever a client tries to authenticate via this server. - /// - 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"}; - } - - /// - /// Starts the authentication server. - /// - /// The port on which the authentication server should listen on. - public void Start(ushort port = 12003) - { - if (_http != null) - { - throw new InvalidOperationException("This server is already running"); - } - - _log.Debug("Starting authentication server..."); - _http = new HttpServer(new HttpRequestProvider()); - _http.Use(new TcpListenerAdapter(new TcpListener(IPAddress.Any, port))); - _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(); - _log.Debug("Done starting authentication server."); - } - - /// - /// Stops the authentication server. - /// - public void Stop() - { - _http.Dispose(); - } - - protected class AuthenticateHandler : IHttpRequestHandler - { - private readonly SessionAuthenticationServer _authServer; - - public AuthenticateHandler(SessionAuthenticationServer sessionAuthenticationServer) - { - _authServer = sessionAuthenticationServer; - } - - public Task Handle(IHttpContext context, Func next) - { - SessionAuthenticationResult sar; - - string username; - string password; - - if (!context.Request.Post.Parsed.TryGetByName("user", out username) || - !context.Request.Post.Parsed.TryGetByName("pass", out password)) - { - var login = Encoding.UTF8.GetString(context.Request.Post.Raw) - .Split(new[] {"&&"}, StringSplitOptions.None); - if (login.Length != 2) - { - sar = new SessionAuthenticationResult {Reason = @"Invalid login data"}; - - context.Response = new HttpResponse(HttpResponseCode.Ok, sar.ToString(), - context.Request.Headers.KeepAliveConnection()); - return Task.Factory.GetCompleted(); - } - - username = login[0]; - password = login[1]; - } - - try - { - sar = _authServer.OnAuthenticating(username, password); - } - catch (Exception error) - { - _authServer._log.Error(@"Authentication handler error", error); - sar = new SessionAuthenticationResult {Reason = @"Internal server error"}; - } - - _authServer._log.DebugFormat("/authenticate reply is {0}", sar); - - context.Response = new HttpResponse(HttpResponseCode.Ok, sar.ToString(), - !sar.Success && context.Request.Headers.KeepAliveConnection()); - return Task.Factory.GetCompleted(); - } - } - } -} \ No newline at end of file diff --git a/src/server/Events/ClientEventArgs.cs b/src/server/Events/ClientEventArgs.cs deleted file mode 100644 index 7abadbc..0000000 --- a/src/server/Events/ClientEventArgs.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using NPSharp.NP; - -namespace NPSharp.Events -{ - public class ClientEventArgs : EventArgs - { - internal ClientEventArgs(NPServerClient client) - { - Client = client; - } - - public NPServerClient Client { get; private set; } - } -} \ No newline at end of file diff --git a/src/server/Events/ClientEventHandler.cs b/src/server/Events/ClientEventHandler.cs deleted file mode 100644 index f46d5f5..0000000 --- a/src/server/Events/ClientEventHandler.cs +++ /dev/null @@ -1,11 +0,0 @@ -using NPSharp.NP; - -namespace NPSharp.Events -{ - /// - /// A delegate for all general client-related events. - /// - /// The instance of the NP server - /// All related arguments to this event - public delegate void ClientEventHandler(NPServer sender, ClientEventArgs args); -} \ No newline at end of file diff --git a/src/server/Handlers/IAuthenticationHandler.cs b/src/server/Handlers/IAuthenticationHandler.cs deleted file mode 100644 index 865e2f1..0000000 --- a/src/server/Handlers/IAuthenticationHandler.cs +++ /dev/null @@ -1,44 +0,0 @@ -using NPSharp.NP; -using NPSharp.RPC.Messages.Data; - -namespace NPSharp.Handlers -{ - /// - /// Represents a handler for all authentication-related requests. - /// - public interface IAuthenticationHandler - { - /// - /// Authenticates a user based on username and password. - /// - /// The NP server client to authenticate - /// The username to use for authentication - /// The password to use for authentication - /// An instance of - NPAuthenticationResult AuthenticateUser(NPServerClient client, string username, string password); - - /// - /// Authenticates a user based on a session token. - /// - /// The NP server client to authenticate - /// The session token to use for authentication - /// An instance of - NPAuthenticationResult AuthenticateUser(NPServerClient client, string token); - - /// - /// Authenticates a dedicated server based on its license key. - /// - /// The NP server client of the dedicated server to authenticate - /// The license key to use for authentication - /// An instance of - NPAuthenticationResult AuthenticateServer(NPServerClient client, string licenseKey); - - /// - /// Validates a ticket. - /// - /// The NP server client of the user who is trying to get the ticket validated - /// The server that the user wants to connect to using this ticket - /// A determining if the ticket is valid - TicketValidationResult ValidateTicket(NPServerClient client, NPServerClient server); - } -} \ No newline at end of file diff --git a/src/server/Handlers/IFileServingHandler.cs b/src/server/Handlers/IFileServingHandler.cs deleted file mode 100644 index 4de4e9c..0000000 --- a/src/server/Handlers/IFileServingHandler.cs +++ /dev/null @@ -1,34 +0,0 @@ -using NPSharp.NP; - -namespace NPSharp.Handlers -{ - /// - /// Represents a handler for all file-related requests. - /// - public interface IFileServingHandler - { - /// - /// Reads a file assigned to the user. - /// - /// The file contents as byte array or null if the file could not be read, opened or generated - /// NP server client of the user - /// The file name - byte[] ReadUserFile(NPServerClient client, string file); - - /// - /// Reads a publisher file. - /// - /// The file contents as byte array or null if the file could not be read, opened or generated - /// NP server client of the user - /// The file name - byte[] ReadPublisherFile(NPServerClient client, string file); - - /// - /// Writes a file and assigns it to the client user. - /// - /// NP server client of the user - /// The file name - /// The file contents as byte array - void WriteUserFile(NPServerClient client, string file, byte[] data); - } -} \ No newline at end of file diff --git a/src/server/Handlers/IFriendsHandler.cs b/src/server/Handlers/IFriendsHandler.cs deleted file mode 100644 index 73694a5..0000000 --- a/src/server/Handlers/IFriendsHandler.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; -using NPSharp.NP; -using NPSharp.RPC.Messages.Data; - -namespace NPSharp.Handlers -{ - /// - /// Represents a handler for all friends-related requests. - /// - public interface IFriendsHandler - { - /// - /// Fetches all friends of the connected user. - /// - /// The NP server client of the user - /// All friend details found for the user - IEnumerable GetFriends(NPServerClient client); - } -} \ No newline at end of file diff --git a/src/server/Handlers/IUserAvatarHandler.cs b/src/server/Handlers/IUserAvatarHandler.cs deleted file mode 100644 index cfd0f4a..0000000 --- a/src/server/Handlers/IUserAvatarHandler.cs +++ /dev/null @@ -1,14 +0,0 @@ -using NPSharp.Steam; - -namespace NPSharp.Handlers -{ - /// - /// Represents a handler for all user avatar-related requests. - /// - public interface IUserAvatarHandler - { - byte[] GetUserAvatar(CSteamID id); - - byte[] GetDefaultAvatar(); - } -} \ No newline at end of file diff --git a/src/server/Master/MasterServer.cs b/src/server/Master/MasterServer.cs deleted file mode 100644 index d8eb93c..0000000 --- a/src/server/Master/MasterServer.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Threading.Tasks; -using log4net; -using NPSharp.Master.Messages; -using NPSharp.Master.Messages.Client; - -namespace NPSharp.Master -{ - public class MasterServer - { - // TODO: !! Avoid socket fail if stopping then restarting - - private readonly List>> _callbacks = - new List>>(); - - private readonly ILog _log; - private readonly ushort _port; - - // TODO: Use the same kind of interfaces as in NP server to handle server addition and deletion - private readonly List _registeredServers = new List(); - private readonly Socket _socket4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - private readonly Socket _socket6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); - - public MasterServer(ushort port = 20810) - { - _port = port; - _log = LogManager.GetLogger("MasterServer"); - - // Internal callbacks - AddCallback(messages => { }); - } - - internal void AddCallback(Action callback) where T : MasterClientMessage - { - _callbacks.Add( - new KeyValuePair>( - typeof (T), - msg => callback.Invoke((T) msg))); - } - - /// - /// Starts up the NP server. - /// - public void Start() - { - if (_socket4.IsBound || _socket6.IsBound) - throw new InvalidOperationException("This server is already running"); - - try - { - // ReSharper disable once ObjectCreationAsStatement - // TODO: fix this shit permission code - new SocketPermission(NetworkAccess.Accept, TransportType.Udp, "", _port); - } - catch - { - _log.Error("Socket permission request failed, can't start server."); - throw new SocketException(10013 /* Permission denied */); - } - - _socket4.Bind(new IPEndPoint(IPAddress.Any, _port)); - _socket4.Listen(100); - - _socket6.Bind(new IPEndPoint(IPAddress.IPv6Any, _port)); - _socket6.Listen(100); - - - // TODO: Implement IPv4 handling - - Task.Factory.StartNew(() => - { - _log.Debug("Listener loop (IPv6) started"); - - while (_socket6 != null && _socket6.IsBound) - { - var mergedBuffer = new List(); - while (true) - { - var buffer = new byte[1400]; - var clientEndPoint = (EndPoint) new IPEndPoint(IPAddress.IPv6Any, 0); - var recvLength = _socket6.ReceiveFrom(buffer, ref clientEndPoint); - if (recvLength <= buffer.Length) - mergedBuffer.AddRange(buffer); - if (recvLength < 1400) - break; - _handleClient(buffer, clientEndPoint); - } - } - _log.Debug("Listener loop (IPv6) shut down"); - }); - } - - private void _handleClient(byte[] buffer, EndPoint ep) - { - _log.DebugFormat("Handle client {0}", ep); - - var message = MasterClientMessage.Deserialize(buffer); - if (message == null) - { - _log.WarnFormat("Received invalid or empty request from {0}", ep); - return; - } - - // Invoke (internal) callbacks for fitting message types - foreach (var callback in _callbacks.Where(i => i.Key == message.GetType()).Select(i => i.Value)) - { - callback.Invoke(message); - } - - _log.DebugFormat("Not handling client {0} anymore", ep); - } - } -} \ No newline at end of file diff --git a/src/server/Master/README.txt b/src/server/Master/README.txt deleted file mode 100644 index 439f495..0000000 --- a/src/server/Master/README.txt +++ /dev/null @@ -1,97 +0,0 @@ -# Master API source code - -This folder is supposed to contain the source code that will provide both, a master server and client. - -alterIWnet and fourdeltaone made use of the open-sourced DP master server code to serve registration of dedicated servers -and the dedicated server list for the game client. - -This server is supposed to be replaced by a full implementation of the protocol, both as server and client, in C# and is -to be integrated into libnpsharp. - -## Protocol - -The master server protocol is based on Quake 3's (http://src.gnu-darwin.org/ports/games/masterserver/work/masterserver-0.4.1/docs/PROTOCOLS): - -Request message: - AA AA AA AA|BB .. .. BB| A=Header, B=Body ending with line break or \x00 - -Response message: - AA .. .. AA|0a|BB .. .. A=Message name, B=Body - BB - -Important: The response is supposed to be one or multiple specific-length plus a less-than-specific-length -UDP packet(s). - -TODO: Look up the specific length in the DP master source code. It's somewhere around 1400/1500 bytes. - -The master server itself has to only serve for the "getservers" request and has to respond with a getServersResponse message. -This getServersResponse message's body is serialized as following: - - XX AA AA AA AA BB BB[CC X=ascii("\\"), A=IP (uint, network byte order), B=Port 1 (ushort, network byte order), C=Optional port 2 (ushort, network byte order) - CC[..] ]XX .. .. .. .. - XX YY YY YY 00 00 00[00 Y=ascii("EOT") - 00] - -In short, serialized ip-port pairs/triples separated by ASCII backslashes and ended by an entry which decodes to "EOT". - -## Protocol limits - -(see https://github.com/kphillisjr/dpmaster/blob/master/src/messages.c for reference) - -```c -// Timeout after a valid infoResponse (in secondes) -#define TIMEOUT_INFORESPONSE (15 * 60) - -// Period of validity for a challenge string (in secondes) -#define TIMEOUT_CHALLENGE 2 - -// Maximum size of a reponse packet -#define MAX_PACKET_SIZE_OUT 1400 -``` - -## Internal management - -Both the server and the client should be able to handle a variable amount of ports. For this, we plan following things: - -1) Assume "\" at the beginning of an entry. If not true, stream out of sync -> throw exception/disconnect. -2) Assume an IP address exists (4 bytes minimum). - a) If IP address is ASCII "EOT" with \x00 bytes at the end, stop reading the message. -3) Do not assume a fixed amount of ports. Instead - a) ...if rest > 0 bytes, read 2 bytes and append to a ushort list for ports. - b) ...if rest == 0 bytes, prepare for reading next entry. - -This should be compatible with both, messages with 1 IP address + 1 port (game) and 1 IP address + 2 ports (game/query). - -## Messages - -(see https://github.com/kphillisjr/dpmaster/blob/master/src/messages.c for reference) - -// Types of messages (with samples): - -// Q3: "heartbeat QuakeArena-1\x0A" -// DP: "heartbeat DarkPlaces\x0A" -#define S2M_HEARTBEAT "heartbeat " - -// Q3 & DP & QFusion: "getinfo A_Challenge" -#define M2S_GETINFO "getinfo" - -// Q3 & DP & QFusion: "infoResponse\x0A\\pure\\1\\..." -#define S2M_INFORESPONSE "infoResponse\x0A" - -// Q3: "getservers 67 ffa empty full" -// DP: "getservers DarkPlaces-Quake 3 empty full" -// DP: "getservers Transfusion 3 empty full" -// QFusion: "getservers qfusion 39 empty full" -#define C2M_GETSERVERS "getservers " - -// DP: "getserversExt DarkPlaces-Quake 3 empty full ipv4 ipv6" -// IOQuake3: "getserversExt 68 empty ipv6" -#define C2M_GETSERVERSEXT "getserversExt " - -// Q3 & DP & QFusion: -// "getserversResponse\\...(6 bytes)...\\...(6 bytes)...\\EOT\0\0\0" -#define M2C_GETSERVERSREPONSE "getserversResponse" - -// DP & IOQuake3: -// "getserversExtResponse\\...(6 bytes)...//...(18 bytes)...\\EOT\0\0\0" -#define M2C_GETSERVERSEXTREPONSE "getserversExtResponse" diff --git a/src/server/NP/NPServer.cs b/src/server/NP/NPServer.cs deleted file mode 100644 index 820f429..0000000 --- a/src/server/NP/NPServer.cs +++ /dev/null @@ -1,732 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using log4net; -using NPSharp.Events; -using NPSharp.Handlers; -using NPSharp.RPC; -using NPSharp.RPC.Messages.Client; -using NPSharp.RPC.Messages.Data; -using NPSharp.RPC.Messages.Server; -using NPSharp.Steam; - -namespace NPSharp.NP -{ - public class NPServer - { - private readonly List _clients; - private readonly ILog _log; - private readonly ushort _port; -#if MONO_INCOMPATIBLE - private readonly Socket _socket; -#else - private readonly Socket _socket4; - private readonly Socket _socket6; -#endif - - /// - /// Constructs a new NP server. - /// - public NPServer(ushort port = 3025) - { - _log = LogManager.GetLogger("NPServer"); - _clients = new List(); - -#if MONO_INCOMPATIBLE - // Mono can't compile this since the constructor is proprietary to Windows' .NET library - _socket = new Socket(SocketType.Stream, ProtocolType.IP); - - // Mono can't compile this either since the IPv6Only socket option is completely missing. - //_socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); - //_socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0); -#else - // So as much as this hurts me, I have to go with TWO sockets. - // Guys, this is why I hate network programming sometimes. SOMETIMES. - _socket4 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - _socket6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); -#endif - _port = port; - } - - /// - /// The handler to use for file requests to this NP server. - /// - public IFileServingHandler FileServingHandler { get; set; } - - /// - /// The handler to use for user avatar requests to this NP server. - /// - public IUserAvatarHandler UserAvatarHandler { get; set; } - - /// - /// Returns all currently connected clients - /// - public NPServerClient[] Clients - { - get { return _clients.ToArray(); } // Avoid race condition by IEnum changes - } - - /// - /// The handler to use for authentication requests to this NP server. - /// - public IAuthenticationHandler AuthenticationHandler { get; set; } - - /// - /// The handler to use for friends-related requests to this NP server. - /// - public IFriendsHandler FriendsHandler { get; set; } - - /// - /// Starts up the NP server. - /// - public void Start() - { - if ( -#if MONO_INCOMPATIBLE - _socket.IsBound -#else - _socket4.IsBound || _socket6.IsBound -#endif - ) - throw new InvalidOperationException("This server is already running"); - - try - { - // ReSharper disable once ObjectCreationAsStatement - // TODO: fix this shit permission code - new SocketPermission(NetworkAccess.Accept, TransportType.Tcp, "", _port); - } - catch - { - _log.Error("Socket permission request failed, can't start server."); - throw new SocketException(10013 /* Permission denied */); - } - -#if MONO_INCOMPATIBLE - _socket.Bind(new IPEndPoint(IPAddress.IPv6Any, _port)); - _socket.Listen(100); -#else - _socket4.Bind(new IPEndPoint(IPAddress.Any, _port)); - _socket4.Listen(100); - - _socket6.Bind(new IPEndPoint(IPAddress.IPv6Any, _port)); - _socket6.Listen(100); -#endif - - Task.Factory.StartNew(() => - { - _log.Debug("Listener loop started"); -#if MONO_INCOMPATIBLE - var socket = _socket; -#else - var socket = _socket4; -#endif - var allDone = new ManualResetEvent(false); - while ( -#if MONO_INCOMPATIBLE - _socket != null && _socket.IsBound -#else - _socket4 != null && _socket4.IsBound -#endif - ) - { - allDone.Reset(); - socket.BeginAccept(ar => - { - _log.Debug("Async accept client start"); - allDone.Set(); - - var serverSocket = (Socket) ar.AsyncState; - var clientSocket = serverSocket.EndAccept(ar); - - var npsc = new NPServerClient(this, new RPCServerStream(clientSocket)); - - _log.Debug("Async accept client end"); - - _handleClient(npsc); - }, socket); - allDone.WaitOne(); - } - _log.Debug("Listener loop shut down"); - }); - -#if !MONO_INCOMPATIBLE - Task.Factory.StartNew(() => - { - _log.Debug("Listener loop (IPv6) started"); - - var allDone = new ManualResetEvent(false); - while (_socket6 != null && _socket6.IsBound) - { - allDone.Reset(); - _socket6.BeginAccept(ar => - { - _log.Debug("Async accept (IPv6) client start"); - allDone.Set(); - - var serverSocket = (Socket) ar.AsyncState; - var clientSocket = serverSocket.EndAccept(ar); - - var npsc = new NPServerClient(this, new RPCServerStream(clientSocket)); - - _log.Debug("Async accept (IPv6) client end"); - - _handleClient(npsc); - }, _socket6); - allDone.WaitOne(); - } - _log.Debug("Listener loop (IPv6) shut down"); - }); - } -#endif - - /// - /// Shuts down all connections and stops the NP server. - /// - public void Stop() - { - // TODO: Wait for sockets to COMPLETELY shut down -#if MONO_INCOMPATIBLE - _socket.Shutdown(SocketShutdown.Both); -#else - _socket4.Shutdown(SocketShutdown.Both); - _socket6.Shutdown(SocketShutdown.Both); -#endif - } - - internal void _handleClient(NPServerClient client) - { - _log.Debug("Client now being handled"); - - #region RPC authentication message handlers - - client.RPC.AttachHandlerForMessageType(msg => - { - var result = new NPAuthenticationResult(); - if (AuthenticationHandler != null) - { - try - { - result = AuthenticationHandler.AuthenticateServer(client, msg.LicenseKey); - } - catch (Exception error) - { - _log.Error("Error occurred in authentication handler", error); - } - } - - // Send authentication result directly to client - client.RPC.Send(new AuthenticateResultMessage - { - NPID = result.UserID, - Result = result.Result ? 0 : 1, - SessionToken = string.Empty - }); - - // Authentication failed => close connection - if (!result.Result) - { - client.RPC.Close(); - return; - } - - // Assign login ID - client.UserID = result.UserID; - client.IsServer = true; - - OnClientAuthenticated(client); - }); - - client.RPC.AttachHandlerForMessageType(msg => - { - var result = new NPAuthenticationResult(); - if (AuthenticationHandler != null) - { - try - { - result = AuthenticationHandler.AuthenticateUser(client, msg.Username, msg.Password); - } - catch (Exception error) - { - _log.Error("Error occurred in authentication handler", error); - result = new NPAuthenticationResult(); - } - } - - // Send authentication result directly to client - client.RPC.Send(new AuthenticateResultMessage - { - NPID = result.UserID, - Result = result.Result ? 0 : 1, - SessionToken = string.Empty - }); - - // Authentication failed => close connection - if (!result.Result) - { - client.RPC.Close(); - return; - } - - // Assign login ID - client.UserID = result.UserID; - - // Send "online" notification to all friends of this player - foreach (var fconn in client.FriendConnections) - { - fconn.RPC.Send(new FriendsPresenceMessage - { - CurrentServer = client.DedicatedServer == null ? 0 : client.DedicatedServer.UserID, - Friend = client.UserID, - Presence = client.PresenceData, - PresenceState = client.DedicatedServer == null ? 1 : 2 - }); - } - - // Send friends roster to player - client.RPC.Send(new FriendsRosterMessage - { - Friends = client.Friends.ToArray() - }); - - OnClientAuthenticated(client); - }); - - client.RPC.AttachHandlerForMessageType(msg => - { - var result = new NPAuthenticationResult(); - if (AuthenticationHandler != null) - { - try - { - result = AuthenticationHandler.AuthenticateUser(client, msg.Token); - } - catch (Exception error) - { - _log.Error("Error occurred in authentication handler", error); - } - } - - // Send authentication result directly to client - client.RPC.Send(new AuthenticateResultMessage - { - NPID = result.UserID == null ? 0 : result.UserID.ConvertToUint64(), - Result = result.Result ? 0 : 1, - SessionToken = msg.Token - }); - - // Authentication failed => close connection - if (!result.Result) - { - client.RPC.Close(); - return; - } - - // Assign login ID - client.UserID = result.UserID; - - // Send "online" notification to all friends of this player - foreach (var fconn in client.FriendConnections) - { - fconn.RPC.Send(new FriendsPresenceMessage - { - CurrentServer = client.DedicatedServer == null ? 0 : client.DedicatedServer.UserID, - Friend = client.UserID, - Presence = client.PresenceData, - PresenceState = client.DedicatedServer == null ? 1 : 2 - }); - } - - // Send friends roster to player - client.RPC.Send(new FriendsRosterMessage - { - Friends = client.Friends.ToArray() - }); - - OnClientAuthenticated(client); - }); - - client.RPC.AttachHandlerForMessageType(msg => - { - var validTicket = false; - - if (!client.IsDirty) - { - Ticket ticketData = null; - - try - { - ticketData = new Ticket(msg.Ticket); - - _log.DebugFormat("Ticket[Version={0},ServerID={1},Time={2}]", ticketData.Version, - ticketData.ServerID, ticketData.Time); - } - catch (ArgumentException error) - { - _log.Warn("Got some weird-length ticket data", error); - } - - if (ticketData != null) - { - if (ticketData.Version == 1) // Version 1 enforcement - { - if (ticketData.ClientID == client.UserID) // NPID enforcement - { - var s = - _clients.Where(c => c.IsServer && !c.IsDirty && c.UserID == ticketData.ServerID) - .ToArray(); - - if (s.Any()) - { - // TODO: Time validation. Problem is some clocks go wrong by minutes! - client.DedicatedServer = s.First(); - validTicket = true; - _log.Debug("Ticket validated"); - } - else - { - _log.Warn("Ticket invalid, could not find any sane servers with requested server ID"); - } - } - else - { - _log.Warn("Ticket invalid, found NPID spoofing attempt"); - } - } - else - { - _log.Warn("Ticket invalid, found invalid version"); - } - } - } - else - { - _log.Warn("Ticket invalid, client is marked as dirty"); - } - - // Invalid data buffer - client.RPC.Send(new AuthenticateValidateTicketResultMessage - { - GroupID = client.GroupID, - NPID = client.UserID, - Result = validTicket ? 0 : 1 - }); - }); - - #endregion - - #region RPC friend message handlers - - client.RPC.AttachHandlerForMessageType(msg => - { - foreach (var pdata in msg.Presence) - { - client.SetPresence(pdata.Key, pdata.Value); - _log.DebugFormat("Client says presence \"{0}\" is \"{1}\"", pdata.Key, pdata.Value); - } - }); - - client.RPC.AttachHandlerForMessageType(msg => - { - // TODO: Not compatible with non-public accounts - var npid = new CSteamID((uint) msg.Guid, EUniverse.Public, - EAccountType.Individual).ConvertToUint64(); - - var avatar = UserAvatarHandler.GetUserAvatar(npid) ?? UserAvatarHandler.GetDefaultAvatar(); - - client.RPC.Send(new FriendsGetUserAvatarResultMessage - { - FileData = avatar, - Guid = msg.Guid, - Result = avatar != null ? 0 : 1 - }); - }); - - client.RPC.AttachHandlerForMessageType(msg => - { - // TODO - }); - - #endregion - - #region RPC storage message handlers - - client.RPC.AttachHandlerForMessageType(msg => - { - if (FileServingHandler == null) - { - client.RPC.Send(new StoragePublisherFileMessage - { - MessageId = msg.MessageId, - Result = 2, - FileName = msg.FileName - }); - return; - } - - try - { - if (client.UserID == null) - { - client.RPC.Send(new StoragePublisherFileMessage - { - MessageId = msg.MessageId, - Result = 2, - FileName = msg.FileName - }); - _log.WarnFormat("Client tried to read publisher file {0} while not logged in", msg.FileName); - return; - } - - var data = FileServingHandler.ReadPublisherFile(client, msg.FileName); - if (data == null) - { - client.RPC.Send(new StoragePublisherFileMessage - { - MessageId = msg.MessageId, - Result = 1, - FileName = msg.FileName - }); - _log.DebugFormat("Could not open publisher file {0}", msg.FileName); - return; - } - - client.RPC.Send(new StoragePublisherFileMessage - { - MessageId = msg.MessageId, - Result = 0, - FileName = msg.FileName, - FileData = data - }); - _log.DebugFormat("Sent publisher file {0}", msg.FileName); - } - catch (Exception error) - { - _log.Warn("GetPublisherFile handler error", error); - client.RPC.Send(new StoragePublisherFileMessage - { - MessageId = msg.MessageId, - Result = 2, - FileName = msg.FileName - }); - } - }); - - client.RPC.AttachHandlerForMessageType(msg => - { - if (FileServingHandler == null) - { - client.RPC.Send(new StorageUserFileMessage - { - MessageId = msg.MessageId, - Result = 2, - FileName = msg.FileName - }); - return; - } - - try - { - if (client.UserID == null) - { - client.RPC.Send(new StorageUserFileMessage - { - MessageId = msg.MessageId, - Result = 2, - FileName = msg.FileName, - NPID = client.UserID - }); - _log.WarnFormat("Client tried to read user file {0} while not logged in", msg.FileName); - return; - } - - var data = FileServingHandler.ReadUserFile(client, msg.FileName); - if (data == null) - { - client.RPC.Send(new StorageUserFileMessage - { - MessageId = msg.MessageId, - Result = 1, - FileName = msg.FileName, - NPID = client.UserID - }); - _log.DebugFormat("Could not open user file {0}", msg.FileName); - return; - } - - client.RPC.Send(new StorageUserFileMessage - { - MessageId = msg.MessageId, - Result = 0, - FileName = msg.FileName, - FileData = data, - NPID = client.UserID - }); - _log.DebugFormat("Sent user file {0}", msg.FileName); - } - catch (Exception error) - { - _log.Warn("GetUserFile handler error", error); - client.RPC.Send(new StorageUserFileMessage - { - MessageId = msg.MessageId, - Result = 2, - FileName = msg.FileName - }); - } - }); - - client.RPC.AttachHandlerForMessageType(msg => - { - // TODO: Handle "random string" messages - }); - - client.RPC.AttachHandlerForMessageType(msg => - { - if (FileServingHandler == null) - { - client.RPC.Send(new StorageWriteUserFileResultMessage - { - MessageId = msg.MessageId, - Result = 2, - FileName = msg.FileName - }); - return; - } - - try - { - if (client.UserID == null) - { - client.RPC.Send(new StorageWriteUserFileResultMessage - { - MessageId = msg.MessageId, - Result = 2, - FileName = msg.FileName, - NPID = client.UserID - }); - _log.WarnFormat("Client tried to write user file {0} while not logged in", msg.FileName); - return; - } - - FileServingHandler.WriteUserFile(client, msg.FileName, msg.FileData); - - client.RPC.Send(new StorageWriteUserFileResultMessage - { - MessageId = msg.MessageId, - Result = 0, - FileName = msg.FileName, - NPID = client.UserID - }); - _log.DebugFormat("Received and wrote user file {0}", msg.FileName); - } - catch (Exception error) - { - _log.Warn("WriteUserFile handler error", error); - client.RPC.Send(new StorageWriteUserFileResultMessage - { - MessageId = msg.MessageId, - Result = 2, - FileName = msg.FileName - }); - } - }); - // TODO: RPC message handling for MessagingSendData - - #endregion - - #region RPC server session message handler - - #endregion - - _clients.Add(client); -#if !DEBUG - try - { -#endif - _log.Debug("Client connected"); - OnClientConnected(client); -#if !DEBUG - try -#endif - { - while (true) - { - var msg = client.RPC.Read(); - if (msg == null) - break; - } - } -#if !DEBUG - catch (Exception error) - { - _log.Error("Error in RPC read loop", error); - } -#endif - _log.Debug("Client disconnected"); - OnClientDisconnected(client); -#if !DEBUG - } - catch (Exception error) - { - _log.Error("Error in client handling loop", error); - client.RPC.Send(new CloseAppMessage {Reason = "Server-side error occurred, try again later."}); - client.RPC.Close(); - } -#endif - _clients.Remove(client); - } - - /// - /// Triggered when a client has connected but is not authenticating yet. - /// - public event ClientEventHandler ClientConnected; - - /// - /// Invokes the event. - /// - /// The client - protected virtual void OnClientConnected(NPServerClient client) - { - var handler = ClientConnected; - var args = new ClientEventArgs(client); - if (handler != null) handler(this, args); - } - - /// - /// Triggered when a client has disconnected. - /// - public event ClientEventHandler ClientDisconnected; - - /// - /// Invokes the event. - /// - /// The client - protected virtual void OnClientDisconnected(NPServerClient client) - { - var handler = ClientDisconnected; - var args = new ClientEventArgs(client); - if (handler != null) handler(this, args); - } - - /// - /// Triggered when a client has authenticated successfully. - /// - public event ClientEventHandler ClientAuthenticated; - - /// - /// Invokes the event. - /// - /// The client - protected virtual void OnClientAuthenticated(NPServerClient client) - { - var handler = ClientAuthenticated; - var args = new ClientEventArgs(client); - if (handler != null) handler(this, args); - } - } -} \ No newline at end of file diff --git a/src/server/NP/NPServerClient.cs b/src/server/NP/NPServerClient.cs deleted file mode 100644 index e569d8a..0000000 --- a/src/server/NP/NPServerClient.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using NPSharp.RPC; -using NPSharp.RPC.Messages.Data; -using NPSharp.Steam; - -namespace NPSharp.NP -{ - /// - /// Represents a remote client connection to an NP server. - /// - public class NPServerClient - { - internal readonly NPServer NP; - internal readonly RPCServerStream RPC; - private readonly Dictionary _presence = new Dictionary(); - internal NPServerClient DedicatedServer; - - internal NPServerClient(NPServer np, RPCServerStream rpcclient) - { - NP = np; - RPC = rpcclient; - } - - public CSteamID UserID { get; internal set; } - - public IEnumerable Friends - { - get - { - return NP.FriendsHandler == null - ? new FriendDetails[0] - : NP.FriendsHandler.GetFriends(this).ToArray(); - } - } - - public IEnumerable FriendConnections - { - get { return NP.Clients.Where(c => Friends.Any(f => f.NPID == c.UserID)); } - } - - public FriendsPresence[] PresenceData - { - get { return _presence.Select(i => new FriendsPresence {Key = i.Key, Value = i.Value}).ToArray(); } - } - - public bool IsServer { get; set; } - public bool IsDirty { get; set; } - public int GroupID { get; set; } - - internal void SetPresence(string key, string value) - { - if (!_presence.ContainsKey(key)) - _presence.Add(key, value); - else - _presence[key] = value; - } - } -} \ No newline at end of file diff --git a/src/server/NPSharp.Server.csproj b/src/server/NPSharp.Server.csproj deleted file mode 100644 index 1d3dd18..0000000 --- a/src/server/NPSharp.Server.csproj +++ /dev/null @@ -1,93 +0,0 @@ - - - - - Debug - AnyCPU - {1A5AC63A-250E-4BC8-B81A-822AC31F5E37} - Library - Properties - NPSharp - npsharp_server - v4.5 - 512 - - 12.0.0 - 2.0 - ..\..\ - true - - - true - full - false - TRACE;DEBUG;COMPILE_RPC,COMPILE_NP,COMPILE_AUTH - prompt - 4 - false - - - pdbonly - true - TRACE;COMPILE_RPC,COMPILE_NP,COMPILE_AUTH - prompt - 4 - false - - - $(SolutionDir)\bin\$(Configuration)\$(Platform)\ - $(SolutionDir)\obj\$(TargetName)\$(Configuration)\$(Platform)\ - $(SolutionDir)\obj\$(TargetName)\$(Configuration)\$(Platform)\ - $(SolutionDir)\obj\$(TargetName)\$(Configuration)\$(Platform)\ - $(SolutionDir)\bin\$(Configuration)\$(Platform)\ - - - - $(SolutionDir)\packages\log4net.2.0.3\lib\net40-full\log4net.dll - - - $(SolutionDir)\packages\Newtonsoft.Json.6.0.3\lib\net45\Newtonsoft.Json.dll - - - - False - $(SolutionDir)\packages\uHttpSharp.0.1.5.4\lib\net40\uhttpsharp.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - {c6f941a5-82af-456a-9b3a-752e5b001035} - NPSharp.Client - - - - \ No newline at end of file diff --git a/src/server/Properties/AssemblyInfo.cs b/src/server/Properties/AssemblyInfo.cs deleted file mode 100644 index 2291f1e..0000000 --- a/src/server/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// Allgemeine Informationen über eine Assembly werden über die folgenden -// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, -// die mit einer Assembly verknüpft sind. - -[assembly: AssemblyTitle("NPSharp server library")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Carl Kittelberger")] -[assembly: AssemblyProduct("NPSharp")] -[assembly: AssemblyCopyright("© 2014 Carl Kittelberger")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar -// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von -// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. - -[assembly: ComVisible(false)] - -// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird - -[assembly: Guid("0efdd0b5-fd69-48fd-b714-f4f0e032f417")] - -// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: -// -// Hauptversion -// Nebenversion -// Buildnummer -// Revision -// -// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern -// übernehmen, indem Sie "*" eingeben: -// [assembly: AssemblyVersion("1.0.*")] - -[assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyFileVersion("1.0")] \ No newline at end of file diff --git a/src/server/RPC/RPCServerStream.cs b/src/server/RPC/RPCServerStream.cs deleted file mode 100644 index fb24e91..0000000 --- a/src/server/RPC/RPCServerStream.cs +++ /dev/null @@ -1,19 +0,0 @@ -#if COMPILE_RPC||COMPILE_NP - -using System.Net.Sockets; -using NPSharp.RPC.Messages; - -namespace NPSharp.RPC -{ - /// - /// Represents a low-level stream which communicates with an NP client using RPC messages. - /// - public class RPCServerStream : RPCStream - { - public RPCServerStream(Socket sock) : base(sock) - { - } - } -} - -#endif \ No newline at end of file diff --git a/src/server/app.config b/src/server/app.config deleted file mode 100644 index e63f192..0000000 --- a/src/server/app.config +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/src/server/packages.config b/src/server/packages.config deleted file mode 100644 index 4b9179d..0000000 --- a/src/server/packages.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file