From e52f2c25275119bf43054cdf711b91fe16db3238 Mon Sep 17 00:00:00 2001 From: Miguel Freitas Date: Thu, 26 Jun 2014 16:38:43 -0300 Subject: [PATCH] dhtproxy: tunnel DHT traffic into TCP connections --- Makefile.am | 1 + src/dhtproxy.cpp | 330 ++++++++++++++++++++++++++++++++++++++++++ src/dhtproxy.h | 195 +++++++++++++++++++++++++ src/init.cpp | 6 +- src/main.cpp | 41 ++++++ src/makefile.android | 1 + src/makefile.freebsd | 1 + src/makefile.osx | 2 + src/makefile.unix | 1 + src/net.h | 2 + src/twister.cpp | 91 ++++++++---- src/twister.h | 11 ++ src/twister_utils.cpp | 13 ++ src/twister_utils.h | 3 + src/version.h | 5 +- twister-qt.pro | 2 + 16 files changed, 676 insertions(+), 29 deletions(-) create mode 100644 src/dhtproxy.cpp create mode 100644 src/dhtproxy.h diff --git a/Makefile.am b/Makefile.am index cd07798d..1feac2b2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -156,6 +156,7 @@ BITCOIN_TWISTER_SOURCES = \ src/leveldb.cpp \ src/txdb.cpp \ src/chainparams.cpp \ + src/dhtproxy.cpp \ src/twister.cpp \ src/twister_rss.cpp \ src/twister_utils.cpp \ diff --git a/src/dhtproxy.cpp b/src/dhtproxy.cpp new file mode 100644 index 00000000..358b401a --- /dev/null +++ b/src/dhtproxy.cpp @@ -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 // for 'map_list_of()' +#include +#include +#include // 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 > m_dhtgetMap; + map > m_dhtgetPeersReq; + + class PeerBanStats { + public: + PeerBanStats() : active(0), count(0), limit(time_now()) {} + int active; + int count; + ptime limit; + }; + map 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 >::iterator mi = m_dhtgetMap.find(ih); + if( mi != m_dhtgetMap.end() ) { + std::list &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 >::iterator mi = m_dhtgetMap.find(ih); + if( mi != m_dhtgetMap.end() ) { + std::list &amList = (*mi).second; + BOOST_FOREACH(alert_manager *am, amList) { + am->post_alert(a); + } + } + } + + vector getRandomDhtProxies() + { + // (cs_vNodes) lock must be held! + vector 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 dhtgetStartRequest(std::string const &username, std::string const &resource, bool multi) + { + CDHTGetRequest req; + req.vchUsername = std::vector(username.begin(), username.end()); + req.vchResource = std::vector(resource.begin(), resource.end()); + req.resTypeMulti = multi; + req.stopReq = false; + + LOCK(cs_vNodes); + vector 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 vNodesReq, std::string const &username, std::string const &resource, bool multi) + { + CDHTGetRequest req; + req.vchUsername = std::vector(username.begin(), username.end()); + req.vchResource = std::vector(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 >::iterator mi = m_dhtgetPeersReq.find(ih); + if( mi != m_dhtgetPeersReq.end() ) { + std::list &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(ih.begin(), ih.end()); + dht_reply_data_alert const* rd = alert_cast(a); + if (rd) { + bencode(std::back_inserter(reply.vchBencodedData), rd->m_lst); + } + + LOCK(cs_dhtProxy); + std::map >::iterator mi = m_dhtgetPeersReq.find(ih); + if( mi != m_dhtgetPeersReq.end() ) { + std::list &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(username.begin(), username.end()); + req.vchResource = std::vector(resource.begin(), resource.end()); + req.resTypeMulti = multi; + req.vchStr_p = std::vector(str_p.begin(), str_p.end()); + req.vchSig_p = std::vector(sig_p.begin(), sig_p.end()); + req.vchSig_user = std::vector(sig_user.begin(), sig_user.end()); + + LOCK(cs_vNodes); + vector 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; + } + } + +} diff --git a/src/dhtproxy.h b/src/dhtproxy.h new file mode 100644 index 00000000..55d2e36e --- /dev/null +++ b/src/dhtproxy.h @@ -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 + +// 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 dhtgetStartRequest(std::string const &username, std::string const &resource, bool multi); + + // Stop a dhtget request to the nodes listed. (client side) + void dhtgetStopRequest(vector 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 vchUsername; + std::vector 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 vchTargetHash; + std::vector vchBencodedData; + + CDHTGetReply() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(vchTargetHash); + READWRITE(vchBencodedData); + ) + + void SetNull() + { + vchTargetHash.clear(); + vchBencodedData.clear(); + } +}; + +class CDHTPutRequest : public CDHTTarget +{ +public: + std::vector vchStr_p; + std::vector vchSig_p; + std::vector 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 diff --git a/src/init.cpp b/src/init.cpp index 0bbec61b..41bb2950 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -431,7 +431,11 @@ bool AppInit2(boost::thread_group& threadGroup) // to protect privacy, do not listen by default if a proxy server is specified SoftSetBoolArg("-listen", false); } - + + if (mapArgs.count("-proxy") || mapArgs.count("-tor")) { + SoftSetBoolArg("-dhtproxy", true); + } + if (!GetBoolArg("-listen", true)) { // do not map ports or try to retrieve public IP when not listening (pointless) SoftSetBoolArg("-upnp", false); diff --git a/src/main.cpp b/src/main.cpp index 314e8d45..cbc9313e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,6 +13,7 @@ #include "ui_interface.h" #include "checkqueue.h" #include "chainparams.h" +#include "dhtproxy.h" #include "twister.h" #include "utf8core.h" @@ -3185,6 +3186,46 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fRelayTxes = true; } + else if (strCommand == "dhtgetreq") + { + CDHTGetRequest req; + vRecv >> req; + + if( DhtProxy::dhtgetRequestReceived(req, pfrom) ) { + // ok + } else { + pfrom->Misbehaving(20); + } + } + + else if (strCommand == "dhtputreq") + { + CDHTPutRequest req; + vRecv >> req; + + if( DhtProxy::dhtputRequestReceived(req, pfrom) ) { + // ok + } else { + pfrom->Misbehaving(20); + } + } + + else if (strCommand == "dhtgetreply") + { + CDHTGetReply reply; + vRecv >> reply; + + if( DhtProxy::dhtgetReplyReceived(reply, pfrom) ) { + // ok + } else { + pfrom->Misbehaving(20); + } + } + + else if (strCommand == "nodhtproxy") + { + pfrom->fNoDhtProxy = true; + } else { diff --git a/src/makefile.android b/src/makefile.android index bb901b53..a2eb23a2 100644 --- a/src/makefile.android +++ b/src/makefile.android @@ -133,6 +133,7 @@ OBJS= \ obj/leveldb.o \ obj/txdb.o \ obj/chainparams.o \ + obj/dhtproxy.o \ obj/twister.o \ obj/twister_rss.o \ obj/twister_utils.o diff --git a/src/makefile.freebsd b/src/makefile.freebsd index d1fef219..9a48e80c 100644 --- a/src/makefile.freebsd +++ b/src/makefile.freebsd @@ -148,6 +148,7 @@ OBJS= \ obj/leveldb.o \ obj/txdb.o \ obj/chainparams.o \ + obj/dhtproxy.o \ obj/twister.o \ obj/twister_rss.o \ obj/twister_utils.o diff --git a/src/makefile.osx b/src/makefile.osx index 98870b1d..adf842d2 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -148,7 +148,9 @@ OBJS= \ obj/leveldb.o \ obj/txdb.o \ obj/chainparams.o \ + obj/dhtproxy.o \ obj/twister.o \ + obj/twister_rss.o \ obj/twister_utils.o ifdef USE_SSE2 diff --git a/src/makefile.unix b/src/makefile.unix index 886b34df..1fae0e65 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -150,6 +150,7 @@ OBJS= \ obj/leveldb.o \ obj/txdb.o \ obj/chainparams.o \ + obj/dhtproxy.o \ obj/twister.o \ obj/twister_rss.o \ obj/twister_utils.o diff --git a/src/net.h b/src/net.h index 693e1dfd..c286ea99 100644 --- a/src/net.h +++ b/src/net.h @@ -203,6 +203,7 @@ public: // b) the peer may tell us in their version message that we should not relay tx invs // until they have initialized their bloom filter. bool fRelayTxes; + bool fNoDhtProxy; CSemaphoreGrant grantOutbound; CCriticalSection cs_filter; CBloomFilter* pfilter; @@ -266,6 +267,7 @@ public: fGetAddr = false; nMisbehavior = 0; fRelayTxes = false; + fNoDhtProxy = false; setInventoryKnown.max_size(SendBufferSize() / 1000); pfilter = NULL; diff --git a/src/twister.cpp b/src/twister.cpp index 08bdc395..91db3855 100644 --- a/src/twister.cpp +++ b/src/twister.cpp @@ -1,6 +1,7 @@ #include "twister.h" #include "twister_utils.h" +#include "dhtproxy.h" #include "main.h" #include "init.h" @@ -91,18 +92,6 @@ private: #define USER_DATA_FILE "user_data" #define GLOBAL_DATA_FILE "global_data" -sha1_hash dhtTargetHash(std::string const &username, std::string const &resource, std::string const &type) -{ - entry target; - target["n"] = username; - target["r"] = resource; - target["t"] = type; - - std::vector buf; - bencode(std::back_inserter(buf), target); - return hasher(buf.data(), buf.size()).final(); -} - void dhtgetMapAdd(sha1_hash &ih, alert_manager *am) { LOCK(cs_dhtgetMap); @@ -337,7 +326,12 @@ void ThreadWaitExtIP() //dhts.restrict_routing_ips = false; //dhts.restrict_search_ips = false; ses->set_dht_settings(dhts); - ses->start_dht(); + + if( !DhtProxy::fEnabled ) { + ses->start_dht(); + } else { + ses->stop_dht(); + } } session_settings settings; @@ -495,7 +489,7 @@ void ThreadMaintainDHTNodes() vNodesSize = vNodes.size(); } - if( !ses->is_paused() ) { + if( !ses->is_paused() && !DhtProxy::fEnabled ) { vector vAddr = addrman.GetAddr(); int totalNodesCandidates = (int)(vNodesSize + vAddr.size()); if( ((!dht_nodes && totalNodesCandidates) || @@ -630,6 +624,7 @@ void ThreadSessionAlerts() t && t->type() == entry::string_t) { sha1_hash ih = dhtTargetHash(n->string(), r->string(), t->string()); dhtgetMapPost(ih,*rd); + DhtProxy::dhtgetPeerReqReply(ih,rd); } } } @@ -675,7 +670,7 @@ void ThreadSessionAlerts() t->string().c_str()); #endif neighborCheck[ih] = false; - ses->dht_getData(n->string(), r->string(), t->string() == "m"); + dhtGetData(n->string(), r->string(), t->string() == "m"); } else if( neighborCheck[ih] ) { sha1_hash ihStatus = dhtTargetHash(n->string(), "status", "s"); @@ -686,7 +681,7 @@ void ThreadSessionAlerts() n->string().c_str(), "status", "s"); #endif statusCheck[ihStatus] = GetTime(); - ses->dht_getData(n->string(), "status", false); + dhtGetData(n->string(), "status", false); } } } @@ -710,6 +705,7 @@ void ThreadSessionAlerts() if( !dd->m_got_data ) { // no data: post alert to return from wait_for_alert in dhtget() dhtgetMapPost(ih,*dd); + DhtProxy::dhtgetPeerReqReply(ih,dd); } if( neighborCheck.count(ih) ) { @@ -721,7 +717,7 @@ void ThreadSessionAlerts() #endif sha1_hash ihStatus = dhtTargetHash(dd->m_username, "status", "s"); statusCheck[ihStatus] = GetTime(); - ses->dht_getData(dd->m_username, "status", false); + dhtGetData(dd->m_username, "status", false); } } if( statusCheck.count(ih) ) { @@ -765,6 +761,8 @@ void startSessionTorrent(boost::thread_group& threadGroup) m_noExpireResources["following"] = NumberedNoExpire; m_noExpireResources["status"] = SimpleNoExpire; m_noExpireResources["post"] = PostNoExpireRecent; + + DhtProxy::fEnabled = GetBoolArg("-dhtproxy", false); m_threadsToJoin = 0; threadGroup.create_thread(boost::bind(&ThreadWaitExtIP)); @@ -1334,16 +1332,24 @@ entry formatSpamPost(const string &msg, const string &username, uint64_t utcTime } -void dhtPutData(std::string const &username, std::string const &resource, bool multi, - entry const &value, std::string const &sig_user, - boost::int64_t timeutc, int seq) +void dhtGetData(std::string const &username, std::string const &resource, bool multi) { + if( DhtProxy::fEnabled ) { + printf("dhtGetData: not allowed - using proxy (bug!)\n"); + return; + } boost::shared_ptr ses(m_ses); if( !ses ) { - printf("dhtPutData: libtorrent session not ready\n"); + printf("dhtGetData: libtorrent session not ready\n"); return; } - + ses->dht_getData(username,resource,multi); +} + +void dhtPutData(std::string const &username, std::string const &resource, bool multi, + entry const &value, std::string const &sig_user, + boost::int64_t timeutc, int seq) +{ // construct p dictionary and sign it entry p; entry& target = p["target"]; @@ -1365,7 +1371,27 @@ void dhtPutData(std::string const &username, std::string const &resource, bool m return; } - ses->dht_putDataSigned(username,resource,multi,p,sig_p,sig_user, true); + if( !DhtProxy::fEnabled ) { + dhtPutDataSigned(username,resource,multi,p,sig_p,sig_user, true); + } else { + DhtProxy::dhtputRequest(username,resource,multi,str_p,sig_p,sig_user); + } +} + +void dhtPutDataSigned(std::string const &username, std::string const &resource, bool multi, + libtorrent::entry const &p, std::string const &sig_p, std::string const &sig_user, bool local) +{ + if( DhtProxy::fEnabled ) { + printf("dhtputDataSigned: not allowed - using proxy (bug!)\n"); + return; + } + boost::shared_ptr ses(m_ses); + if( !ses ) { + printf("dhtPutData: libtorrent session not ready\n"); + return; + } + + ses->dht_putDataSigned(username,resource,multi,p,sig_p,sig_user, local); } Value dhtput(const Array& params, bool fHelp) @@ -1441,9 +1467,15 @@ Value dhtget(const Array& params, bool fHelp) alert_manager am(10, alert::dht_notification); sha1_hash ih = dhtTargetHash(strUsername,strResource,strMulti); - dhtgetMapAdd(ih, &am); - - ses->dht_getData(strUsername, strResource, multi); + + vector dhtProxyNodes; + if( !DhtProxy::fEnabled ) { + dhtgetMapAdd(ih, &am); + dhtGetData(strUsername, strResource, multi); + } else { + DhtProxy::dhtgetMapAdd(ih, &am); + dhtProxyNodes = DhtProxy::dhtgetStartRequest(strUsername, strResource, multi); + } Array ret; std::set uniqueSigPs; @@ -1488,7 +1520,12 @@ Value dhtget(const Array& params, bool fHelp) } } - dhtgetMapRemove(ih,&am); + if( !DhtProxy::fEnabled ) { + dhtgetMapRemove(ih,&am); + } else { + DhtProxy::dhtgetMapRemove(ih,&am); + DhtProxy::dhtgetStopRequest(dhtProxyNodes, strUsername, strResource, multi); + } return ret; } diff --git a/src/twister.h b/src/twister.h index d8738cfc..74456961 100644 --- a/src/twister.h +++ b/src/twister.h @@ -13,6 +13,9 @@ #define BLOCK_AGE_TO_EXPIRE_DHT_ENTRY (2016) // about 2 weeks #define BLOCK_AGE_TO_EXPIRE_DHT_POSTS (4320*2) // about 2 months +namespace libtorrent { + class entry; +} class twister { @@ -41,4 +44,12 @@ int getDhtNodes(boost::int64_t *dht_global_nodes = NULL); void updateSeenHashtags(std::string &message, int64_t msgTime); +// interface to dht api of the libtorrent current session +void dhtGetData(std::string const &username, std::string const &resource, bool multi); +void dhtPutData(std::string const &username, std::string const &resource, bool multi, + libtorrent::entry const &value, std::string const &sig_user, + boost::int64_t timeutc, int seq); +void dhtPutDataSigned(std::string const &username, std::string const &resource, bool multi, + libtorrent::entry const &p, std::string const &sig_p, std::string const &sig_user, bool local); + #endif // TWISTER_H diff --git a/src/twister_utils.cpp b/src/twister_utils.cpp index a150cc2e..a4a1bc56 100644 --- a/src/twister_utils.cpp +++ b/src/twister_utils.cpp @@ -360,3 +360,16 @@ libtorrent::entry safeGetEntryDict(libtorrent::entry const &e, std::string const } } +sha1_hash dhtTargetHash(std::string const &username, std::string const &resource, std::string const &type) +{ + entry target; + target["n"] = username; + target["r"] = resource; + target["t"] = type; + + std::vector buf; + bencode(std::back_inserter(buf), target); + return hasher(buf.data(), buf.size()).final(); +} + + diff --git a/src/twister_utils.h b/src/twister_utils.h index aa6c61a7..26c4c913 100644 --- a/src/twister_utils.h +++ b/src/twister_utils.h @@ -3,6 +3,7 @@ #include "json/json_spirit.h" #include "libtorrent/entry.hpp" +#include "libtorrent/peer_id.hpp" #include #include @@ -48,4 +49,6 @@ std::string safeGetEntryString(libtorrent::entry const &e, std::string const& ke int safeGetEntryInt(libtorrent::entry const &e, std::string const& key); libtorrent::entry safeGetEntryDict(libtorrent::entry const &e, std::string const& key); +libtorrent::sha1_hash dhtTargetHash(std::string const &username, std::string const &resource, std::string const &type); + #endif // TWISTER_UTILS_H diff --git a/src/version.h b/src/version.h index d8b315d4..075df660 100644 --- a/src/version.h +++ b/src/version.h @@ -25,7 +25,7 @@ extern const std::string CLIENT_DATE; // network protocol versioning // -static const int PROTOCOL_VERSION = 70002; +static const int PROTOCOL_VERSION = 70003; // earlier versions not supported as of Feb 2012, and are disconnected static const int MIN_PROTO_VERSION = 209; @@ -44,6 +44,9 @@ static const int BIP0031_VERSION = 60000; // soft checkpoint min version static const int SOFT_CHECKPOINT_VERSION = 70002; +// dht proxy min version +static const int DHT_PROXY_VERSION = 70003; + // "mempool" command, enhanced "getdata" behavior starts with this version: static const int MEMPOOL_GD_VERSION = 60002; diff --git a/twister-qt.pro b/twister-qt.pro index f86b9e9e..6f496341 100644 --- a/twister-qt.pro +++ b/twister-qt.pro @@ -200,6 +200,7 @@ HEADERS += \ src/limitedmap.h \ src/scrypt.h \ src/utf8core.h \ + src/dhtproxy.h \ src/twister.h \ src/twister_rss.h \ src/twister_utils.h @@ -277,6 +278,7 @@ SOURCES += \ #src/qt/bitcoin.cpp \ src/leveldb.cpp \ src/txdb.cpp \ src/scrypt.cpp \ + src/dhtproxy.cpp \ src/twister.cpp \ src/twister_rss.cpp \ src/twister_utils.cpp