/*
* lftp - file transfer program
*
* Copyright (c) 2012-2016 by Alexander V. Lukyanov (lav@yars.free.net)
*
* 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 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 DHT_H
#define DHT_H
class Torrent;
class DHT : public SMTask, protected ProtoLog, public ResClient
{
static const int K = 8;
static const int MAX_NODES = 160*K;
static const int MAX_TORRENTS = 1024;
static const int MAX_PEERS = 60; // per torrent
static const int MAX_SEND_QUEUE = 256;
class Node
{
public:
xstring id;
xstring token;
xstring my_token;
xstring my_last_token;
xstring origin_id;
sockaddr_u addr;
Timer good_timer; // 15 minutes, questionable when expired
Timer token_timer;
Timer ping_timer; // don't send pings too often
bool responded; // has ever responded to our query
bool in_routes; // belongs to the routing table;
int ping_lost_count;
int id_change_count;
int bad_node_count;
bool IsGood() const { return !good_timer.Stopped(); }
void SetGood() { good_timer.Reset(); }
bool IsBad() const { return (!IsGood() && ping_lost_count>=2) || id_change_count>=2; }
void LostPing() { ping_lost_count++; }
void ResetLostPing() { ping_lost_count=0; }
void SetOrigin(const Node *o) { origin_id.set(o->id); }
const char *GetName() const { return addr.to_string(); }
Node(const xstring& i,const sockaddr_u& a)
: id(i.copy()), addr(a), good_timer(15*60), token_timer(5*60),
ping_timer(30), responded(false), in_routes(false),
ping_lost_count(0), id_change_count(0), bad_node_count(0)
{
good_timer.Stop();
ping_timer.Stop();
}
const xstring& GetToken();
bool TokenIsValid(const xstring& token) const;
};
class RouteBucket
{
public:
int prefix_bits;
xstring prefix;
xarray nodes;
Timer fresh_timer;
bool IsFresh() const { return !fresh_timer.Stopped(); }
void SetFresh() { fresh_timer.Reset(); }
bool PrefixMatch(const xstring& i,int skew=0) const;
void RemoveNode(Node *n);
void RemoveNode(int i);
bool HasGoodNodes() const {
for(int i=0; iIsGood())
return true;
return false;
}
RouteBucket(int pb,const xstring& p)
: prefix_bits(pb), prefix(p.copy()), fresh_timer(15*60)
{
assert(prefix.length()>=size_t((prefix_bits+7)/8));
}
const char *to_string() const;
};
class Request
{
public:
Ref data;
sockaddr_u addr;
xstring node_id;
Timer expire_timer;
bool Expired() const { return expire_timer.Stopped(); }
const xstring& GetNodeId() const { return node_id; }
const xstring& GetSearchTarget() const;
Request(BeNode *b,const sockaddr_u& a,const xstring& id)
: data(b), addr(a), node_id(id.copy()), expire_timer(180) {}
};
class Search
{
public:
xstring target_id;
xstring best_node_id;
xmap searched;
int depth;
Timer search_timer;
bool want_peers;
bool noseed;
bool bootstrap;
Search(const xstring& i)
: target_id(i.copy()), depth(0), search_timer(185),
want_peers(false), noseed(false), bootstrap(false) {}
bool IsFeasible(const xstring &id) const;
bool IsFeasible(const Node *n) const { return IsFeasible(n->id); }
void ContinueOn(DHT *d,const Node *n);
void WantPeers(bool ns) { want_peers=true; noseed=ns; }
void Bootstrap() { bootstrap=true; }
};
class Peer
{
public:
sockaddr_compact compact_addr;
Timer good_timer;
bool seed;
Peer(const sockaddr_compact &a,bool s)
: compact_addr(a), good_timer(15*60), seed(s) {}
bool IsGood() const { return !good_timer.Stopped(); }
};
class KnownTorrent
{
public:
xarray_p peers;
void AddPeer(Peer *);
};
class BlackList : private ProtoLog
{
xmap_p bl;
public:
bool Listed(const sockaddr_u &a);
void Add(const sockaddr_u &a,const char *t="1h");
};
int af;
BlackList black_list;
RateLimit rate_limit;
RefQueue send_queue;
xmap_p sent_req; // the key is "t"
Timer sent_req_expire_scan;
Timer search_cleanup_timer;
Timer refresh_timer;
Timer nodes_cleanup_timer;
Timer save_timer;
// voting for new external IP
xmap ip_votes;
xmap ip_voted;
xstring node_id;
xmap_p nodes;
xmap node_by_addr;
RefArray routes;
xmap_p search;
xmap_p torrents;
xqueue_p bootstrap_nodes;
SMTaskRef resolver;
void Bootstrap();
void AddNode(Node *);
void RemoveNode(Node *);
void ChangeNodeId(Node *n,const xstring& new_node_id);
void BlackListNode(Node *n,const char *timeout);
void AddRoute(Node *);
void RemoveRoute(Node *n);
bool SplitRoute0();
Node *FoundNode(const xstring& id,const sockaddr_u& a,bool responded,Search *s=0);
int FindRoute(const xstring& i,int start=0,int skew=0);
void FindNodes(const xstring& i,xarray &a,int max_count,bool only_good,const xmap *exclude=0);
void StartSearch(Search *s);
void RestartSearch(Search *s);
void AddPeer(const xstring& ih,const sockaddr_compact& ca,bool seed);
Node *GetOrigin(const Node *n);
unsigned t; // transaction id
BeNode *NewQuery(const char *q,xmap_p& a);
BeNode *NewReply(const xstring& t0,xmap_p& r);
BeNode *NewError(const xstring& t0,int code,const char *msg);
void SendMessage(BeNode *q,const sockaddr_u& a,const xstring& id=xstring::null);
void SendMessage(Request *);
bool MaySendMessage();
static const char *MessageType(BeNode *q);
static int AddNodesToReply(xmap_p &r,const xstring& target,bool want_n4,bool want_n6);
int AddNodesToReply(xmap_p &r,const xstring& target,int max_count);
enum {
ERR_GENERIC=201,
ERR_SERVER=202,
ERR_PROTOCOL=203,
ERR_UNKNOWN_METHOD=204,
};
SMTaskRef state_io;
public:
DHT(int af,const xstring &id);
~DHT();
int Do();
static void MakeNodeId(xstring &id,const sockaddr_compact& ip,int r=random()/13);
static bool ValidNodeId(const xstring &id,const sockaddr_compact& ip);
void Restart();
void Reconfig(const char *name);
const char *GetLogContext() { return af==AF_INET?"DHT":"DHT6"; }
const xstring& GetNodeID() const { return node_id; }
void SendPing(const sockaddr_u& addr,const xstring& id=xstring::null);
void SendPing(Node *n);
int PingQuestionable(const xarray& nodes,int limit);
void AnnouncePeer(const Torrent *);
void DenouncePeer(const Torrent *);
void HandlePacket(BeNode *b,const sockaddr_u& src);
void Save(const SMTaskRef& buf);
void Load(const SMTaskRef& buf);
xstring state_file;
void Save();
void Load();
void AddBootstrapNode(const char *n) { bootstrap_nodes.push(new xstring(n)); }
};
#endif//DHT_H