diff --git a/include/opendht/dht.h b/include/opendht/dht.h index 36d595f1db3134510e5b93b8bc689377fa3252fe..2194d8e1f47600e16d0f19cae74ce0a7f77c3330 100644 --- a/include/opendht/dht.h +++ b/include/opendht/dht.h @@ -41,8 +41,11 @@ THE SOFTWARE. namespace dht { +using Address = std::pair<sockaddr_storage, socklen_t>; + std::string print_addr(const sockaddr* sa, socklen_t slen); std::string print_addr(const sockaddr_storage& ss, socklen_t sslen); +std::string printAddr(const Address& addr); struct NodeExport { InfoHash id; @@ -332,6 +335,8 @@ public: /* This must be provided by the user. */ static bool isBlacklisted(const sockaddr*, socklen_t) { return false; } + std::vector<Address> getPublicAddress(); + protected: LogMethod DHT_DEBUG = NOLOG; LogMethod DHT_WARN = NOLOG; @@ -734,6 +739,7 @@ private: sockaddr_storage blacklist[BLACKLISTED_MAX] {}; unsigned next_blacklisted = 0; + // timing time_point now; time_point mybucket_grow_time {time_point::min()}, mybucket6_grow_time {time_point::min()}; time_point expire_stuff_time {time_point::min()}; @@ -742,6 +748,9 @@ private: time_point rotate_secrets_time {time_point::min()}; std::queue<time_point> rate_limit_time {}; + using ReportedAddr = std::pair<unsigned, Address>; + std::vector<ReportedAddr> reported_addr; + // Networking & packet handling int send(const char* buf, size_t len, int flags, const sockaddr*, socklen_t); int sendPing(const sockaddr*, socklen_t, TransId tid); @@ -784,13 +793,16 @@ private: uint8_t *nodes_return, unsigned *nodes_len, uint8_t *nodes6_return, unsigned *nodes6_len, std::vector<std::shared_ptr<Value>>& values_return, - want_t* want_return, uint16_t& error_code, bool& ring); + want_t* want_return, uint16_t& error_code, bool& ring, + sockaddr* addr_return, socklen_t& addr_length_return); void rotateSecrets(); Blob makeToken(const sockaddr *sa, bool old) const; bool tokenMatch(const Blob& token, const sockaddr *sa) const; + void reportedAddr(const sockaddr *sa, socklen_t sa_len); + // Storage Storage* findStorage(const InfoHash& id); const Storage* findStorage(const InfoHash& id) const { @@ -828,7 +840,7 @@ private: void dumpBucket(const Bucket& b, std::ostream& out) const; // Nodes - std::shared_ptr<Node> newNode(const InfoHash& id, const sockaddr*, socklen_t, int confirm); + std::shared_ptr<Node> newNode(const InfoHash& id, const sockaddr*, socklen_t, int confirm, const sockaddr* addr=nullptr, socklen_t addr_length=0); std::shared_ptr<Node> findNode(const InfoHash& id, sa_family_t af); const std::shared_ptr<Node> findNode(const InfoHash& id, sa_family_t af) const; bool trySearchInsert(const std::shared_ptr<Node>& node); diff --git a/include/opendht/dhtrunner.h b/include/opendht/dhtrunner.h index 036aea9eff39bdfb153e71d7c3a83879b38b7661..eab88bbf8a8968847de3935f91d32e387cc83da7 100644 --- a/include/opendht/dhtrunner.h +++ b/include/opendht/dhtrunner.h @@ -278,6 +278,18 @@ public: std::lock_guard<std::mutex> lck(dht_mtx); return dht_->getSearchesLog(af); } + std::vector<Address> getPublicAddress() + { + std::lock_guard<std::mutex> lck(dht_mtx); + return dht_->getPublicAddress(); + } + std::vector<std::string> getPublicAddressStr() + { + auto addrs = getPublicAddress(); + std::vector<std::string> ret(addrs.size()); + std::transform(addrs.begin(), addrs.end(), ret.begin(), dht::printAddr); + return ret; + } // securedht methods diff --git a/src/dht.cpp b/src/dht.cpp index a9452c18465b19bd6c902ab7a2c32919907bd1bd..84a6a89e58b5fc0acf719f38c1066b2b1779a27c 100644 --- a/src/dht.cpp +++ b/src/dht.cpp @@ -109,9 +109,11 @@ dht::print_addr(const sockaddr* sa, socklen_t slen) std::stringstream out; if (!getnameinfo(sa, slen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) { if (sa->sa_family == AF_INET6) - out << "[" << hbuf << "]:" << sbuf; + out << "[" << hbuf << "]"; else - out << hbuf << ":" << sbuf; + out << hbuf; + if (strcmp(sbuf, "0")) + out << ":" << sbuf; } else out << "[invalid address]"; return out.str(); @@ -123,6 +125,11 @@ dht::print_addr(const sockaddr_storage& ss, socklen_t sslen) return print_addr((const sockaddr*)&ss, sslen); } +std::string +dht::printAddr(const Address& addr) { + return print_addr((const sockaddr*)&addr.first, addr.second); +} + template <class DT> static double print_dt(DT d) { @@ -496,6 +503,19 @@ Dht::isNodeBlacklisted(const sockaddr *sa, socklen_t salen) const return false; } +std::vector<Address> +Dht::getPublicAddress() +{ + std::sort(reported_addr.begin(), reported_addr.end(), [](const ReportedAddr& a, const ReportedAddr& b) { + return a.first < b.first; + }); + std::vector<Address> ret; + ret.reserve(reported_addr.size()); + for (const auto& addr : reported_addr) + ret.emplace_back(addr.second); + return ret; +} + /* Split a bucket into two equal parts. */ bool Dht::RoutingTable::split(const RoutingTable::iterator& b) @@ -540,10 +560,23 @@ Dht::trySearchInsert(const std::shared_ptr<Node>& node) return inserted; } +void +Dht::reportedAddr(const sockaddr *sa, socklen_t sa_len) +{ + auto it = std::find_if(reported_addr.begin(), reported_addr.end(), [=](const ReportedAddr& addr){ + return (addr.second.second == sa_len) && std::equal((uint8_t*)&addr.second.first, (uint8_t*)&addr.second.first + addr.second.second, (uint8_t*)sa); + }); + if (it == reported_addr.end()) { + if (reported_addr.size() < 32) + reported_addr.emplace_back(1, std::make_pair(*((sockaddr_storage*)sa), sa_len)); + } else + it->first++; +} + /* We just learnt about a node, not necessarily a new one. Confirm is 1 if the node sent a message, 2 if it sent us a reply. */ std::shared_ptr<Node> -Dht::newNode(const InfoHash& id, const sockaddr *sa, socklen_t salen, int confirm) +Dht::newNode(const InfoHash& id, const sockaddr *sa, socklen_t salen, int confirm, const sockaddr* addr, socklen_t addr_length) { if (id == myid || isMartian(sa, salen) || isNodeBlacklisted(sa, salen)) return nullptr; @@ -555,8 +588,11 @@ Dht::newNode(const InfoHash& id, const sockaddr *sa, socklen_t salen, int confir bool mybucket = list.contains(b, myid); - if (confirm == 2) + if (confirm == 2) { b->time = now; + if (addr and addr_length) + reportedAddr(addr, addr_length); + } for (auto& n : b->nodes) { if (n->id != id) continue; @@ -1717,6 +1753,7 @@ Dht::connectivityChanged() confirm_nodes_time = now; mybucket_grow_time = now; mybucket6_grow_time = now; + reported_addr.clear(); for (auto& s : searches) for (auto& sn : s.nodes) sn.listenStatus = {}; @@ -2154,6 +2191,8 @@ Dht::processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, soc in_port_t port; Value::Id value_id; uint16_t error_code; + sockaddr_storage addr; + socklen_t addr_length = sizeof(sockaddr_storage); std::vector<std::shared_ptr<Value>> values; @@ -2176,7 +2215,7 @@ Dht::processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, soc message = parseMessage(buf, buflen, tid, id, info_hash, target, port, token, value_id, nodes, &nodes_len, nodes6, &nodes6_len, - values, &want, error_code, ring); + values, &want, error_code, ring, (sockaddr*)&addr, addr_length); if (message != MessageType::Error && id == zeroes) throw DhtException("no or invalid InfoHash"); } catch (const std::exception& e) { @@ -2241,7 +2280,7 @@ Dht::processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, soc } if (tid.matches(TransPrefix::PING)) { DHT_DEBUG("Pong!"); - newNode(id, from, fromlen, 2); + newNode(id, from, fromlen, 2, (sockaddr*)&addr, addr_length); } else if (tid.matches(TransPrefix::FIND_NODE) or tid.matches(TransPrefix::GET_VALUES)) { bool gp = false; Search *sr = nullptr; @@ -2259,7 +2298,7 @@ Dht::processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, soc DHT_WARN("Unknown search with tid %u !", ttid); n = newNode(id, from, fromlen, 1); } else { - n = newNode(id, from, fromlen, 2); + n = newNode(id, from, fromlen, 2, (sockaddr*)&addr, addr_length); for (unsigned i = 0; i < nodes_len / 26; i++) { uint8_t *ni = nodes + i * 26; const InfoHash& ni_id = *reinterpret_cast<InfoHash*>(ni); @@ -2336,7 +2375,7 @@ Dht::processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, soc DHT_DEBUG("Unknown search or announce!"); newNode(id, from, fromlen, 1); } else { - auto n = newNode(id, from, fromlen, 2); + auto n = newNode(id, from, fromlen, 2, (sockaddr*)&addr, addr_length); for (auto& sn : sr->nodes) if (sn.node == n) { auto it = sn.acked.emplace(value_id, SearchNode::RequestStatus{}); @@ -2364,7 +2403,7 @@ Dht::processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, soc DHT_DEBUG("Unknown search or announce!"); newNode(id, from, fromlen, 1); } else { - auto n = newNode(id, from, fromlen, 2); + auto n = newNode(id, from, fromlen, 2, (sockaddr*)&addr, addr_length); for (auto& sn : sr->nodes) if (sn.node == n) { sn.listenStatus.reply_time = now; @@ -2698,13 +2737,25 @@ Dht::sendPing(const sockaddr *sa, socklen_t salen, TransId tid) return send(buf, i, 0, sa, salen); } +void +insertAddr(char* buf, size_t buflen, size_t& p, const sockaddr *sa, socklen_t) +{ + size_t addr_len = (sa->sa_family == AF_INET) ? sizeof(in_addr) : sizeof(in6_addr); + void* addr_ptr = (sa->sa_family == AF_INET) ? (void*)&((sockaddr_in*)sa)->sin_addr + : (void*)&((sockaddr_in6*)sa)->sin6_addr; + int rc = snprintf(buf + p, buflen - p, "2:sa%lu:", addr_len); + INC(p, rc, buflen); + COPY(buf, p, addr_ptr, addr_len, buflen); +} + int Dht::sendPong(const sockaddr *sa, socklen_t salen, TransId tid) { char buf[512]; - int i = 0, rc; - rc = snprintf(buf + i, 512 - i, "d1:rd2:id20:"); INC(i, rc, 512); + size_t i = 0; + auto rc = snprintf(buf + i, 512 - i, "d1:rd2:id20:"); INC(i, rc, 512); COPY(buf, i, myid.data(), myid.size(), 512); + insertAddr(buf, 512, i, sa, salen); rc = snprintf(buf + i, 512 - i, "e1:t%d:", tid.length); INC(i, rc, 512); COPY(buf, i, tid.data(), tid.length, 512); ADD_V(buf, i, 512); @@ -2745,10 +2796,11 @@ Dht::sendNodesValues(const sockaddr *sa, socklen_t salen, TransId tid, { constexpr const size_t BUF_SZ = 2048 * 64; char buf[BUF_SZ]; - int i = 0, rc; + size_t i = 0; - rc = snprintf(buf + i, BUF_SZ - i, "d1:rd2:id20:"); INC(i, rc, BUF_SZ); + auto rc = snprintf(buf + i, BUF_SZ - i, "d1:rd2:id20:"); INC(i, rc, BUF_SZ); COPY(buf, i, myid.data(), myid.size(), BUF_SZ); + insertAddr(buf, BUF_SZ, i, sa, salen); if (nodes_len > 0) { rc = snprintf(buf + i, BUF_SZ - i, "5:nodes%u:", nodes_len); INC(i, rc, BUF_SZ); @@ -2951,10 +3003,12 @@ Dht::sendListenConfirmation(const sockaddr* sa, socklen_t salen, TransId tid) { static constexpr const size_t BUF_SZ = 512; char buf[BUF_SZ]; - int i = 0, rc; + size_t i = 0; - rc = snprintf(buf + i, BUF_SZ - i, "d1:rd2:id20:"); INC(i, rc, BUF_SZ); + auto rc = snprintf(buf + i, BUF_SZ - i, "d1:rd2:id20:"); INC(i, rc, BUF_SZ); COPY(buf, i, myid.data(), myid.size(), BUF_SZ); + insertAddr(buf, BUF_SZ, i, sa, salen); + rc = snprintf(buf + i, BUF_SZ - i, "e1:t%u:", tid.length); INC(i, rc, BUF_SZ); COPY(buf, i, tid.data(), tid.length, BUF_SZ); ADD_V(buf, i, BUF_SZ); @@ -2994,10 +3048,12 @@ int Dht::sendValueAnnounced(const sockaddr *sa, socklen_t salen, TransId tid, Value::Id vid) { char buf[512]; - int i = 0, rc; + size_t i = 0; - rc = snprintf(buf + i, 512 - i, "d1:rd2:id20:"); INC(i, rc, 512); + auto rc = snprintf(buf + i, 512 - i, "d1:rd2:id20:"); INC(i, rc, 512); COPY(buf, i, myid.data(), myid.size(), 512); + insertAddr(buf, 512, i, sa, salen); + rc = snprintf(buf + i, 512 - i, "3:vid%lu:", sizeof(Value::Id)); INC(i, rc, 512); COPY(buf, i, &vid, sizeof(Value::Id), 512); rc = snprintf(buf + i, 512 - i, "e1:t%u:", tid.length); INC(i, rc, 512); @@ -3044,7 +3100,8 @@ Dht::parseMessage(const uint8_t *buf, size_t buflen, uint8_t *nodes_return, unsigned *nodes_len, uint8_t *nodes6_return, unsigned *nodes6_len, std::vector<std::shared_ptr<Value>>& values_return, - want_t* want_return, uint16_t& error_code, bool& ring) + want_t* want_return, uint16_t& error_code, bool& ring, + sockaddr* addr_return, socklen_t& addr_length_return) { const uint8_t *p; @@ -3073,6 +3130,34 @@ Dht::parseMessage(const uint8_t *buf, size_t buflen, id_return = {}; } + if (addr_return and addr_length_return) { + p = (uint8_t*)dht_memmem(buf, buflen, "2:sa", 4); + if (p) { + char *q; + size_t l = strtoul((char*)p + 4, &q, 10); + if (q && *q == ':' && (l == sizeof(in_addr) or l == sizeof(in6_addr))) { + CHECK(q + 1, l); + if (l == sizeof(in_addr)) { + auto addr = (sockaddr_in*)addr_return; + std::fill_n((uint8_t*)addr, sizeof(sockaddr_in), 0); + addr->sin_family = AF_INET; + addr->sin_port = 0; + memcpy(&addr->sin_addr, q+1, l); + addr_length_return = sizeof(sockaddr_in); + } else if (l == sizeof(in6_addr)) { + auto addr = (sockaddr_in6*)addr_return; + std::fill_n((uint8_t*)addr, sizeof(sockaddr_in6), 0); + addr_return->sa_family = AF_INET6; + addr->sin6_port = 0; + memcpy(&addr->sin6_addr, q+1, l); + addr_length_return = sizeof(sockaddr_in6); + } + } else + addr_length_return = 0; + } else + addr_length_return = 0; + } + p = (uint8_t*)dht_memmem(buf, buflen, "9:info_hash20:", 14); if (p) { CHECK(p + 14, HASH_LEN); diff --git a/tools/dhtnode.cpp b/tools/dhtnode.cpp index fd38edc7ed876e0eb8fd087b8f187a696abdaa73..346b816ae4938aaba046d6aeb74eb05d386ebfa4 100644 --- a/tools/dhtnode.cpp +++ b/tools/dhtnode.cpp @@ -142,6 +142,12 @@ main(int argc, char **argv) std::cout << "Searches:" << std::endl; std::cout << dht.getSearchesLog() << std::endl; continue; + } else if (op == "la") { + std::cout << "Reported public addresses:" << std::endl; + auto addrs = dht.getPublicAddressStr(); + for (const auto& addr : addrs) + std::cout << addr << std::endl; + continue; } else if (op == "log") { do_log = !do_log; if (do_log)