//-----------------------------------------------------------------------------------
//
// 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