From a838cdde0aa83d5717856d5b133105453c4e3d53 Mon Sep 17 00:00:00 2001 From: myl7 Date: Sun, 5 Dec 2021 08:33:56 +0800 Subject: [PATCH] Add UDP SOCKS5 proxy support for BT By inherienting DHTConnectionImpl that is used by UDP trackers and DHT connections --- src/DHTConnectionSocksProxyImpl.cc | 229 +++++++++++++++++++++++++++++ src/DHTConnectionSocksProxyImpl.h | 76 ++++++++++ src/DHTSetup.cc | 13 +- src/Makefile.am | 1 + 4 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 src/DHTConnectionSocksProxyImpl.cc create mode 100644 src/DHTConnectionSocksProxyImpl.h diff --git a/src/DHTConnectionSocksProxyImpl.cc b/src/DHTConnectionSocksProxyImpl.cc new file mode 100644 index 00000000..1ac0bdd0 --- /dev/null +++ b/src/DHTConnectionSocksProxyImpl.cc @@ -0,0 +1,229 @@ +/* */ +#include "DHTConnectionSocksProxyImpl.h" + +#include +#include + +#include "SocketCore.h" + +namespace aria2 { + +DHTConnectionSocksProxyImpl::DHTConnectionSocksProxyImpl(int family) + : DHTConnectionImpl(family), + family_(family), + proxySocket_(std::make_unique()) +{ +} + +DHTConnectionSocksProxyImpl::~DHTConnectionSocksProxyImpl() = default; + +bool DHTConnectionSocksProxyImpl::startProxy(const std::string& host, + uint16_t port, + const std::string& user, + const std::string& passwd, + const std::string& listenAddr, + uint16_t listenPort) +{ + proxySocket_->establishConnection(host, port); + proxySocket_->setBlockingMode(); + Endpoint proxyEndpoint; + unsigned char resBuf[10]; + + // Authentication negotiation + bool noAuth = user.empty() || passwd.empty(); + if (noAuth) { + std::string req("\x05\x01\x00", 3); + size_t resLen = 2; + proxySocket_->writeData(req); + proxySocket_->readData(resBuf, resLen); + if (resBuf[1] != '\x00') { + proxySocket_->closeConnection(); + return false; + } + } + else { + std::string req("\x05\x02\x00\x02", 4); + size_t resLen = 2; + proxySocket_->writeData(req); + Endpoint proxyEndpoint; + proxySocket_->readData(resBuf, resLen); + if (resBuf[1] != '\x02' && resBuf[1] != '\x00') { + proxySocket_->closeConnection(); + return false; + } + + // Username/password authentication + if (resBuf[1] == '\x02') { + req = std::string("\x01", 1); + req.push_back(static_cast(user.length())); + req += user; + req.push_back(static_cast(passwd.length())); + req += passwd; + size_t resLen = 2; + proxySocket_->writeData(req); + proxySocket_->readData(resBuf, resLen); + if (resBuf[1] != '\x00') { + proxySocket_->closeConnection(); + return false; + } + } + } + + // UDP associate + std::string req("\x05\x03\x00", 3); + if (family_ == AF_INET) { + req.push_back('\x01'); + char addrBuf[10]; + net::getBinAddr(addrBuf, listenAddr); + req.append(addrBuf, 4); + } + else { + req.push_back('\x04'); + char addrBuf[20]; + net::getBinAddr(addrBuf, listenAddr); + req.append(addrBuf, 16); + } + uint16_t listenPortBuf = htons(listenPort); + req.append(reinterpret_cast(&listenPortBuf), 2); + size_t resLen = 5; + proxySocket_->writeData(req); + proxySocket_->readData(resBuf, resLen); + if (resBuf[1] != '\x00') { + proxySocket_->closeConnection(); + return false; + } + if (resBuf[3] == '\x01') { + unsigned char addrBuf[6]; + addrBuf[0] = resBuf[4]; + resLen = 5; + proxySocket_->readData(addrBuf + 1, resLen); + char addrStrBuf[20]; + inetNtop(AF_INET, addrBuf, addrStrBuf, 20); + bndAddr_ = std::string(addrStrBuf); + bndPort_ = ntohs(*reinterpret_cast(addrBuf + 4)); + } + else if (resBuf[3] == '\x04') { + unsigned char addrBuf[18]; + addrBuf[0] = resBuf[4]; + resLen = 17; + proxySocket_->readData(addrBuf + 1, resLen); + char addrStrBuf[50]; + inetNtop(AF_INET6, addrBuf, addrStrBuf, 50); + bndAddr_ = std::string(addrStrBuf); + bndPort_ = ntohs(*reinterpret_cast(addrBuf + 16)); + } + else if (resBuf[3] == '\x03') { + bndAddr_ = std::string(resBuf[4] + 2, '\x00'); + resLen = resBuf[4] + 2; + proxySocket_->readData(&bndAddr_[0], resLen); + bndPort_ = ntohs(*reinterpret_cast(&bndAddr_[0] + resBuf[4])); + bndAddr_.resize(resBuf[4]); + } + else { + proxySocket_->closeConnection(); + return false; + } + return true; +} + +ssize_t DHTConnectionSocksProxyImpl::receiveMessage(unsigned char* data, + size_t len, + std::string& host, + uint16_t& port) +{ + Endpoint remoteEndpoint; + size_t resLen = len + family_ == AF_INET ? 10 : 22; + std::string buf; + buf.resize(resLen); + ssize_t length = getSocket()->readDataFrom(&buf[0], resLen, remoteEndpoint); + if (length == 0) { + return length; + } + + // unencapsulate SOCKS5 UDP header if has + if (length > (family_ == AF_INET ? 10 : 22) && + buf.substr(0, 3) == "\x00\x00\x00" && + buf[3] == (family_ == AF_INET ? '\x01' : '\x04')) { + if (family_ == AF_INET) { + char addrBuf[20]; + inetNtop(AF_INET, &buf[4], addrBuf, 20); + host = std::string(addrBuf); + port = ntohs(*(reinterpret_cast(&buf[8]))); + memcpy(data, buf.c_str(), length - 10); + return length - 10; + } + else { + char addrBuf[50]; + inetNtop(AF_INET6, &buf[4], addrBuf, 50); + host = std::string(addrBuf); + port = ntohs(*(reinterpret_cast(&buf[20]))); + memcpy(data, buf.c_str(), length - 22); + return length - 22; + } + } + else { + host = remoteEndpoint.addr; + port = remoteEndpoint.port; + return length; + } +} + +ssize_t DHTConnectionSocksProxyImpl::sendMessage(const unsigned char* data, + size_t len, + const std::string& host, + uint16_t port) +{ + std::string buf; + if (family_ == AF_INET) { + buf.resize(10); + buf[3] = '\x01'; + // host is got from receiveMessage(). And as socket is binded according to + // family, host should be the same family as family_. Omit family checking. + net::getBinAddr(&buf[4], host); + *(reinterpret_cast(&buf[8])) = htons(port); + buf.append(reinterpret_cast(data), len); + } + else { + buf.resize(22); + buf[3] = '\x04'; + net::getBinAddr(&buf[4], host); + *(reinterpret_cast(&buf[20])) = htons(port); + buf.append(reinterpret_cast(data), len); + } + return getSocket()->writeData(&buf[0], buf.length(), bndAddr_, bndPort_); +} + +} // namespace aria2 diff --git a/src/DHTConnectionSocksProxyImpl.h b/src/DHTConnectionSocksProxyImpl.h new file mode 100644 index 00000000..d6f6d54c --- /dev/null +++ b/src/DHTConnectionSocksProxyImpl.h @@ -0,0 +1,76 @@ +/* */ +#ifndef D_DHT_CONNECTION_SOCKS_PROXY_IMPL_H +#define D_DHT_CONNECTION_SOCKS_PROXY_IMPL_H + +#include "DHTConnectionImpl.h" + +#include + +namespace aria2 { + +class SocketCore; + +class DHTConnectionSocksProxyImpl : public DHTConnectionImpl { +private: + std::unique_ptr proxySocket_; + int family_; + std::string bndAddr_; + uint16_t bndPort_; + +public: + DHTConnectionSocksProxyImpl(int family); + + virtual ~DHTConnectionSocksProxyImpl(); + + // Connect to SOCKS5 server to start UDP proxy. + // Leave user and passwd empty to indicate no authentication. + // Leave listen host and port empty / 0 to indicate no receiving from proxy. + bool startProxy(const std::string& host, uint16_t port, + const std::string& user, const std::string& passwd, + const std::string& listenHost, uint16_t listenPort); + + virtual ssize_t receiveMessage(unsigned char* data, size_t len, + std::string& host, + uint16_t& port) CXX11_OVERRIDE; + + virtual ssize_t sendMessage(const unsigned char* data, size_t len, + const std::string& host, + uint16_t port) CXX11_OVERRIDE; +}; + +} // namespace aria2 + +#endif // D_DHT_CONNECTION_SOCKS_PROXY_IMPL_H diff --git a/src/DHTSetup.cc b/src/DHTSetup.cc index d107a53e..5a773c23 100644 --- a/src/DHTSetup.cc +++ b/src/DHTSetup.cc @@ -41,6 +41,7 @@ #include "util.h" #include "DHTNode.h" #include "DHTConnectionImpl.h" +#include "DHTConnectionSocksProxyImpl.h" #include "DHTRoutingTable.h" #include "DHTMessageFactoryImpl.h" #include "DHTMessageTracker.h" @@ -113,7 +114,7 @@ DHTSetup::setup(DownloadEngine* e, int family) } uint16_t port; - auto connection = make_unique(family); + auto connection = make_unique(family); { port = e->getBtRegistry()->getUdpPort(); const std::string& addr = e->getOption()->get( @@ -139,6 +140,16 @@ DHTSetup::setup(DownloadEngine* e, int family) } A2_LOG_DEBUG(fmt("Initialized local node ID=%s", util::toHex(localNode->getID(), DHT_ID_LENGTH).c_str())); + { + if (!connection->startProxy("127.0.0.1", 8000, "", "", + localNode->getIPAddress(), + localNode->getPort())) { + throw DL_ABORT_EX("Error occurred while connecting to SOCKS5 relay " + "server for UDP proxy for DHT and UDP trackers"); + } + } + A2_LOG_DEBUG(fmt("Connected to SOCKS5 relay server %s:%u for UDP proxy", + "127.0.0.1", 8000)); auto tracker = std::make_shared(); auto routingTable = make_unique(localNode); auto factory = make_unique(family); diff --git a/src/Makefile.am b/src/Makefile.am index cb6e3b7c..c352021a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -503,6 +503,7 @@ SRCS += \ DHTBucketTree.cc DHTBucketTree.h\ DHTConnection.h\ DHTConnectionImpl.cc DHTConnectionImpl.h\ + DHTConnectionSocksProxyImpl.cc DHTConnectionSocksProxyImpl.h\ DHTConstants.h\ DHTEntryPointNameResolveCommand.cc DHTEntryPointNameResolveCommand.h\ DHTFindNodeMessage.cc DHTFindNodeMessage.h\