librats 0.9.1 → 1.0.0

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.
@@ -41,11 +41,38 @@ bool is_valid_ipv6(const std::string& ip_str);
41
41
  */
42
42
  bool is_hostname(const std::string& str);
43
43
 
44
+ /**
45
+ * Check whether an IP address is publicly routable (not a private/reserved range).
46
+ *
47
+ * Returns false for RFC1918 (10/8, 172.16/12, 192.168/16), CGNAT (100.64/10),
48
+ * loopback, link-local (169.254/16, fe80::/10), unspecified, multicast/reserved,
49
+ * and IPv6 unique-local (fc00::/7). A non-IP / unparseable string yields false.
50
+ *
51
+ * Used both by the DHT (BEP 42 external-IP voting) and by automatic port
52
+ * forwarding to detect a double-NAT gateway whose reported "external" IP is itself
53
+ * private and therefore not a usable public endpoint.
54
+ */
55
+ bool is_public_ip(const std::string& ip);
56
+
44
57
  /**
45
58
  * Get all local network interface addresses (IPv4 and IPv6)
46
59
  * @return Vector of local IP addresses from all network interfaces
47
60
  */
48
61
  std::vector<std::string> get_local_interface_addresses();
49
62
 
63
+ /**
64
+ * Get the default IPv4 gateway address(es) of the host.
65
+ *
66
+ * Used for NAT port forwarding (NAT-PMP talks to the gateway directly, and UPnP
67
+ * can use it to restrict discovery to the local router). The OS routing table is
68
+ * consulted where available (Windows iphlpapi, Linux /proc/net/route). When the
69
+ * platform routing table cannot be read, a best-effort heuristic derived from the
70
+ * local IPv4 addresses (network .1) is appended so callers still have a candidate
71
+ * to try.
72
+ *
73
+ * @return Vector of gateway IPv4 addresses, most specific first. May be empty.
74
+ */
75
+ std::vector<std::string> get_default_gateways();
76
+
50
77
  } // namespace network_utils
51
78
  } // namespace librats
@@ -0,0 +1,78 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file port_mapping.h
5
+ * @brief Shared types for automatic NAT port forwarding (UPnP IGD + NAT-PMP)
6
+ *
7
+ * Both the UPnP (@ref UpnpClient) and NAT-PMP (@ref NatPmpClient) backends ask a
8
+ * home router to forward an external (WAN) port to a local (LAN) port so that
9
+ * inbound peer connections can reach a host behind NAT. They share the small set
10
+ * of vocabulary types defined here: the transport protocol of the mapping, which
11
+ * backend produced a result, and the result/callback shape RatsClient consumes.
12
+ */
13
+
14
+ #include <cstdint>
15
+ #include <string>
16
+ #include <functional>
17
+
18
+ namespace librats {
19
+
20
+ /// Transport protocol of a port mapping.
21
+ enum class PortMapProtocol : uint8_t {
22
+ TCP,
23
+ UDP
24
+ };
25
+
26
+ /// Which NAT traversal backend produced a result.
27
+ enum class PortMapTransport : uint8_t {
28
+ UPnP,
29
+ NatPMP
30
+ };
31
+
32
+ /// Human readable protocol name ("TCP"/"UDP").
33
+ inline const char* to_string(PortMapProtocol p) {
34
+ return p == PortMapProtocol::TCP ? "TCP" : "UDP";
35
+ }
36
+
37
+ /// Human readable transport name ("UPnP"/"NAT-PMP").
38
+ inline const char* to_string(PortMapTransport t) {
39
+ return t == PortMapTransport::UPnP ? "UPnP" : "NAT-PMP";
40
+ }
41
+
42
+ /**
43
+ * Result of a port mapping attempt.
44
+ *
45
+ * On success @ref external_port holds the public port the router assigned (which
46
+ * may differ from the requested one) and, when the backend can report it,
47
+ * @ref external_ip holds the discovered public IP address.
48
+ */
49
+ struct PortMapResult {
50
+ PortMapTransport transport; ///< Backend that produced this result
51
+ PortMapProtocol protocol; ///< Protocol of the mapping
52
+ bool success = false; ///< Whether the mapping is currently active
53
+ uint16_t internal_port = 0; ///< Local (LAN) port that was mapped
54
+ uint16_t external_port = 0; ///< Public (WAN) port assigned by the router
55
+ std::string external_ip; ///< Discovered public IP (may be empty)
56
+ std::string error; ///< Human readable error when !success
57
+ };
58
+
59
+ /**
60
+ * Callback invoked whenever a mapping is established, refreshed, removed or fails.
61
+ * Always called from the backend's own worker thread.
62
+ */
63
+ using PortMapCallback = std::function<void(const PortMapResult&)>;
64
+
65
+ /**
66
+ * Configuration for RatsClient's automatic port forwarding.
67
+ *
68
+ * Both backends run in parallel by default; whichever the router supports
69
+ * succeeds. Disabling one (or all) of them is a matter of flipping a flag.
70
+ */
71
+ struct PortMappingConfig {
72
+ bool enabled = true; ///< Master switch for automatic port forwarding
73
+ bool enable_upnp = true; ///< Use the UPnP IGD backend
74
+ bool enable_natpmp = true; ///< Use the NAT-PMP backend
75
+ uint32_t lease_duration_seconds = 3600; ///< Requested lease duration
76
+ };
77
+
78
+ } // namespace librats
@@ -844,18 +844,29 @@ int send_udp_data(socket_t socket, const std::vector<uint8_t>& data,
844
844
  }
845
845
 
846
846
  std::vector<uint8_t> receive_udp_data(socket_t socket, size_t buffer_size, Peer& sender_peer,
847
- int timeout_ms) {
848
- // Handle timeout using select
849
- if (timeout_ms >= 0) {
847
+ int timeout_ms, socket_t interrupt_fd) {
848
+ // Handle timeout (and optional interrupt socket) using select. When no interrupt
849
+ // fd is supplied this path is identical to the plain timeout behavior.
850
+ const bool have_interrupt = is_valid_socket(interrupt_fd);
851
+ if (timeout_ms >= 0 || have_interrupt) {
850
852
  fd_set read_fds;
851
853
  FD_ZERO(&read_fds);
852
854
  FD_SET(socket, &read_fds);
855
+ socket_t maxfd = socket;
856
+ if (have_interrupt) {
857
+ FD_SET(interrupt_fd, &read_fds);
858
+ if (interrupt_fd > maxfd) maxfd = interrupt_fd;
859
+ }
853
860
 
854
861
  struct timeval timeout;
855
- timeout.tv_sec = timeout_ms / 1000;
856
- timeout.tv_usec = (timeout_ms % 1000) * 1000;
862
+ struct timeval* ptimeout = nullptr; // timeout_ms < 0 => block until readable
863
+ if (timeout_ms >= 0) {
864
+ timeout.tv_sec = timeout_ms / 1000;
865
+ timeout.tv_usec = (timeout_ms % 1000) * 1000;
866
+ ptimeout = &timeout;
867
+ }
857
868
 
858
- int result = select(socket + 1, &read_fds, nullptr, nullptr, &timeout);
869
+ int result = select(static_cast<int>(maxfd) + 1, &read_fds, nullptr, nullptr, ptimeout);
859
870
  if (result == 0) {
860
871
  LOG_SOCKET_DEBUG("UDP receive timeout (" << timeout_ms << "ms)");
861
872
  return {};
@@ -863,6 +874,15 @@ std::vector<uint8_t> receive_udp_data(socket_t socket, size_t buffer_size, Peer&
863
874
  LOG_SOCKET_ERROR("Select error while waiting for UDP data");
864
875
  return {};
865
876
  }
877
+ // Woken by the interrupt socket (e.g. stop requested): leave the data socket
878
+ // untouched and report no data so the caller can re-check its stop flag.
879
+ if (have_interrupt && FD_ISSET(interrupt_fd, &read_fds)) {
880
+ return {};
881
+ }
882
+ // Guard against calling recvfrom on a data socket that isn't actually ready.
883
+ if (!FD_ISSET(socket, &read_fds)) {
884
+ return {};
885
+ }
866
886
  }
867
887
 
868
888
  std::vector<uint8_t> buffer(buffer_size);
@@ -170,10 +170,14 @@ int send_udp_data(socket_t socket, const std::vector<uint8_t>& data, const std::
170
170
  * @param buffer_size Maximum number of bytes to receive
171
171
  * @param sender_peer Output parameter for the sender's peer info
172
172
  * @param timeout_ms Timeout in milliseconds (-1 for blocking, 0 for non-blocking, >0 for timeout)
173
- * @return Received data, empty vector on timeout or error
173
+ * @param interrupt_fd Optional second socket to watch; when it becomes readable the
174
+ * call returns immediately with an empty vector (used to wake a
175
+ * blocking receive on shutdown). INVALID_SOCKET_VALUE disables it.
176
+ * @return Received data, empty vector on timeout, error or interrupt
174
177
  */
175
178
  std::vector<uint8_t> receive_udp_data(socket_t socket, size_t buffer_size, Peer& sender_peer,
176
- int timeout_ms = -1);
179
+ int timeout_ms = -1,
180
+ socket_t interrupt_fd = INVALID_SOCKET_VALUE);
177
181
 
178
182
  // Common Socket Functions
179
183
  /**