librats 2.3.3 → 2.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/native-src/CMakeLists.txt +15 -0
- package/native-src/src/librats/bindings/rats.cpp +59 -2
- package/native-src/src/librats/bindings/rats.h +30 -0
- package/native-src/src/librats/bittorrent/client.cpp +221 -26
- package/native-src/src/librats/bittorrent/client.h +73 -6
- package/native-src/src/librats/bittorrent/mse.cpp +626 -0
- package/native-src/src/librats/bittorrent/mse.h +218 -0
- package/native-src/src/librats/bittorrent/peer_connection.cpp +187 -59
- package/native-src/src/librats/bittorrent/peer_connection.h +109 -17
- package/native-src/src/librats/bittorrent/peer_link.cpp +134 -0
- package/native-src/src/librats/bittorrent/peer_link.h +145 -0
- package/native-src/src/librats/bittorrent/peer_list.cpp +75 -12
- package/native-src/src/librats/bittorrent/peer_list.h +130 -18
- package/native-src/src/librats/bittorrent/torrent.cpp +54 -11
- package/native-src/src/librats/bittorrent/torrent.h +19 -3
- package/native-src/src/librats/bittorrent/types.h +27 -0
- package/native-src/src/librats/bittorrent/utp_manager.cpp +234 -0
- package/native-src/src/librats/bittorrent/utp_manager.h +131 -0
- package/native-src/src/librats/bittorrent/utp_packet.h +216 -0
- package/native-src/src/librats/bittorrent/utp_stream.cpp +903 -0
- package/native-src/src/librats/bittorrent/utp_stream.h +400 -0
- package/native-src/src/librats/core/socket.cpp +44 -8
- package/native-src/src/librats/core/socket.h +23 -1
- package/package.json +1 -1
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file utp_manager.h
|
|
5
|
+
* @brief The one UDP socket every uTP peer shares, and the demultiplexer in front
|
|
6
|
+
* of it.
|
|
7
|
+
*
|
|
8
|
+
* uTP puts every connection on a single socket and separates them by a 16-bit
|
|
9
|
+
* connection id — the same shape as the node's own `UdpMux`, and for the same
|
|
10
|
+
* reasons: one NAT mapping for the whole swarm rather than one per peer, one
|
|
11
|
+
* pollable descriptor however many peers there are, and a source port a third
|
|
12
|
+
* party can dial back (which is what makes hole punching possible at all).
|
|
13
|
+
*
|
|
14
|
+
* The manager owns the socket, the streams, and the one timer that drives all of
|
|
15
|
+
* their retransmission clocks. Everything lives on the BitTorrent reactor thread.
|
|
16
|
+
*
|
|
17
|
+
* ## Deferred acknowledgements
|
|
18
|
+
*
|
|
19
|
+
* A peer sending at line rate delivers a burst of datagrams per wakeup. Acking
|
|
20
|
+
* each one costs a syscall per packet and floods the reverse path with 20-byte
|
|
21
|
+
* datagrams that say almost the same thing. So a stream that owes an ack registers
|
|
22
|
+
* here instead of sending, and the whole set is flushed once, after the socket has
|
|
23
|
+
* been drained — one ack per burst rather than one per packet. There is no timer
|
|
24
|
+
* involved: the ack still leaves in the same event-loop iteration it was earned in,
|
|
25
|
+
* so nothing waits on it.
|
|
26
|
+
*
|
|
27
|
+
* ## Port sharing
|
|
28
|
+
*
|
|
29
|
+
* The socket binds the *same* port as the TCP listener, because a peer learns one
|
|
30
|
+
* port for us (from the tracker or the DHT) and must be able to reach us over
|
|
31
|
+
* either transport with it. Client::open_listener() is what keeps the two in step.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
#include "librats/bittorrent/reactor.h"
|
|
35
|
+
#include "librats/bittorrent/utp_stream.h"
|
|
36
|
+
#include "librats/core/address.h"
|
|
37
|
+
#include "librats/core/socket.h"
|
|
38
|
+
|
|
39
|
+
#include <chrono>
|
|
40
|
+
#include <cstdint>
|
|
41
|
+
#include <functional>
|
|
42
|
+
#include <memory>
|
|
43
|
+
#include <string>
|
|
44
|
+
#include <unordered_map>
|
|
45
|
+
#include <vector>
|
|
46
|
+
|
|
47
|
+
namespace librats::bittorrent::utp {
|
|
48
|
+
|
|
49
|
+
/// How often every stream's timers are examined. Fine enough that a retransmission
|
|
50
|
+
/// timeout (500 ms at the very least) is not measurably late, coarse enough that a
|
|
51
|
+
/// few hundred idle streams cost nothing.
|
|
52
|
+
constexpr std::chrono::milliseconds kTickInterval{50};
|
|
53
|
+
|
|
54
|
+
class Manager final : public Host {
|
|
55
|
+
public:
|
|
56
|
+
/// Called with a freshly accepted inbound stream, once its SYN has been
|
|
57
|
+
/// processed. The handler takes over the stream by setting an observer on it;
|
|
58
|
+
/// leaving it without one lets the manager reap it in short order.
|
|
59
|
+
using AcceptHandler = std::function<void(Stream&)>;
|
|
60
|
+
|
|
61
|
+
explicit Manager(Reactor& reactor);
|
|
62
|
+
~Manager() override;
|
|
63
|
+
|
|
64
|
+
Manager(const Manager&) = delete;
|
|
65
|
+
Manager& operator=(const Manager&) = delete;
|
|
66
|
+
|
|
67
|
+
/// Bind the shared socket to @p port and start listening. Returns false if the
|
|
68
|
+
/// bind failed, in which case the session simply runs TCP-only.
|
|
69
|
+
bool open(std::uint16_t port, const std::string& bind_address = "");
|
|
70
|
+
void close();
|
|
71
|
+
|
|
72
|
+
bool is_open() const noexcept { return is_valid_socket(socket_); }
|
|
73
|
+
std::uint16_t port() const noexcept { return port_; }
|
|
74
|
+
|
|
75
|
+
void set_accept_handler(AcceptHandler h) { accept_ = std::move(h); }
|
|
76
|
+
/// Whether a SYN from an unknown peer opens a stream. Off means we still dial
|
|
77
|
+
/// out over uTP but never answer — the equivalent of a firewalled TCP port.
|
|
78
|
+
void set_accept_incoming(bool on) noexcept { accept_incoming_ = on; }
|
|
79
|
+
|
|
80
|
+
/// Open an outgoing stream to @p to and send its SYN. Returns nullptr if the
|
|
81
|
+
/// socket is not open or the stream cap has been reached. The stream is owned
|
|
82
|
+
/// here; the caller attaches an observer and must call release() when done.
|
|
83
|
+
Stream* connect(const Address& to);
|
|
84
|
+
|
|
85
|
+
/// Hand a stream back: it is detached from its observer and closed, then reaped
|
|
86
|
+
/// once it has finished saying goodbye. The caller's pointer is dead on return.
|
|
87
|
+
void release(Stream& s);
|
|
88
|
+
|
|
89
|
+
std::size_t num_streams() const noexcept { return streams_.size(); }
|
|
90
|
+
|
|
91
|
+
// ---- Host ----
|
|
92
|
+
void utp_send(const Address& to, const std::uint8_t* data, std::size_t len) override;
|
|
93
|
+
void utp_defer_ack(Stream& s) override;
|
|
94
|
+
|
|
95
|
+
/// Largest number of streams held at once, inbound and outbound together. A SYN
|
|
96
|
+
/// arriving past it is dropped rather than answered, so a flood costs one
|
|
97
|
+
/// hash lookup and nothing else.
|
|
98
|
+
static constexpr std::size_t kMaxStreams = 500;
|
|
99
|
+
|
|
100
|
+
private:
|
|
101
|
+
void on_readable();
|
|
102
|
+
void handle_datagram(const std::uint8_t* data, std::size_t len, const Address& from,
|
|
103
|
+
Stream::Clock::time_point now);
|
|
104
|
+
void tick();
|
|
105
|
+
void flush_deferred_acks(Stream::Clock::time_point now);
|
|
106
|
+
void reap(Stream::Clock::time_point now);
|
|
107
|
+
/// The stream registered under @p id that is also talking to @p from. Both must
|
|
108
|
+
/// match: an id alone is guessable, and a stream must not be hijackable by
|
|
109
|
+
/// anyone who happens to send from a different address.
|
|
110
|
+
Stream* find(const Address& from, std::uint16_t id);
|
|
111
|
+
|
|
112
|
+
Reactor& reactor_;
|
|
113
|
+
socket_t socket_ = RATS_INVALID_SOCKET;
|
|
114
|
+
std::uint16_t port_ = 0;
|
|
115
|
+
TimerId tick_timer_ = kInvalidTimerId;
|
|
116
|
+
AcceptHandler accept_;
|
|
117
|
+
bool accept_incoming_ = true;
|
|
118
|
+
|
|
119
|
+
/// Keyed by the stream's *receive* id — the id a peer puts in the datagrams it
|
|
120
|
+
/// sends us. A multimap because two unrelated peers may pick the same id; the
|
|
121
|
+
/// endpoint then separates them.
|
|
122
|
+
std::unordered_multimap<std::uint16_t, std::unique_ptr<Stream>> streams_;
|
|
123
|
+
|
|
124
|
+
/// Streams owing an acknowledgement at the end of the current drain.
|
|
125
|
+
std::vector<Stream*> deferred_;
|
|
126
|
+
/// Reused snapshot for iteration, so a callback that releases a stream cannot
|
|
127
|
+
/// invalidate the loop it fired from.
|
|
128
|
+
std::vector<Stream*> scratch_;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
} // namespace librats::bittorrent::utp
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file utp_packet.h
|
|
5
|
+
* @brief Wire format of uTP (BEP 29): one fixed 20-byte header plus a chain of
|
|
6
|
+
* optional extension records.
|
|
7
|
+
*
|
|
8
|
+
* uTP is the BitTorrent swarm's UDP transport. Unlike the node's own reliable-UDP
|
|
9
|
+
* layer (`transport/udp_packet.h`), nothing here is ours to choose: every byte is
|
|
10
|
+
* dictated by BEP 29 and by what uTorrent/libtorrent actually put on the wire, so
|
|
11
|
+
* this header exists to pin that format down exactly and nothing else.
|
|
12
|
+
*
|
|
13
|
+
* 0 4 8 16 24 32
|
|
14
|
+
* +-------+-------+---------------+---------------+---------------+
|
|
15
|
+
* | type | ver | extension | connection_id |
|
|
16
|
+
* +-------+-------+---------------+---------------+---------------+
|
|
17
|
+
* | timestamp_microseconds |
|
|
18
|
+
* +---------------+---------------+---------------+---------------+
|
|
19
|
+
* | timestamp_difference_microseconds |
|
|
20
|
+
* +---------------+---------------+---------------+---------------+
|
|
21
|
+
* | wnd_size |
|
|
22
|
+
* +---------------+---------------+---------------+---------------+
|
|
23
|
+
* | seq_nr | ack_nr |
|
|
24
|
+
* +---------------+---------------+---------------+---------------+
|
|
25
|
+
*
|
|
26
|
+
* Note the first byte: the *type* is the high nibble and the version the low one,
|
|
27
|
+
* which is the reverse of the layout a reader of the ASCII diagram in BEP 29 might
|
|
28
|
+
* assume. Everything is big-endian.
|
|
29
|
+
*
|
|
30
|
+
* - connection_id : the id the *receiver* of this datagram registered the stream
|
|
31
|
+
* under, so one shared socket demultiplexes every peer with a
|
|
32
|
+
* single hash lookup. The two ids of a connection are always
|
|
33
|
+
* adjacent (`id` and `id + 1`) — see utp_stream.h.
|
|
34
|
+
* - timestamp_microseconds : the sender's clock when it put the datagram on the
|
|
35
|
+
* wire. The receiver subtracts it from its own clock to get a
|
|
36
|
+
* one-way delay sample. The two clocks are unrelated, which is
|
|
37
|
+
* fine: only *changes* in the difference carry information.
|
|
38
|
+
* - timestamp_difference_microseconds : the last such sample the sender measured,
|
|
39
|
+
* reflected back. This is the congestion signal LEDBAT runs on
|
|
40
|
+
* and the whole reason uTP yields to TCP rather than competing
|
|
41
|
+
* with it.
|
|
42
|
+
* - wnd_size : the sender's free receive-buffer space **in bytes** (uTP is
|
|
43
|
+
* byte-windowed, unlike our own rudp which counts packets).
|
|
44
|
+
* - seq_nr : sequence number, in *packets*, of this datagram. ST_SYN,
|
|
45
|
+
* ST_DATA and ST_FIN each consume one; ST_STATE consumes none,
|
|
46
|
+
* which is why a pure ack is never itself acknowledged.
|
|
47
|
+
* - ack_nr : the last sequence number received in order.
|
|
48
|
+
*
|
|
49
|
+
* Sequence numbers are 16-bit and wrap roughly every 65 536 packets — about 80 MB
|
|
50
|
+
* at our payload size, i.e. several times a minute on a fast link. Never compare
|
|
51
|
+
* them with `<`; use seq_less()/seq_diff(), which are correct across the wrap.
|
|
52
|
+
*
|
|
53
|
+
* Extensions form a linked list: the header's `extension` byte names the first
|
|
54
|
+
* record's type, and each record is `[next_type:u8][len:u8][len bytes]`. Type 0
|
|
55
|
+
* ends the chain. We emit only Sack (1) and skip everything else, which is what
|
|
56
|
+
* lets a peer add records (uTorrent's close-reason, type 3) without breaking us.
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
#include "librats/bittorrent/byte_io.h"
|
|
60
|
+
#include "librats/core/bytes.h"
|
|
61
|
+
|
|
62
|
+
#include <cstddef>
|
|
63
|
+
#include <cstdint>
|
|
64
|
+
|
|
65
|
+
namespace librats::bittorrent::utp {
|
|
66
|
+
|
|
67
|
+
/// The only protocol version in existence. A datagram carrying anything else is
|
|
68
|
+
/// dropped in silence — an unauthenticated sender is owed no answer.
|
|
69
|
+
constexpr std::uint8_t kVersion = 1;
|
|
70
|
+
|
|
71
|
+
/// Packet types, in BEP 29's numbering (which is *not* the order they occur in).
|
|
72
|
+
enum class PacketType : std::uint8_t {
|
|
73
|
+
Data = 0, ///< stream payload; consumes a sequence number
|
|
74
|
+
Fin = 1, ///< orderly end of the sender's stream; consumes a sequence number
|
|
75
|
+
State = 2, ///< pure acknowledgement / window update; consumes nothing
|
|
76
|
+
Reset = 3, ///< abort now: the stream is gone or was never known
|
|
77
|
+
Syn = 4, ///< open a stream; consumes a sequence number
|
|
78
|
+
};
|
|
79
|
+
constexpr std::uint8_t kNumPacketTypes = 5;
|
|
80
|
+
|
|
81
|
+
/// Extension record types. 2 is deliberately absent: an obsolete extension in the
|
|
82
|
+
/// wild used it, so BEP 29 skipped it when assigning `close_reason`.
|
|
83
|
+
enum class ExtensionType : std::uint8_t {
|
|
84
|
+
None = 0,
|
|
85
|
+
Sack = 1,
|
|
86
|
+
CloseReason = 3,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
constexpr std::size_t kHeaderSize = 20;
|
|
90
|
+
|
|
91
|
+
/// Bytes of selective-ack bitmap we emit. BEP 29 allows any multiple of 4; 4 bytes
|
|
92
|
+
/// name the 32 packets after the hole, which is the same reach our own rudp gives
|
|
93
|
+
/// itself and comfortably more than the 3-duplicate-ack window fast retransmit
|
|
94
|
+
/// actually acts on.
|
|
95
|
+
constexpr std::size_t kSackBytes = 4;
|
|
96
|
+
|
|
97
|
+
/// Payload one Data packet carries. uTP has no path-MTU discovery here (libtorrent
|
|
98
|
+
/// probes; we deliberately do not — see utp_stream.h), so this is chosen to survive
|
|
99
|
+
/// any path without IP fragmentation: IPv6's 1280-byte floor, less a 40-byte IPv6
|
|
100
|
+
/// header, an 8-byte UDP header and our own 20 + 6 of uTP header and SACK, rounded
|
|
101
|
+
/// down. Fragmenting would turn one lost fragment into a lost packet, which on a
|
|
102
|
+
/// congestion-controlled stream costs far more than the bytes saved.
|
|
103
|
+
constexpr std::size_t kMaxPayload = 1200;
|
|
104
|
+
|
|
105
|
+
/// Largest datagram we ever send. A peer may send us more (its MTU probe may have
|
|
106
|
+
/// found a bigger path), so the receive path sizes its buffer independently.
|
|
107
|
+
constexpr std::size_t kMaxDatagram = kHeaderSize + 2 + kSackBytes + kMaxPayload;
|
|
108
|
+
|
|
109
|
+
/// Biggest datagram we will accept. Generous enough for any real peer's MTU
|
|
110
|
+
/// (jumbo frames included) while still bounding what one recvfrom can hand us.
|
|
111
|
+
constexpr std::size_t kMaxRecvDatagram = 9216;
|
|
112
|
+
|
|
113
|
+
/// The decoded fixed header. Extensions are walked separately (see parse_header),
|
|
114
|
+
/// because their length is only known after the fact.
|
|
115
|
+
struct Header {
|
|
116
|
+
PacketType type = PacketType::Data;
|
|
117
|
+
std::uint8_t version = 0;
|
|
118
|
+
std::uint8_t extension = 0; ///< type of the first extension record, 0 if none
|
|
119
|
+
std::uint16_t connection_id = 0;
|
|
120
|
+
std::uint32_t timestamp = 0; ///< microseconds, sender's clock
|
|
121
|
+
std::uint32_t timestamp_diff = 0;///< microseconds, last one-way delay it measured
|
|
122
|
+
std::uint32_t wnd_size = 0; ///< sender's free receive buffer, in bytes
|
|
123
|
+
std::uint16_t seq_nr = 0;
|
|
124
|
+
std::uint16_t ack_nr = 0;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/// Decode the fixed header. Returns false when the datagram is too short or names
|
|
128
|
+
/// a version we do not speak; the type is *not* validated here so the caller can
|
|
129
|
+
/// tell an unknown type from a malformed datagram.
|
|
130
|
+
inline bool parse_header(const std::uint8_t* data, std::size_t len, Header& out) noexcept {
|
|
131
|
+
if (len < kHeaderSize) return false;
|
|
132
|
+
out.version = std::uint8_t(data[0] & 0x0f);
|
|
133
|
+
if (out.version != kVersion) return false;
|
|
134
|
+
out.type = PacketType(data[0] >> 4);
|
|
135
|
+
out.extension = data[1];
|
|
136
|
+
out.connection_id = read_u16_be(data + 2);
|
|
137
|
+
out.timestamp = read_u32_be(data + 4);
|
|
138
|
+
out.timestamp_diff = read_u32_be(data + 8);
|
|
139
|
+
out.wnd_size = read_u32_be(data + 12);
|
|
140
|
+
out.seq_nr = read_u16_be(data + 16);
|
|
141
|
+
out.ack_nr = read_u16_be(data + 18);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/// Write the fixed header into a buffer of at least kHeaderSize bytes.
|
|
146
|
+
inline void write_header(std::uint8_t* out, const Header& h) noexcept {
|
|
147
|
+
out[0] = std::uint8_t((std::uint8_t(h.type) << 4) | (kVersion & 0x0f));
|
|
148
|
+
out[1] = h.extension;
|
|
149
|
+
write_u16_be(out + 2, h.connection_id);
|
|
150
|
+
write_u32_be(out + 4, h.timestamp);
|
|
151
|
+
write_u32_be(out + 8, h.timestamp_diff);
|
|
152
|
+
write_u32_be(out + 12, h.wnd_size);
|
|
153
|
+
write_u16_be(out + 16, h.seq_nr);
|
|
154
|
+
write_u16_be(out + 18, h.ack_nr);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/// One extension record found while walking the chain.
|
|
158
|
+
struct Extension {
|
|
159
|
+
ExtensionType type = ExtensionType::None;
|
|
160
|
+
const std::uint8_t* data = nullptr;
|
|
161
|
+
std::size_t len = 0;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Walk the extension chain, calling @p fn for each record.
|
|
166
|
+
*
|
|
167
|
+
* @return the offset of the payload (i.e. the total header size), or 0 if the
|
|
168
|
+
* chain is malformed — a record that claims to run past the end of the
|
|
169
|
+
* datagram, which a hostile peer can trivially send. Callers must treat 0
|
|
170
|
+
* as "drop this datagram" rather than "no payload".
|
|
171
|
+
*/
|
|
172
|
+
template <class Fn>
|
|
173
|
+
std::size_t walk_extensions(const std::uint8_t* data, std::size_t len,
|
|
174
|
+
const Header& h, Fn&& fn) {
|
|
175
|
+
std::size_t off = kHeaderSize;
|
|
176
|
+
std::uint8_t next = h.extension;
|
|
177
|
+
// A chain longer than this is a peer trying to make us spin, not a peer with
|
|
178
|
+
// something to say: BEP 29 defines two record types in total.
|
|
179
|
+
for (int guard = 0; next != 0 && guard < 16; ++guard) {
|
|
180
|
+
if (off + 2 > len) return 0;
|
|
181
|
+
const std::uint8_t type = next;
|
|
182
|
+
next = data[off];
|
|
183
|
+
const std::size_t rec = data[off + 1];
|
|
184
|
+
off += 2;
|
|
185
|
+
if (off + rec > len) return 0;
|
|
186
|
+
fn(Extension{ExtensionType(type), data + off, rec});
|
|
187
|
+
off += rec;
|
|
188
|
+
}
|
|
189
|
+
return next == 0 ? off : 0;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ---- 16-bit sequence arithmetic ----------------------------------------------
|
|
193
|
+
//
|
|
194
|
+
// Sequence numbers wrap, so "less than" means "fewer than half the space ahead".
|
|
195
|
+
// Both helpers are exact inverses of each other over the whole 16-bit range and
|
|
196
|
+
// have no undefined behaviour at the wrap point — which is the entire reason they
|
|
197
|
+
// exist rather than being written out at each call site.
|
|
198
|
+
|
|
199
|
+
/// True when @p a precedes @p b in sequence space.
|
|
200
|
+
inline bool seq_less(std::uint16_t a, std::uint16_t b) noexcept {
|
|
201
|
+
return std::uint16_t(b - a) != 0 && std::uint16_t(b - a) < 0x8000;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/// Signed distance from @p a to @p b (positive when b is ahead).
|
|
205
|
+
inline int seq_diff(std::uint16_t b, std::uint16_t a) noexcept {
|
|
206
|
+
return int(std::int16_t(std::uint16_t(b - a)));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/// The same "less than, modulo the wrap" test over the full 32-bit space. Used for
|
|
210
|
+
/// the microsecond timestamps, which are a truncated clock and therefore wrap just
|
|
211
|
+
/// like a sequence number — about every 71 minutes.
|
|
212
|
+
inline bool seq_less_u32(std::uint32_t a, std::uint32_t b) noexcept {
|
|
213
|
+
return std::uint32_t(b - a) != 0 && std::uint32_t(b - a) < 0x80000000u;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
} // namespace librats::bittorrent::utp
|