/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* 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, see .
*/
#ifndef InterfaceController_H
#define InterfaceController_H
#include "benc/String.h"
#include "interface/Interface.h"
#include "wire/Headers.h"
#include "crypto/CryptoAuth.h"
#include "dht/dhtcore/Router.h"
#include "dht/dhtcore/RumorMill.h"
#include "interface/Interface.h"
#include "memory/Allocator.h"
#include "switch/SwitchCore.h"
#include "net/SwitchPinger.h"
#include "util/log/Log.h"
#include "util/Linker.h"
Linker_require("interface/InterfaceController.c")
#include
#include
enum InterfaceController_PeerState
{
/**
* In state >= NEW, a valid packet has been received but it could still be a replay.
* Or it's an outgoing connection so we don't care about authentication.
*/
InterfaceController_PeerState_NEW = CryptoAuth_NEW,
InterfaceController_PeerState_HANDSHAKE1 = CryptoAuth_HANDSHAKE1,
InterfaceController_PeerState_HANDSHAKE2 = CryptoAuth_HANDSHAKE2,
InterfaceController_PeerState_HANDSHAKE3 = CryptoAuth_HANDSHAKE3,
/** In state == ESTABLISHED, we know the node at the other end is authentic. */
InterfaceController_PeerState_ESTABLISHED = CryptoAuth_ESTABLISHED,
/** If state == UNRESPONSIVE, the peer has not responded to pings in the required timeframe. */
InterfaceController_PeerState_UNRESPONSIVE = -1,
/** If state is UNAUTHENTICATED, the other node has not sent a single valid packet. */
InterfaceController_PeerState_UNAUTHENTICATED = -2,
};
Assert_compileTime(CryptoAuth_STATE_COUNT == 5);
static inline char* InterfaceController_stateString(enum InterfaceController_PeerState ps)
{
switch (ps) {
case InterfaceController_PeerState_NEW: return "NEW";
case InterfaceController_PeerState_HANDSHAKE1: return "HANDSHAKE1";
case InterfaceController_PeerState_HANDSHAKE2: return "HANDSHAKE2";
case InterfaceController_PeerState_HANDSHAKE3: return "HANDSHAKE3";
case InterfaceController_PeerState_ESTABLISHED: return "ESTABLISHED";
case InterfaceController_PeerState_UNRESPONSIVE: return "UNRESPONSIVE";
case InterfaceController_PeerState_UNAUTHENTICATED: return "UNAUTHENTICATED";
default: return "INVALID";
}
}
/**
* Stats about a peer
*/
struct InterfaceController_peerStats
{
uint8_t* pubKey;
int state;
uint64_t timeOfLastMessage;
uint64_t bytesOut;
uint64_t bytesIn;
uint64_t switchLabel;
bool isIncomingConnection;
String* user;
/** Packet loss/duplication statistics. see: ReplayProtector */
uint32_t duplicates;
uint32_t lostPackets;
uint32_t receivedOutOfRange;
};
struct InterfaceController
{
/** If set, this will override the default registerPeer function */
int (* registerPeer)(struct InterfaceController* ifController,
uint8_t herPublicKey[32],
String* password,
bool requireAuth,
bool isIncomingConnection,
struct Interface* externalInterface);
/** If set, this will override the default getPeer function. */
struct InterfaceController_Peer* (* getPeer)(struct InterfaceController* ifc,
struct Interface* iface);
};
struct InterfaceController_Peer
{
/** The interface which is registered with the switch. */
struct Interface switchIf;
/** The internal (wrapped by CryptoAuth) interface. */
struct Interface* cryptoAuthIf;
/** The external (network side) interface. */
struct Interface* external;
/** The label for this endpoint, needed to ping the endpoint. */
uint64_t switchLabel;
/** Milliseconds since the epoch when the last *valid* message was received. */
uint64_t timeOfLastMessage;
/** Time when the last switch ping response was received from this node. */
uint64_t timeOfLastPing;
/** A counter to allow for 3/4 of all pings to be skipped when a node is definitely down. */
uint32_t pingCount;
/** The handle which can be used to look up this endpoint in the endpoint set. */
uint32_t handle;
/** True if we should forget about the peer if they do not respond. */
bool isIncomingConnection;
/**
* If InterfaceController_PeerState_UNAUTHENTICATED, no permanent state will be kept.
* During transition from HANDSHAKE to ESTABLISHED, a check is done for a registeration of a
* node which is already registered in a different switch slot, if there is one and the
* handshake completes, it will be moved.
*/
enum InterfaceController_PeerState state;
// traffic counters
uint64_t bytesOut;
uint64_t bytesIn;
Identity
};
/**
* Add a new peer.
* Called from the network interface when it is asked to make a connection or it autoconnects.
* If the peer which is connected to becomes unresponsive, IC will *not* remove it but will
* set it's state to UNRESPONSIVE and it is the job of the caller to remove the peer by freeing
* the allocator which is provided with iface.
*
* BEWARE: the interface allocator you provide here may be freed by this code!
*
* The following cases will cause the allocator to be freed:
*
* 1. If a peer is registered and it turns out to have the same cryptographic key as an
* existing peer, the existing one will be freed by the IC and the new one will take it's
* place.
*
* 2. If a peer which is registered as "transient" and is unresponsive for more than
* FORGET_AFTER_MILLISECONDS milliseconds then the session will be removed.
*
* @param ic the interface controller.
* @param herPublicKey the public key of the foreign node, NULL if unknown.
* @param password the password for authenticating with the other node or NULL if unspecified.
* @param requireAuth true if the other node must authenticate (incoming connection).
* @param transient if true then this peer may be forgotten.
* @param iface an interface which pipes messages to/from this peer. The peer will be
* deregistered if this allocator is freed.
*
* @return 0 if all goes well.
* InterfaceController_registerPeer_OUT_OF_SPACE if there is no space to store the peer.
* InterfaceController_registerPeer_BAD_KEY the provided herPublicKey is not valid.
* InterfaceController_registerPeer_INTERNAL unspecified error.
*/
#define InterfaceController_registerPeer_INTERNAL -3
#define InterfaceController_registerPeer_BAD_KEY -2
#define InterfaceController_registerPeer_OUT_OF_SPACE -1
int InterfaceController_registerPeer(struct InterfaceController* ifController,
uint8_t herPublicKey[32],
String* password,
bool requireAuth,
bool isIncomingConnection,
struct Interface* externalInterface);
/**
* Disconnect a previously registered peer.
*
* @param ic the if controller
* @param herPublicKey the public key of the foreign node
* @retrun 0 if all goes well.
* InterfaceController_disconnectPeer_NOTFOUND if no peer with herPublicKey is found.
*/
#define InterfaceController_disconnectPeer_NOTFOUND -1
int InterfaceController_disconnectPeer(struct InterfaceController* ifController,
uint8_t herPublicKey[32]);
/**
* Populate an empty beacon with password, public key, and version.
* Each startup, a password is generated consisting of Headers_Beacon_PASSWORD_LEN bytes.
* If beaconing is enabled for an interface, this password is sent out in each beacon message
* so that others can connect.
* NOTE: Anyone can connect to any interface, even those not beaconing, using this password.
* The public key attached to the beacon message is the public key for this node.
*
* @param ic the if controller
* @param beacon an empty buffer to place the beacon information in.
*/
void InterfaceController_populateBeacon(struct InterfaceController* ifc,
struct Headers_Beacon* beacon);
/** Get the IfController peer for a registered interface. */
struct InterfaceController_Peer* InterfaceController_getPeer(struct InterfaceController* ifc,
struct Interface* iface);
/**
* Get stats for the connected peers.
*
* @params ic the if controller
* @params alloc the Allocator to use for the peerStats array in statsOut
* @params statsOut pointer to the InterfaceController_peerStats array
* @return the number of InterfaceController_peerStats in statsOut
*/
int InterfaceController_getPeerStats(struct InterfaceController* ic,
struct Allocator* alloc,
struct InterfaceController_peerStats** statsOut);
struct InterfaceController* InterfaceController_new(struct CryptoAuth* ca,
struct SwitchCore* switchCore,
struct Router* router,
struct RumorMill* rumorMill,
struct Log* logger,
struct EventBase* eventBase,
struct SwitchPinger* switchPinger,
struct Random* rand,
struct Allocator* allocator);
#endif