librats 2.3.4 → 2.3.6
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.
|
@@ -73,19 +73,39 @@ void Client::open_listener() {
|
|
|
73
73
|
|
|
74
74
|
// TCP and uTP must answer on the *same* port: a peer learns one number for us
|
|
75
75
|
// (from the tracker, the DHT or PEX) and has to be able to reach us with it
|
|
76
|
-
// over either wire.
|
|
77
|
-
//
|
|
78
|
-
//
|
|
76
|
+
// over either wire. So the pair is acquired as a pair and retried as one — and
|
|
77
|
+
// which half picks the number is what decides whether the retry buys anything.
|
|
78
|
+
//
|
|
79
|
+
// On a fixed port nothing is picked: TCP takes the number it was given, and the
|
|
80
|
+
// mux either gets the UDP half or moves aside (the DHT case — mainline serves
|
|
81
|
+
// the DHT on the torrent port). On an ephemeral port the *UDP* half chooses and
|
|
82
|
+
// TCP follows it, because UDP is the scarce side. An ephemeral UDP bind is by
|
|
83
|
+
// construction a number nobody holds, while a TCP-chosen number is only a guess
|
|
84
|
+
// about UDP — and a guess a retry cannot improve on, since the OS hands out
|
|
85
|
+
// ephemeral ports in sequence: any block-shaped obstruction (a reserved range,
|
|
86
|
+
// a neighbouring process's mux) takes all eight consecutive attempts with it.
|
|
79
87
|
constexpr int kPairAttempts = 8;
|
|
80
88
|
for (int attempt = 0; attempt < kPairAttempts; ++attempt) {
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
// open(0) is idempotent, so this both opens the mux and re-reads its port.
|
|
90
|
+
// A mux that cannot open at all must not cost us the TCP listener too, so
|
|
91
|
+
// it falls back to TCP picking — where there is nothing left to pair with.
|
|
92
|
+
const bool udp_first = want_utp && config_.listen_port == 0 && utp_.open(0);
|
|
93
|
+
const int want = udp_first ? int(utp_.port()) : config_.listen_port;
|
|
94
|
+
const socket_t tcp = create_tcp_server(want, 16, "", AddressFamily::IPv4);
|
|
95
|
+
if (!is_valid_socket(tcp)) {
|
|
96
|
+
// Only the UDP-first path has anywhere to go: drop this number and let
|
|
97
|
+
// the next bind choose another. On the last go keep the mux anyway —
|
|
98
|
+
// dialling out still works without a listener.
|
|
99
|
+
if (!udp_first || attempt + 1 == kPairAttempts) break;
|
|
100
|
+
utp_.close();
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
83
103
|
const std::uint16_t port = std::uint16_t(get_bound_port(tcp));
|
|
84
104
|
// Keep this TCP socket when the pair came up, when a fixed port leaves us
|
|
85
105
|
// nowhere to move, or when this was the last go — running out of attempts
|
|
86
106
|
// must not cost us a listener we already hold. Only a retry that is really
|
|
87
107
|
// going to happen may throw one away.
|
|
88
|
-
if (!want_utp || utp_.open(port) || config_.listen_port != 0
|
|
108
|
+
if (udp_first || !want_utp || utp_.open(port) || config_.listen_port != 0
|
|
89
109
|
|| attempt + 1 == kPairAttempts) {
|
|
90
110
|
listener_ = tcp;
|
|
91
111
|
actual_port_ = port;
|