Skip to content

Commit

Permalink
libtorrent: Optimize UDP trackers for UDNS (#42)
Browse files Browse the repository at this point in the history
This commit optimizes UDP trackers with a fast path to ipv4 addresses. When using UDNS, we know the address is going to resolve to IPV4. We can skip a few checks to increase performance.
  • Loading branch information
stickz authored Jul 20, 2024
1 parent b51727c commit c39f8f6
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 116 deletions.
19 changes: 19 additions & 0 deletions libtorrent/rak/socket_address.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ class socket_address_inet {
bool is_port_any() const { return port() == 0; }
bool is_address_any() const { return m_sockaddr.sin_addr.s_addr == htonl(INADDR_ANY); }

#ifdef USE_UDNS
bool is_bindable() const { return !is_address_any(); }
#endif

void clear() { std::memset(this, 0, sizeof(socket_address_inet)); set_family(); }

uint16_t port() const { return ntohs(m_sockaddr.sin_port); }
Expand All @@ -168,6 +172,10 @@ class socket_address_inet {
uint32_t address_n() const { return m_sockaddr.sin_addr.s_addr; }
std::string address_str() const;
bool address_c_str(char* buf, socklen_t size) const;

#ifdef USE_UDNS
std::string pretty_address_str() const { return address_str(); }
#endif

void set_address(in_addr a) { m_sockaddr.sin_addr = a; }
void set_address_h(uint32_t a) { m_sockaddr.sin_addr.s_addr = htonl(a); }
Expand All @@ -180,13 +188,24 @@ class socket_address_inet {
sa_family_t family() const { return m_sockaddr.sin_family; }
void set_family() { m_sockaddr.sin_family = AF_INET; }

#ifdef USE_UDNS
uint32_t length() const { return sizeof(sockaddr_in); }
#endif

sockaddr* c_sockaddr() { return reinterpret_cast<sockaddr*>(&m_sockaddr); }
sockaddr_in* c_sockaddr_inet() { return &m_sockaddr; }

const sockaddr* c_sockaddr() const { return reinterpret_cast<const sockaddr*>(&m_sockaddr); }
const sockaddr_in* c_sockaddr_inet() const { return &m_sockaddr; }

socket_address_inet6 to_mapped_address() const;

#ifdef USE_UDNS
static socket_address_inet* cast_from(sockaddr* sa) { return reinterpret_cast<socket_address_inet*>(sa); }
static const socket_address_inet* cast_from(const sockaddr* sa) { return reinterpret_cast<const socket_address_inet*>(sa); }
static socket_address_inet* cast_from_inet(sockaddr_in* sa) { return reinterpret_cast<socket_address_inet*>(sa); }
static const socket_address_inet* cast_from_inet(const sockaddr_in* sa) { return reinterpret_cast<const socket_address_inet*>(sa); }
#endif

bool operator == (const socket_address_inet& rhs) const;
bool operator < (const socket_address_inet& rhs) const;
Expand Down
2 changes: 1 addition & 1 deletion libtorrent/src/net/socket_datagram.cc
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ SocketDatagram::write_datagram(const void* buffer, unsigned int length, rak::soc

#ifdef USE_UDNS
int
SocketDatagram::write_datagram_ipv4(const void* buffer, unsigned int length, rak::socket_address* sa) {
SocketDatagram::write_datagram_ipv4(const void* buffer, unsigned int length, rak::socket_address_inet* sa) {
if (length == 0)
throw internal_error("Tried to send buffer length 0");

Expand Down
2 changes: 1 addition & 1 deletion libtorrent/src/net/socket_datagram.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class SocketDatagram : public SocketBase {
int read_datagram(void* buffer, unsigned int length, rak::socket_address* sa = NULL);
int write_datagram(const void* buffer, unsigned int length, rak::socket_address* sa = NULL);
#ifdef USE_UDNS
int write_datagram_ipv4(const void* buffer, unsigned int length, rak::socket_address* sa = NULL);
int write_datagram_ipv4(const void* buffer, unsigned int length, rak::socket_address_inet* sa = NULL);
#endif
};

Expand Down
2 changes: 1 addition & 1 deletion libtorrent/src/net/socket_fd.cc
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ SocketFd::bind(const rak::socket_address& sa) {

#ifdef USE_UDNS
bool
SocketFd::bind_ipv4(const rak::socket_address& sa) {
SocketFd::bind_ipv4(const rak::socket_address_inet& sa) {
check_valid();

return !::bind(m_fd, sa.c_sockaddr(), sa.length());
Expand Down
5 changes: 4 additions & 1 deletion libtorrent/src/net/socket_fd.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@

namespace rak {
class socket_address;
#ifdef USE_UDNS
class socket_address_inet;
#endif
}

namespace torrent {
Expand Down Expand Up @@ -88,7 +91,7 @@ class SocketFd {
bool getsockname(rak::socket_address* sa);

#ifdef USE_UDNS
bool bind_ipv4(const rak::socket_address& sa);
bool bind_ipv4(const rak::socket_address_inet& sa);
#endif

bool listen(int size);
Expand Down
10 changes: 7 additions & 3 deletions libtorrent/src/torrent/connection_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ class UdnsAsyncResolver : public AsyncResolver {
public:
UdnsAsyncResolver(ConnectionManager *cm) : AsyncResolver(cm) {}

void *enqueue(const char *name, int family, resolver_callback *cbck) {
return m_udnsevent.enqueue_resolve(name, family, cbck);
void *enqueue(const char *name, resolver_callback *cbck) {
return m_udnsevent.enqueue_resolve(name, cbck);
}

void flush() {
Expand All @@ -79,7 +79,7 @@ class UdnsAsyncResolver : public AsyncResolver {
};

void
ConnectionManager::start_udp_announce(uint64_t idx, const sockaddr* sa, int err) {
ConnectionManager::start_udp_announce(uint64_t idx, const sockaddr_in* sa, int err) {
if (m_tracker_udp_list[idx] != NULL) {
m_tracker_udp_list[idx]->start_announce(sa, err);
}
Expand Down Expand Up @@ -135,7 +135,11 @@ class StubAsyncResolver : public AsyncResolver {
#endif

static void
#ifdef USE_UDNS
resolve_host(const char* host, int family, int socktype, legacy_resolver_callback slot) {
#else
resolve_host(const char* host, int family, int socktype, resolver_callback slot) {
#endif
if (manager->main_thread_main()->is_current())
thread_base::release_global_lock();

Expand Down
19 changes: 14 additions & 5 deletions libtorrent/src/torrent/connection_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@ typedef std::pair<Throttle*, Throttle*> ThrottlePair;

// The sockaddr argument in the result call is NULL if the resolve failed,
// and the int holds the error code.
#ifdef USE_UDNS
typedef std::function<void (const sockaddr_in*, int)> resolver_callback;
typedef std::function<void (const sockaddr*, int)> legacy_resolver_callback;
#else
typedef std::function<void (const sockaddr*, int)> resolver_callback;
#endif

// Encapsulates whether we do genuine async resolution or fall back to sync.
// In a build with USE_UDNS, these do genuine asynchronous DNS resolution.
Expand All @@ -74,7 +79,11 @@ class LIBTORRENT_EXPORT AsyncResolver {
// this queues a DNS resolve but doesn't send it. it doesn't execute any callbacks
// and returns control immediately. the return value is an opaque identifier that
// can be used to cancel the query (as long as the callback hasn't been executed yet):
#ifdef USE_UDNS
virtual void* enqueue(const char *name, resolver_callback *cbck) = 0;
#else
virtual void* enqueue(const char *name, int family, resolver_callback *cbck) = 0;
#endif
// this sends any queued resolves. it can execute arbitrary callbacks
// before returning control:
virtual void flush() = 0;
Expand Down Expand Up @@ -130,7 +139,11 @@ class LIBTORRENT_EXPORT ConnectionManager {
typedef std::function<uint32_t (const sockaddr*)> slot_filter_type;
typedef std::function<ThrottlePair (const sockaddr*)> slot_throttle_type;

#ifdef USE_UDNS
typedef std::function<void (const char*, int, int, legacy_resolver_callback)> slot_resolver_type;
#else
typedef std::function<void (const char*, int, int, resolver_callback)> slot_resolver_type;
#endif

ConnectionManager();
~ConnectionManager();
Expand Down Expand Up @@ -182,12 +195,8 @@ class LIBTORRENT_EXPORT ConnectionManager {
void set_listen_port(port_type p) { m_listen_port = p; }
void set_listen_backlog(int v);

void* enqueue_async_resolve(const char *name, int family, resolver_callback *cbck);
void flush_async_resolves();
void cancel_async_resolve(void *query);

#ifdef USE_UDNS
void start_udp_announce(uint64_t idx, const sockaddr* sa, int err);
void start_udp_announce(uint64_t idx, const sockaddr_in* sa, int err);
void null_udp_tracker(uint64_t idx) { m_tracker_udp_list[idx] = NULL; }
void add_udp_tracker(TrackerUdp* tracker) { m_tracker_udp_list.push_back(tracker); }
uint64_t get_udp_tracker_count() { return m_tracker_udp_list.size(); }
Expand Down
50 changes: 41 additions & 9 deletions libtorrent/src/tracker/tracker_udp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -110,24 +110,25 @@ TrackerUdp::send_state(int state) {

m_resolver_query = manager->connection_manager()->async_resolver().enqueue(
m_hostname.c_str(),
AF_UNSPEC,
#ifdef USE_UDNS
manager->connection_manager()->get_resolver_callback(m_vec_idx)
#else
AF_UNSPEC,
&m_resolver_callback
#endif
);
manager->connection_manager()->async_resolver().flush();
}

#ifdef USE_UDNS
void
TrackerUdp::start_announce(const sockaddr* sa, int err) {
TrackerUdp::start_announce(const sockaddr_in* sa, int err) {
m_resolver_query = NULL;

if (sa == NULL)
return receive_failed("could not resolve hostname");

m_connectAddress = *rak::socket_address::cast_from(sa);
m_connectAddress = *rak::socket_address_inet::cast_from_inet(sa);
m_connectAddress.set_port(m_port);

LT_LOG_TRACKER(DEBUG, "address found (address:%s)", m_connectAddress.address_str().c_str());
Expand All @@ -136,20 +137,50 @@ TrackerUdp::start_announce(const sockaddr* sa, int err) {
return receive_failed("invalid tracker address");

// TODO: Make each of these a separate error... at the very least separate open and bind.
#ifdef USE_UDNS
if (!get_fd().open_datagram_ipv4() || !get_fd().set_nonblock())
return receive_failed("could not open UDP socket");

auto bind_address = rak::socket_address_inet::cast_from(manager->connection_manager()->bind_address());

if (bind_address->is_bindable() && !get_fd().bind_ipv4(*bind_address))
return receive_failed("failed to bind socket to udp address '" + bind_address->pretty_address_str() + "' with error '" + rak::error_number::current().c_str() + "'");

m_readBuffer = new ReadBuffer;
m_writeBuffer = new WriteBuffer;

prepare_connect_input();

manager->poll()->open(this);
manager->poll()->insert_read(this);
manager->poll()->insert_write(this);
manager->poll()->insert_error(this);

m_tries = m_parent->info()->udp_tries();
priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(m_parent->info()->udp_timeout())).round_seconds());
}
#else
void
TrackerUdp::start_announce(const sockaddr* sa, int err) {
m_resolver_query = NULL;

if (sa == NULL)
return receive_failed("could not resolve hostname");

m_connectAddress = *rak::socket_address::cast_from(sa);
m_connectAddress.set_port(m_port);

LT_LOG_TRACKER(DEBUG, "address found (address:%s)", m_connectAddress.address_str().c_str());

if (!m_connectAddress.is_valid())
return receive_failed("invalid tracker address");

// TODO: Make each of these a separate error... at the very least separate open and bind.
if (!get_fd().open_datagram() || !get_fd().set_nonblock())
#endif
return receive_failed("could not open UDP socket");

auto bind_address = rak::socket_address::cast_from(manager->connection_manager()->bind_address());

#ifdef USE_UDNS
if (bind_address->is_bindable() && !get_fd().bind_ipv4(*bind_address))
#else
if (bind_address->is_bindable() && !get_fd().bind(*bind_address))
#endif
return receive_failed("failed to bind socket to udp address '" + bind_address->pretty_address_str() + "' with error '" + rak::error_number::current().c_str() + "'");

m_readBuffer = new ReadBuffer;
Expand All @@ -165,6 +196,7 @@ TrackerUdp::start_announce(const sockaddr* sa, int err) {
m_tries = m_parent->info()->udp_tries();
priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(m_parent->info()->udp_timeout())).round_seconds());
}
#endif

void
TrackerUdp::close() {
Expand Down
11 changes: 10 additions & 1 deletion libtorrent/src/tracker/tracker_udp.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,12 @@ class TrackerUdp : public SocketDatagram, public Tracker {
virtual void event_read();
virtual void event_write();
virtual void event_error();


#ifdef USE_UDNS
void start_announce(const sockaddr_in* sa, int err);
#else
void start_announce(const sockaddr* sa, int err);
#endif

private:
void close_directly();
Expand All @@ -93,7 +97,12 @@ class TrackerUdp : public SocketDatagram, public Tracker {

bool parse_udp_url(const std::string& url, hostname_type& hostname, int& port) const;

#ifdef USE_UDNS
rak::socket_address_inet m_connectAddress;
#else
rak::socket_address m_connectAddress;
#endif

int m_port;
std::string m_hostname;

Expand Down
Loading

0 comments on commit c39f8f6

Please sign in to comment.