Miguel Freitas
11 years ago
16 changed files with 676 additions and 29 deletions
@ -0,0 +1,330 @@
@@ -0,0 +1,330 @@
|
||||
// Copyright (c) 2014 Miguel Freitas
|
||||
// tunnel DHT requests into tcp connection
|
||||
// see: https://groups.google.com/forum/#!topic/twister-dev/uKjFGSw24yA
|
||||
|
||||
#include <boost/assign/list_of.hpp> // for 'map_list_of()' |
||||
#include <boost/assign.hpp> |
||||
#include <boost/foreach.hpp> |
||||
#include <algorithm> // std::random_shuffle |
||||
|
||||
#include "dhtproxy.h" |
||||
|
||||
#include "libtorrent/alert_manager.hpp" |
||||
#include "libtorrent/alert_types.hpp" |
||||
#include "libtorrent/peer_id.hpp" |
||||
#include "libtorrent/bencode.hpp" |
||||
|
||||
#include "main.h" |
||||
#include "uint256.h" |
||||
#include "script.h" |
||||
#include "init.h" |
||||
#include "twister.h" |
||||
#include "twister_utils.h" |
||||
|
||||
//#define dbgprintf OutputDebugStringF
|
||||
#define dbgprintf(...) // no debug printf
|
||||
|
||||
using namespace libtorrent; |
||||
|
||||
namespace DhtProxy |
||||
{ |
||||
bool fEnabled = true; |
||||
CCriticalSection cs_dhtProxy; |
||||
map<sha1_hash, std::list<alert_manager*> > m_dhtgetMap; |
||||
map<sha1_hash, std::list<CService> > m_dhtgetPeersReq; |
||||
|
||||
class PeerBanStats { |
||||
public: |
||||
PeerBanStats() : active(0), count(0), limit(time_now()) {} |
||||
int active; |
||||
int count; |
||||
ptime limit; |
||||
}; |
||||
map<CService, PeerBanStats> m_peerBanStats; |
||||
size_t numProxiesToUse = 4; |
||||
|
||||
void dhtgetMapAdd(sha1_hash &ih, alert_manager *am) |
||||
{ |
||||
LOCK(cs_dhtProxy); |
||||
m_dhtgetMap[ih].push_back(am); |
||||
} |
||||
|
||||
void dhtgetMapRemove(sha1_hash &ih, alert_manager *am) |
||||
{ |
||||
LOCK(cs_dhtProxy); |
||||
std::map<sha1_hash, std::list<alert_manager*> >::iterator mi = m_dhtgetMap.find(ih); |
||||
if( mi != m_dhtgetMap.end() ) { |
||||
std::list<alert_manager *> &amList = (*mi).second; |
||||
amList.remove(am); |
||||
if( !amList.size() ) { |
||||
m_dhtgetMap.erase(ih); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void dhtgetMapPost(sha1_hash &ih, const alert &a) |
||||
{ |
||||
LOCK(cs_dhtProxy); |
||||
std::map<sha1_hash, std::list<alert_manager*> >::iterator mi = m_dhtgetMap.find(ih); |
||||
if( mi != m_dhtgetMap.end() ) { |
||||
std::list<alert_manager *> &amList = (*mi).second; |
||||
BOOST_FOREACH(alert_manager *am, amList) { |
||||
am->post_alert(a); |
||||
} |
||||
} |
||||
} |
||||
|
||||
vector<CNode*> getRandomDhtProxies() |
||||
{ |
||||
// (cs_vNodes) lock must be held!
|
||||
vector<CNode*> vNodesProxy; |
||||
BOOST_FOREACH(CNode* pnode, vNodes) { |
||||
if (pnode->nVersion >= DHT_PROXY_VERSION && !pnode->fNoDhtProxy) { |
||||
vNodesProxy.push_back(pnode); |
||||
} |
||||
} |
||||
std::random_shuffle(vNodesProxy.begin(),vNodesProxy.end()); |
||||
if(vNodesProxy.size() > numProxiesToUse) { |
||||
vNodesProxy.resize(numProxiesToUse); |
||||
} |
||||
|
||||
return vNodesProxy; |
||||
} |
||||
|
||||
vector<CNode*> dhtgetStartRequest(std::string const &username, std::string const &resource, bool multi) |
||||
{ |
||||
CDHTGetRequest req; |
||||
req.vchUsername = std::vector<char>(username.begin(), username.end()); |
||||
req.vchResource = std::vector<char>(resource.begin(), resource.end()); |
||||
req.resTypeMulti = multi; |
||||
req.stopReq = false; |
||||
|
||||
LOCK(cs_vNodes); |
||||
vector<CNode*> vNodesReq = getRandomDhtProxies(); |
||||
BOOST_FOREACH(CNode* pnode, vNodesReq) { |
||||
dbgprintf("DhtProxy::dhtgetStartRequest: pushMessage to %s\n", pnode->addr.ToString().c_str()); |
||||
pnode->PushMessage("dhtgetreq", req); |
||||
pnode->AddRef(); |
||||
} |
||||
if( !vNodesReq.size() ) { |
||||
dbgprintf("DhtProxy::dhtgetStartRequest: sorry, no dht proxy found.\n"); |
||||
|
||||
// fake no data to wakeup listener
|
||||
dht_reply_data_done_alert dd("","",false,false,false); |
||||
sha1_hash ih = dhtTargetHash(username, resource, multi ? "m" : "s"); |
||||
dhtgetMapPost(ih, dd); |
||||
} |
||||
return vNodesReq; |
||||
} |
||||
|
||||
void dhtgetStopRequest(vector<CNode*> vNodesReq, std::string const &username, std::string const &resource, bool multi) |
||||
{ |
||||
CDHTGetRequest req; |
||||
req.vchUsername = std::vector<char>(username.begin(), username.end()); |
||||
req.vchResource = std::vector<char>(resource.begin(), resource.end()); |
||||
req.resTypeMulti = multi; |
||||
req.stopReq = true; |
||||
|
||||
BOOST_FOREACH(CNode* pnode, vNodesReq) { |
||||
dbgprintf("DhtProxy::dhtgetStopRequest: pushMessage to %s\n", pnode->addr.ToString().c_str()); |
||||
pnode->PushMessage("dhtgetreq", req); |
||||
pnode->Release(); |
||||
} |
||||
} |
||||
|
||||
void dhtgetPeerReqAdd(sha1_hash &ih, const CNode *pnode) |
||||
{ |
||||
LOCK(cs_dhtProxy); |
||||
m_dhtgetPeersReq[ih].push_back(pnode->addr); |
||||
m_peerBanStats[pnode->addr].active++; |
||||
} |
||||
|
||||
void dhtgetPeerReqRemove(sha1_hash &ih, const CNode *pnode) |
||||
{ |
||||
LOCK(cs_dhtProxy); |
||||
std::map<sha1_hash, std::list<CService> >::iterator mi = m_dhtgetPeersReq.find(ih); |
||||
if( mi != m_dhtgetPeersReq.end() ) { |
||||
std::list<CService> &addrList = (*mi).second; |
||||
addrList.remove(pnode->addr); |
||||
if( !addrList.size() ) { |
||||
m_dhtgetPeersReq.erase(ih); |
||||
} |
||||
m_peerBanStats[pnode->addr].active--; |
||||
} |
||||
} |
||||
|
||||
void dhtgetPeerReqReply(sha1_hash &ih, const alert *a) |
||||
{ |
||||
CDHTGetReply reply; |
||||
reply.vchTargetHash = std::vector<char>(ih.begin(), ih.end()); |
||||
dht_reply_data_alert const* rd = alert_cast<dht_reply_data_alert>(a); |
||||
if (rd) { |
||||
bencode(std::back_inserter(reply.vchBencodedData), rd->m_lst); |
||||
} |
||||
|
||||
LOCK(cs_dhtProxy); |
||||
std::map<sha1_hash, std::list<CService> >::iterator mi = m_dhtgetPeersReq.find(ih); |
||||
if( mi != m_dhtgetPeersReq.end() ) { |
||||
std::list<CService> &addrList = (*mi).second; |
||||
BOOST_FOREACH(CService &addr, addrList) { |
||||
LOCK(cs_vNodes); |
||||
BOOST_FOREACH(CNode* pnode, vNodes) { |
||||
if ((CService)pnode->addr == addr) { |
||||
dbgprintf("DhtProxy::dhtgetPeerReqReply: pushMessage to %s\n", pnode->addr.ToString().c_str()); |
||||
pnode->PushMessage("dhtgetreply", reply); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
bool checkForAbuse(CNode* pfrom) |
||||
{ |
||||
LOCK(cs_dhtProxy); |
||||
|
||||
// logic inspired/copied from dht_tracker.cpp:incoming_packet
|
||||
ptime now = time_now(); |
||||
PeerBanStats *match = &m_peerBanStats[pfrom->addr]; |
||||
match->count++; |
||||
if( match->count >= 500 ) { |
||||
if (now < match->limit) { |
||||
if( match->count == 500 ) { |
||||
dbgprintf("DhtProxy::checkForAbuse: %s misbehaving, too much requests.\n", |
||||
pfrom->addr.ToString().c_str()); |
||||
} |
||||
match->limit = now + minutes(5); |
||||
return true; |
||||
} |
||||
match->count = 0; |
||||
match->limit = now + seconds(5); |
||||
} |
||||
if( match->active > 10 ) { |
||||
dbgprintf("DhtProxy::checkForAbuse: %s misbehaving, max active requests reached.\n", |
||||
pfrom->addr.ToString().c_str()); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
bool dhtgetRequestReceived(const CDHTGetRequest& req, CNode* pfrom) |
||||
{ |
||||
if( fEnabled ) { |
||||
// we are using proxy ourselves, we can't be proxy to anyone else
|
||||
pfrom->PushMessage("nodhtproxy"); |
||||
return true; |
||||
} else if( !req.stopReq && checkForAbuse(pfrom) ) { |
||||
return false; |
||||
} else { |
||||
std::string username(req.vchUsername.data(), req.vchUsername.size()); |
||||
std::string resource(req.vchResource.data(), req.vchResource.size()); |
||||
bool multi(req.resTypeMulti); |
||||
|
||||
dbgprintf("DhtProxy::dhtgetRequestReceived: (%s,%s,%d,stop=%d) from %s\n", |
||||
username.c_str(), resource.c_str(), multi, req.stopReq, |
||||
pfrom->addr.ToString().c_str()); |
||||
|
||||
sha1_hash ih = dhtTargetHash(username, resource, multi ? "m" : "s"); |
||||
if( !req.stopReq ) { |
||||
dhtgetPeerReqAdd(ih, pfrom); |
||||
dhtGetData(username, resource, multi); |
||||
} else { |
||||
dhtgetPeerReqRemove(ih, pfrom); |
||||
} |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
bool dhtgetReplyReceived(const CDHTGetReply& reply, CNode* pfrom) |
||||
{ |
||||
std::string strTargetHash(reply.vchTargetHash.data(), reply.vchTargetHash.size()); |
||||
sha1_hash ih(strTargetHash); |
||||
|
||||
if( !reply.vchBencodedData.size() ) { |
||||
dbgprintf("DhtProxy::dhtgetReplyReceived: empty data from %s\n", |
||||
pfrom->addr.ToString().c_str()); |
||||
|
||||
// No reply - these fields are not used, we just want cast in twister.cpp:dhtget to fail
|
||||
dht_reply_data_done_alert dd("","",false,false,false); |
||||
dhtgetMapPost(ih, dd); |
||||
} else { |
||||
lazy_entry v; |
||||
int pos; |
||||
libtorrent::error_code ec; |
||||
if (lazy_bdecode(reply.vchBencodedData.data(), reply.vchBencodedData.data() + |
||||
reply.vchBencodedData.size(), v, ec, &pos) == 0 && v.type() == lazy_entry::list_t ) { |
||||
entry lst; |
||||
lst = v; |
||||
dbgprintf("DhtProxy::dhtgetReplyReceived: %zd entries from %s\n", |
||||
lst.list().size(), pfrom->addr.ToString().c_str()); |
||||
dht_reply_data_alert rd(lst.list()); |
||||
dhtgetMapPost(ih, rd); |
||||
} else { |
||||
dbgprintf("DhtProxy::dhtgetReplyReceived: parsing error (data from %s)\n", |
||||
pfrom->addr.ToString().c_str()); |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
|
||||
void dhtputRequest(std::string const &username, std::string const &resource, bool multi, |
||||
std::string const &str_p, std::string const &sig_p, std::string const &sig_user) |
||||
{ |
||||
CDHTPutRequest req; |
||||
req.vchUsername = std::vector<char>(username.begin(), username.end()); |
||||
req.vchResource = std::vector<char>(resource.begin(), resource.end()); |
||||
req.resTypeMulti = multi; |
||||
req.vchStr_p = std::vector<char>(str_p.begin(), str_p.end()); |
||||
req.vchSig_p = std::vector<char>(sig_p.begin(), sig_p.end()); |
||||
req.vchSig_user = std::vector<char>(sig_user.begin(), sig_user.end()); |
||||
|
||||
LOCK(cs_vNodes); |
||||
vector<CNode*> vNodesReq = getRandomDhtProxies(); |
||||
BOOST_FOREACH(CNode* pnode, vNodesReq) { |
||||
dbgprintf("DhtProxy::dhtputRequest: pushMessage to %s\n", pnode->addr.ToString().c_str()); |
||||
pnode->PushMessage("dhtputreq", req); |
||||
} |
||||
if( !vNodesReq.size() ) { |
||||
dbgprintf("DhtProxy::dhtputRequest: sorry, no dht proxy found.\n"); |
||||
} |
||||
} |
||||
|
||||
bool dhtputRequestReceived(const CDHTPutRequest& req, CNode* pfrom) |
||||
{ |
||||
if( fEnabled ) { |
||||
// we are using proxy ourselves, we can't be proxy to anyone else
|
||||
pfrom->PushMessage("nodhtproxy"); |
||||
return true; |
||||
} else if( checkForAbuse(pfrom) ) { |
||||
return false; |
||||
} else { |
||||
std::string username(req.vchUsername.data(), req.vchUsername.size()); |
||||
std::string resource(req.vchResource.data(), req.vchResource.size()); |
||||
bool multi(req.resTypeMulti); |
||||
|
||||
dbgprintf("DhtProxy::dhtputRequestReceived: (%s,%s,%d) from %s\n", |
||||
username.c_str(), resource.c_str(), multi, |
||||
pfrom->addr.ToString().c_str()); |
||||
|
||||
lazy_entry v; |
||||
int pos; |
||||
libtorrent::error_code ec; |
||||
if (lazy_bdecode(req.vchStr_p.data(), req.vchStr_p.data() + |
||||
req.vchStr_p.size(), v, ec, &pos) == 0 ) { |
||||
entry p; |
||||
p = v; |
||||
std::string sig_p(req.vchSig_p.data(), req.vchSig_p.size()); |
||||
std::string sig_user(req.vchSig_user.data(), req.vchSig_user.size()); |
||||
|
||||
dhtPutDataSigned(username,resource,multi,p,sig_p,sig_user, false); |
||||
} else { |
||||
dbgprintf("DhtProxy::dhtputRequestReceived: parsing error (data from %s)\n", |
||||
pfrom->addr.ToString().c_str()); |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,195 @@
@@ -0,0 +1,195 @@
|
||||
// Copyright (c) 2014 Miguel Freitas
|
||||
|
||||
#ifndef DHTPROXY_H |
||||
#define DHTPROXY_H |
||||
|
||||
#include "serialize.h" |
||||
#include "net.h" |
||||
#include "uint256.h" |
||||
|
||||
#include <vector> |
||||
|
||||
// just a small set of declarations to avoid main.c depending on libtorrent headers
|
||||
namespace libtorrent { |
||||
class alert_manager; |
||||
class big_number; |
||||
typedef big_number sha1_hash; |
||||
class alert; |
||||
} |
||||
|
||||
class CDHTTarget; |
||||
class CDHTGetRequest; |
||||
class CDHTGetReply; |
||||
class CDHTPutRequest; |
||||
|
||||
/**
|
||||
* DHTGet Sequence: |
||||
* |
||||
* Client Server |
||||
* 1) dhtgetMapAdd |
||||
* 2) dhtgetStartRequest |
||||
* => CDHTGetRequest => |
||||
* 3) dhtgetRequestReceived(stopReq=False) |
||||
* 4) (dhtgetPeerReqAdd) |
||||
* 5) (ses->dht_getData) |
||||
* 6.1) dhtgetPeerReqReply (from ThreadSessionAlerts) |
||||
* 6.2) <= CDHTGetReply <= |
||||
* 6.3) dhtgetReplyReceived |
||||
* 6.4) (dhtgetMapPost) |
||||
* [ ..item 6 repeats... ] |
||||
* 7) dhtgetMapRemove |
||||
* 8) dhtgetStopRequest |
||||
* => CDHTGetRequest => |
||||
* 9) dhtgetRequestReceived(stopReq=True) |
||||
* 10) (dhtgetPeerReqRemove) |
||||
* |
||||
** |
||||
* DHTPut Sequence: |
||||
* |
||||
* Client Server |
||||
* 1) dhtputRequest |
||||
* => CDHTPutRequest => |
||||
* 2) dhtputRequestReceived |
||||
* 3) (ses->dht_putDataSigned) |
||||
*/ |
||||
namespace DhtProxy |
||||
{ |
||||
extern bool fEnabled; |
||||
|
||||
// Register a listener for dhtget requests (client side)
|
||||
void dhtgetMapAdd(libtorrent::sha1_hash &ih, libtorrent::alert_manager *am); |
||||
|
||||
// Unregister the dhtget listener (client side)
|
||||
void dhtgetMapRemove(libtorrent::sha1_hash &ih, libtorrent::alert_manager *am); |
||||
|
||||
// Request a dhtget. Returns the list of node the request was sent to. (client side)
|
||||
vector<CNode*> dhtgetStartRequest(std::string const &username, std::string const &resource, bool multi); |
||||
|
||||
// Stop a dhtget request to the nodes listed. (client side)
|
||||
void dhtgetStopRequest(vector<CNode*> vNodesReq, std::string const &username, std::string const &resource, bool multi); |
||||
|
||||
// Handle a dhtget request received from TCP. send request to UDP. (server side)
|
||||
// return true if accepted.
|
||||
bool dhtgetRequestReceived(const CDHTGetRequest& req, CNode* pfrom); |
||||
|
||||
// Handle a dhtget reply received from UDP, send it to the peers that made the request. (server side)
|
||||
void dhtgetPeerReqReply(libtorrent::sha1_hash &ih, const libtorrent::alert *a); |
||||
|
||||
// Handle a dhtget reply received from TCP. Will call dhtgetMapPost as needed. (client side)
|
||||
// return true if accepted.
|
||||
bool dhtgetReplyReceived(const CDHTGetReply& reply, CNode* pfrom); |
||||
|
||||
// Request a dhtput.
|
||||
void dhtputRequest(std::string const &username, std::string const &resource, bool multi, |
||||
std::string const &str_p, std::string const &sig_p, std::string const &sig_user); |
||||
|
||||
// Handle a dhtput request received from TCP. send request to UDP. (server side)
|
||||
// return true if accepted.
|
||||
bool dhtputRequestReceived(const CDHTPutRequest& req, CNode* pfrom); |
||||
} |
||||
|
||||
class CDHTTarget |
||||
{ |
||||
public: |
||||
std::vector<char> vchUsername; |
||||
std::vector<char> vchResource; |
||||
bool resTypeMulti; |
||||
|
||||
CDHTTarget() |
||||
{ |
||||
SetNull(); |
||||
} |
||||
|
||||
IMPLEMENT_SERIALIZE |
||||
( |
||||
READWRITE(vchUsername); |
||||
READWRITE(vchResource); |
||||
READWRITE(resTypeMulti); |
||||
) |
||||
|
||||
void SetNull() |
||||
{ |
||||
vchUsername.clear(); |
||||
vchResource.clear(); |
||||
resTypeMulti = false; |
||||
} |
||||
}; |
||||
|
||||
class CDHTGetRequest : public CDHTTarget |
||||
{ |
||||
public: |
||||
bool stopReq; |
||||
|
||||
CDHTGetRequest() : CDHTTarget() |
||||
{ |
||||
SetNull(); |
||||
} |
||||
|
||||
IMPLEMENT_SERIALIZE |
||||
( |
||||
CDHTTarget* pthis = (CDHTTarget*)(this); |
||||
READWRITE(*pthis); |
||||
READWRITE(stopReq); |
||||
) |
||||
|
||||
void SetNull() |
||||
{ |
||||
stopReq = false; |
||||
} |
||||
}; |
||||
|
||||
class CDHTGetReply |
||||
{ |
||||
public: |
||||
std::vector<char> vchTargetHash; |
||||
std::vector<char> vchBencodedData; |
||||
|
||||
CDHTGetReply() |
||||
{ |
||||
SetNull(); |
||||
} |
||||
|
||||
IMPLEMENT_SERIALIZE |
||||
( |
||||
READWRITE(vchTargetHash); |
||||
READWRITE(vchBencodedData); |
||||
) |
||||
|
||||
void SetNull() |
||||
{ |
||||
vchTargetHash.clear(); |
||||
vchBencodedData.clear(); |
||||
} |
||||
}; |
||||
|
||||
class CDHTPutRequest : public CDHTTarget |
||||
{ |
||||
public: |
||||
std::vector<char> vchStr_p; |
||||
std::vector<char> vchSig_p; |
||||
std::vector<char> vchSig_user; |
||||
|
||||
CDHTPutRequest() : CDHTTarget() |
||||
{ |
||||
SetNull(); |
||||
} |
||||
|
||||
IMPLEMENT_SERIALIZE |
||||
( |
||||
CDHTTarget* pthis = (CDHTTarget*)(this); |
||||
READWRITE(*pthis); |
||||
READWRITE(vchStr_p); |
||||
READWRITE(vchSig_p); |
||||
READWRITE(vchSig_user); |
||||
) |
||||
|
||||
void SetNull() |
||||
{ |
||||
vchStr_p.clear(); |
||||
vchSig_p.clear(); |
||||
vchSig_user.clear(); |
||||
} |
||||
}; |
||||
|
||||
|
||||
#endif |
Loading…
Reference in new issue