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. When the port is ephemeral the TCP bind picks it and the
77
- // UDP bind may then find that number already taken by something else, so the
78
- // pair is acquired as a pair and retried as one.
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
- const socket_t tcp = create_tcp_server(config_.listen_port, 16, "", AddressFamily::IPv4);
82
- if (!is_valid_socket(tcp)) break;
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "librats",
3
- "version": "2.3.4",
3
+ "version": "2.3.6",
4
4
  "description": "Node.js bindings for librats - A high-performance peer-to-peer networking library",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",