//----------------------------------------------------------------------------------- // // Torque Network Library // Copyright (C) 2004 GarageGames.com, Inc. // For more information see http://www.opentnl.org // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // For use in products that are not compatible with the terms of the GNU // General Public License, alternative licensing options are available // from GarageGames.com. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //------------------------------------------------------------------------------------ #ifndef _TNL_NETINTERFACE_H_ #define _TNL_NETINTERFACE_H_ #ifndef _TNL_VECTOR_H_ #include "tnlVector.h" #endif #ifndef _TNL_NETBASE_H_ #include "tnlNetBase.h" #endif #include "tnlClientPuzzle.h" #ifndef _TNL_NETOBJECT_H_ #include "tnlNetObject.h" #endif #ifndef _TNL_NETCONNECTION_H_ #include "tnlNetConnection.h" #endif namespace TNL { class AsymmetricKey; class Certificate; struct ConnectionParameters; /// NetInterface class. /// /// Manages all valid and pending notify protocol connections for a port/IP. If you are /// providing multiple services or servicing multiple networks, you may have more than /// one NetInterface. /// /// Connection handshaking basic overview: /// /// TNL does a two phase connect handshake to prevent a several types of /// Denial-of-Service (DoS) attacks. /// /// The initiator of the connection (client) starts the connection by sending /// a unique random nonce (number, used once) value to the server as part of /// the ConnectChallengeRequest packet. /// C->S: ConnectChallengeRequest, Nc /// /// The server responds to the ConnectChallengeRequest with a "Client Puzzle" /// that has the property that verifying a solution to the puzzle is computationally /// simple, but can be of a specified computational, brute-force difficulty to /// compute the solution itself. The client puzzle is of the form: /// secureHash(Ic, Nc, Ns, X) = Y >> k, where Ic is the identity of the client, /// and X is a value computed by the client such that the high k bits of the value /// y are all zero. The client identity is computed by the server as a partial hash /// of the client's IP address and port and some random data on the server. /// its current nonce (Ns), Nc, k, and the server's authentication certificate. /// S->C: ConnectChallengeResponse, Nc, Ns, Ic, Cs /// /// The client, upon receipt of the ConnectChallengeResponse, validates the packet /// sent by the server and computes a solution to the puzzle the server sent. If /// the connection is to be authenticated, the client can also validate the server's /// certificate (if it's been signed by a Certificate Authority), and then generates /// a shared secret from the client's key pair and the server's public key. The client /// response to the server consists of: /// C->S: ConnectRequest, Nc, Ns, X, Cc, sharedSecret(key1, sequence1, NetConnectionClass, class-specific sendData) /// /// The server then can validation the solution to the puzzle the client submitted, along /// with the client identity (Ic). /// Until this point the server has allocated no memory for the client and has /// verified that the client is sending from a valid IP address, and that the client /// has done some amount of work to prove its willingness to start a connection. /// As the server load increases, the server may choose to increase the difficulty (k) of /// the client puzzle, thereby making a resource depletion DoS attack successively more /// difficult to launch. /// /// If the server accepts the connection, it sends a connect accept packet that is /// encrypted and hashed using the shared secret. The contents of the packet are /// another sequence number (sequence2) and another key (key2). The sequence numbers /// are the initial send and receive sequence numbers for the connection, and the /// key2 value becomes the IV of the symmetric cipher. The connection subclass is /// also allowed to write any connection specific data into this packet. /// /// This system can operate in one of 3 ways: unencrypted, encrypted key exchange (ECDH), /// or encrypted key exchange with server and/or client signed certificates (ECDSA). /// /// The unencrypted communication mode is NOT secure. Packets en route between hosts /// can be modified without detection by the hosts at either end. Connections using /// the secure key exchange are still vulnerable to Man-in-the-middle attacks, but still /// much more secure than the unencrypted channels. Using certificate(s) signed by a /// trusted certificate authority (CA), makes the communications channel as securely /// trusted as the trust in the CA. /// /// Arranged Connection handshaking: /// /// NetInterface can also facilitate "arranged" connections. Arranged connections are /// necessary when both parties to the connection are behind firewalls or NAT routers. /// Suppose there are two clients, A and B that want to esablish a direct connection with /// one another. If A and B are both logged into some common server S, then S can send /// A and B the public (NAT'd) address, as well as the IP addresses each client detects /// for itself. /// /// A and B then both send "Punch" packets to the known possible addresses of each other. /// The punch packet client A sends enables the punch packets client B sends to be /// delivered through the router or firewall since it will appear as though it is a service /// response to A's initial packet. /// /// Upon receipt of the Punch packet by the "initiator" /// of the connection, an ArrangedConnectRequest packet is sent. /// if the non-initiator of the connection gets an ArrangedPunch /// packet, it simply sends another Punch packet to the /// remote host, but narrows down its Address range to the address /// it received the packet from. /// The ArrangedPunch packet from the intiator contains the nonce /// for the non-initiator, and the nonce for the initiator encrypted /// with the shared secret. /// The ArrangedPunch packet for the receiver of the connection /// contains all that, plus the public key/keysize or the certificate /// of the receiver. class NetInterface : public Object { friend class NetConnection; public: /// PacketType is encoded as the first byte of each packet. /// /// Subclasses of NetInterface can add custom, non-connected data /// packet types starting at FirstValidInfoPacketId, and overriding /// handleInfoPacket to process them. /// /// Packets that arrive with the high bit of the first byte set /// (i.e. the first unsigned byte is greater than 127), are /// assumed to be connected protocol packets, and are dispatched to /// the appropriate connection for further processing. enum PacketType { ConnectChallengeRequest = 0, ///< Initial packet of the two-phase connect process ConnectChallengeResponse = 1, ///< Response packet to the ChallengeRequest containing client identity, a client puzzle, and possibly the server's public key. ConnectRequest = 2, ///< A connect request packet, including all puzzle solution data and connection initiation data. ConnectReject = 3, ///< A packet sent to notify a host that a ConnectRequest was rejected. ConnectAccept = 4, ///< A packet sent to notify a host that a connection was accepted. Disconnect = 5, ///< A packet sent to notify a host that the specified connection has terminated. Punch = 6, ///< A packet sent in order to create a hole in a firewall or NAT so packets from the remote host can be received. ArrangedConnectRequest = 7, ///< A connection request for an "arranged" connection. FirstValidInfoPacketId = 8, ///< The first valid ID for a NetInterface subclass's info packets. }; protected: Vector mConnectionList; ///< List of all the connections that are in a connected state on this NetInterface. Vector mConnectionHashTable; ///< A resizable hash table for all connected connections. This is a flat hash table (no buckets). Vector mPendingConnections; ///< List of connections that are in the startup state, where the remote host has not fully /// validated the connection. RefPtr mPrivateKey; ///< The private key used by this NetInterface for secure key exchange. RefPtr mCertificate; ///< A certificate, signed by some Certificate Authority, to authenticate this host. ClientPuzzleManager mPuzzleManager; ///< The object that tracks the current client puzzle difficulty, current puzzle and solutions for this NetInterface. /// @name NetInterfaceSocket Socket /// /// State regarding the socket this NetInterface controls. /// /// @{ /// Socket mSocket; ///< Network socket this NetInterface communicates over. /// @} U32 mCurrentTime; ///< Current time tracked by this NetInterface. bool mRequiresKeyExchange; ///< True if all connections outgoing and incoming require key exchange. U32 mLastTimeoutCheckTime; ///< Last time all the active connections were checked for timeouts. U8 mRandomHashData[12]; ///< Data that gets hashed with connect challenge requests to prevent connection spoofing. bool mAllowConnections; ///< Set if this NetInterface allows connections from remote instances. /// Structure used to track packets that are delayed in sending for simulating a high-latency connection. /// /// The DelaySendPacket is allocated as sizeof(DelaySendPacket) + packetSize; struct DelaySendPacket { DelaySendPacket *nextPacket; ///< The next packet in the list of delayed packets. Address remoteAddress; ///< The address to send this packet to. U32 sendTime; ///< Time when we should send the packet. U32 packetSize; ///< Size, in bytes, of the packet data. U8 packetData[1]; ///< Packet data. }; DelaySendPacket *mSendPacketList; ///< List of delayed packets pending to send. enum NetInterfaceConstants { ChallengeRetryCount = 4, ///< Number of times to send connect challenge requests before giving up. ChallengeRetryTime = 2500, ///< Timeout interval in milliseconds before retrying connect challenge. ConnectRetryCount = 4, ///< Number of times to send connect requests before giving up. ConnectRetryTime = 2500, ///< Timeout interval in milliseconds before retrying connect request. PunchRetryCount = 6, ///< Number of times to send groups of firewall punch packets before giving up. PunchRetryTime = 2500, ///< Timeout interval in milliseconds before retrying punch sends. TimeoutCheckInterval = 1500, ///< Interval in milliseconds between checking for connection timeouts. PuzzleSolutionTimeout = 30000, ///< If the server gives us a puzzle that takes more than 30 seconds, time out. }; /// Computes an identity token for the connecting client based on the address of the client and the /// client's unique nonce value. U32 computeClientIdentityToken(const Address &theAddress, const Nonce &theNonce); /// Finds a connection instance that this NetInterface has initiated. NetConnection *findPendingConnection(const Address &address); /// Adds a connection the list of pending connections. void addPendingConnection(NetConnection *conn); /// Removes a connection from the list of pending connections. void removePendingConnection(NetConnection *conn); /// Finds a connection by address from the pending list and removes it. void findAndRemovePendingConnection(const Address &address); /// Adds a connection to the internal connection list. void addConnection(NetConnection *connection); /// Remove a connection from the list. void removeConnection(NetConnection *connection); /// Begins the connection handshaking process for a connection. Called from NetConnection::connect() void startConnection(NetConnection *conn); /// Sends a connect challenge request on behalf of the connection to the remote host. void sendConnectChallengeRequest(NetConnection *conn); /// Handles a connect challenge request by replying to the requestor of a connection with a /// unique token for that connection, as well as (possibly) a client puzzle (for DoS prevention), /// or this NetInterface's public key. void handleConnectChallengeRequest(const Address &addr, BitStream *stream); /// Sends a connect challenge request to the specified address. This can happen as a result /// of receiving a connect challenge request, or during an "arranged" connection for the non-initiator /// of the connection. void sendConnectChallengeResponse(const Address &addr, Nonce &clientNonce, bool wantsKeyExchange, bool wantsCertificate); /// Processes a ConnectChallengeResponse, by issueing a connect request if it was for /// a connection this NetInterface has pending. void handleConnectChallengeResponse(const Address &address, BitStream *stream); /// Continues computation of the solution of a client puzzle, and issues a connect request /// when the solution is found. void continuePuzzleSolution(NetConnection *conn); /// Sends a connect request on behalf of a pending connection. void sendConnectRequest(NetConnection *conn); /// Handles a connection request from a remote host. /// /// This will verify the validity of the connection token, as well as any solution /// to a client puzzle this NetInterface sent to the remote host. If those tests /// pass, it will construct a local connection instance to handle the rest of the /// connection negotiation. void handleConnectRequest(const Address &address, BitStream *stream); /// Sends a connect accept packet to acknowledge the successful acceptance of a connect request. void sendConnectAccept(NetConnection *conn); /// Handles a connect accept packet, putting the connection associated with the /// remote host (if there is one) into an active state. void handleConnectAccept(const Address &address, BitStream *stream); /// Sends a connect rejection to a valid connect request in response to possible error /// conditions (server full, wrong password, etc). void sendConnectReject(ConnectionParameters *theParams, const Address &theAddress, const char *reason); /// Handles a connect rejection packet by notifying the connection object /// that the connection was rejected. void handleConnectReject(const Address &address, BitStream *stream); /// Begins the connection handshaking process for an arranged connection. void startArrangedConnection(NetConnection *conn); /// Sends Punch packets to each address in the possible connection address list. void sendPunchPackets(NetConnection *conn); /// Handles an incoming Punch packet from a remote host. void handlePunch(const Address &theAddress, BitStream *stream); /// Sends an arranged connect request. void sendArrangedConnectRequest(NetConnection *conn); /// Handles an incoming connect request from an arranged connection. void handleArrangedConnectRequest(const Address &theAddress, BitStream *stream); /// Dispatches a disconnect packet for a specified connection. void handleDisconnect(const Address &address, BitStream *stream); /// Handles an error reported while reading a packet from this remote connection. void handleConnectionError(NetConnection *theConnection, const char *errorString); /// Disconnects the given connection and removes it from the NetInterface void disconnect(NetConnection *conn, NetConnection::TerminationReason reason, const char *reasonString); /// @} public: /// @param bindAddress Local network address to bind this interface to. NetInterface(const Address &bindAddress); ~NetInterface(); /// Returns the address of the first network interface in the list that the socket on this NetInterface is bound to. Address getFirstBoundInterfaceAddress(); /// Sets the private key this NetInterface will use for authentication and key exchange void setPrivateKey(AsymmetricKey *theKey); /// Requires that all connections use encryption and key exchange void setRequiresKeyExchange(bool requires) { mRequiresKeyExchange = requires; } /// Sets the public certificate that validates the private key and stores /// information about this host. If no certificate is set, this interface can /// still initiate and accept encrypted connections, but they will be vulnerable to /// man in the middle attacks, unless the remote host can validate the public key /// in another way. void setCertificate(Certificate *theCertificate); /// Returns whether or not this NetInterface allows connections from remote hosts. bool doesAllowConnections() { return mAllowConnections; } /// Sets whether or not this NetInterface allows connections from remote hosts. void setAllowsConnections(bool conn) { mAllowConnections = conn; } /// Returns the Socket associated with this NetInterface Socket &getSocket() { return mSocket; } /// Sends a packet to the remote address over this interface's socket. NetError sendto(const Address &address, BitStream *stream); /// Sends a packet to the remote address after millisecondDelay time has elapsed. /// /// This is used to simulate network latency on a LAN or single computer. void sendtoDelayed(const Address &address, BitStream *stream, U32 millisecondDelay); /// Dispatch function for processing all network packets through this NetInterface. void checkIncomingPackets(); /// Processes a single packet, and dispatches either to handleInfoPacket or to /// the NetConnection associated with the remote address. virtual void processPacket(const Address &address, BitStream *packetStream); /// Handles all packets that don't fall into the category of connection handshake or game data. virtual void handleInfoPacket(const Address &address, U8 packetType, BitStream *stream); /// Checks all connections on this interface for packet sends, and for timeouts and all valid /// and pending connections. void processConnections(); /// Returns the list of connections on this NetInterface. Vector &getConnectionList() { return mConnectionList; } /// looks up a connected connection on this NetInterface NetConnection *findConnection(const Address &remoteAddress); /// returns the current process time for this NetInterface U32 getCurrentTime() { return mCurrentTime; } }; }; #endif