Back to Site
Loading...
Searching...
No Matches
stun.h
Go to the documentation of this file.
1#pragma once
2
11#include "socket.h"
12#include <array>
13#include <vector>
14#include <string>
15#include <optional>
16#include <cstdint>
17#include <memory>
18#include <chrono>
19#include <random>
20
21namespace librats {
22
23// ============================================================================
24// STUN Constants (RFC 5389)
25// ============================================================================
26
28constexpr uint32_t STUN_MAGIC_COOKIE = 0x2112A442;
29
31constexpr size_t STUN_HEADER_SIZE = 20;
32
34constexpr size_t STUN_TRANSACTION_ID_SIZE = 12;
35
37constexpr uint16_t STUN_DEFAULT_PORT = 3478;
38
40constexpr uint16_t STUNS_DEFAULT_PORT = 5349;
41
43constexpr size_t STUN_MAX_MESSAGE_SIZE = 1500;
44
46constexpr int STUN_DEFAULT_RTO_MS = 500;
47
49constexpr int STUN_MAX_RETRANSMISSIONS = 7;
50
51// ============================================================================
52// STUN Message Types (RFC 5389 Section 6)
53// ============================================================================
54
58enum class StunMessageClass : uint8_t {
59 Request = 0x00,
60 Indication = 0x01,
61 SuccessResponse = 0x02,
62 ErrorResponse = 0x03
63};
64
68enum class StunMethod : uint16_t {
69 Binding = 0x001,
70 // TURN methods (RFC 5766)
71 Allocate = 0x003,
72 Refresh = 0x004,
73 Send = 0x006,
74 Data = 0x007,
75 CreatePermission = 0x008,
76 ChannelBind = 0x009
77};
78
82enum class StunMessageType : uint16_t {
83 // Binding
84 BindingRequest = 0x0001,
85 BindingIndication = 0x0011,
87 BindingErrorResponse = 0x0111,
88
89 // TURN Allocate
90 AllocateRequest = 0x0003,
92 AllocateErrorResponse = 0x0113,
93
94 // TURN Refresh
95 RefreshRequest = 0x0004,
97 RefreshErrorResponse = 0x0114,
98
99 // TURN Send/Data
100 SendIndication = 0x0016,
101 DataIndication = 0x0017,
102
103 // TURN CreatePermission
107
108 // TURN ChannelBind
109 ChannelBindRequest = 0x0009,
112};
113
114// ============================================================================
115// STUN Attribute Types (RFC 5389 Section 15)
116// ============================================================================
117
118enum class StunAttributeType : uint16_t {
119 // Comprehension-required (0x0000-0x7FFF)
120 MappedAddress = 0x0001,
121 Username = 0x0006,
122 MessageIntegrity = 0x0008,
123 ErrorCode = 0x0009,
124 UnknownAttributes = 0x000A,
125 Realm = 0x0014,
126 Nonce = 0x0015,
127 XorMappedAddress = 0x0020,
128
129 // TURN attributes (RFC 5766)
130 ChannelNumber = 0x000C,
131 Lifetime = 0x000D,
132 XorPeerAddress = 0x0012,
133 Data = 0x0013,
134 XorRelayedAddress = 0x0016,
135 RequestedTransport = 0x0019,
136 DontFragment = 0x001A,
137
138 // Comprehension-optional (0x8000-0xFFFF)
139 Software = 0x8022,
140 AlternateServer = 0x8023,
141 Fingerprint = 0x8028
142};
143
144// ============================================================================
145// STUN Address Family
146// ============================================================================
147
148enum class StunAddressFamily : uint8_t {
149 IPv4 = 0x01,
150 IPv6 = 0x02
151};
152
153// ============================================================================
154// STUN Error Codes (RFC 5389 Section 15.6)
155// ============================================================================
156
157enum class StunErrorCode : uint16_t {
158 TryAlternate = 300,
159 BadRequest = 400,
160 Unauthorized = 401,
161 Forbidden = 403,
162 UnknownAttribute = 420,
163 StaleNonce = 438,
164 ServerError = 500,
165
166 // TURN error codes (RFC 5766)
167 AllocationMismatch = 437,
168 WrongCredentials = 441,
172};
173
174// ============================================================================
175// STUN Data Structures
176// ============================================================================
177
183 std::string address;
184 uint16_t port;
185
187 StunMappedAddress(StunAddressFamily f, const std::string& addr, uint16_t p)
188 : family(f), address(addr), port(p) {}
189
190 bool is_valid() const { return !address.empty() && port > 0; }
191
192 std::string to_string() const {
194 return "[" + address + "]:" + std::to_string(port);
195 }
196 return address + ":" + std::to_string(port);
197 }
198};
199
203struct StunError {
205 std::string reason;
206
208 StunError(StunErrorCode c, const std::string& r = "") : code(c), reason(r) {}
209};
210
216 std::vector<uint8_t> value;
217
219 StunAttribute(StunAttributeType t, const std::vector<uint8_t>& v) : type(t), value(v) {}
220
221 size_t padded_length() const {
222 // STUN attributes are padded to 4-byte boundary
223 return (value.size() + 3) & ~3;
224 }
225};
226
232 std::array<uint8_t, STUN_TRANSACTION_ID_SIZE> transaction_id;
233 std::vector<StunAttribute> attributes;
234
238
242
245
248
251
253 bool is_request() const { return get_class() == StunMessageClass::Request; }
254
257
260
263
265 void add_attribute(StunAttributeType attr_type, const std::vector<uint8_t>& value);
266
269
272
274 void add_error_code(StunErrorCode code, const std::string& reason = "");
275
277 void add_username(const std::string& username);
278
280 void add_realm(const std::string& realm);
281
283 void add_nonce(const std::string& nonce);
284
286 void add_software(const std::string& software);
287
289 void add_lifetime(uint32_t seconds);
290
292 void add_requested_transport(uint8_t protocol);
293
296
298 void add_data(const std::vector<uint8_t>& data);
299
301 void add_channel_number(uint16_t channel);
302
304 std::optional<StunMappedAddress> get_xor_mapped_address() const;
305
307 std::optional<StunMappedAddress> get_mapped_address() const;
308
310 std::optional<StunMappedAddress> get_xor_relayed_address() const;
311
313 std::optional<StunMappedAddress> get_xor_peer_address() const;
314
316 std::optional<StunError> get_error() const;
317
319 std::optional<uint32_t> get_lifetime() const;
320
322 std::optional<std::vector<uint8_t>> get_data() const;
323
325 std::optional<std::string> get_realm() const;
326
328 std::optional<std::string> get_nonce() const;
329
331 std::vector<uint8_t> serialize() const;
332
334 std::vector<uint8_t> serialize_with_integrity(const std::string& key) const;
335
337 static std::optional<StunMessage> deserialize(const std::vector<uint8_t>& data);
338
340 static bool is_stun_message(const std::vector<uint8_t>& data);
341};
342
343// ============================================================================
344// STUN Client
345// ============================================================================
346
351 int rto_ms = STUN_DEFAULT_RTO_MS; // Initial retransmission timeout
353 int total_timeout_ms = 39500; // Total timeout (RFC 5389: 39.5 seconds)
354 std::string software = "librats"; // Software attribute value
355
356 StunClientConfig() = default;
357};
358
364 std::optional<StunMappedAddress> mapped_address;
365 std::optional<StunError> error;
366 int rtt_ms; // Round-trip time in milliseconds
367
368 StunResult() : success(false), rtt_ms(0) {}
369};
370
384public:
388
389 // Non-copyable, movable
390 StunClient(const StunClient&) = delete;
391 StunClient& operator=(const StunClient&) = delete;
393 StunClient& operator=(StunClient&&) noexcept;
394
402 StunResult binding_request(const std::string& server,
403 uint16_t port = STUN_DEFAULT_PORT,
404 int timeout_ms = 0);
405
416 const std::string& server,
417 uint16_t port,
418 int timeout_ms = 0);
419
430 const StunMessage& request,
431 const std::string& server,
432 uint16_t port,
433 int timeout_ms);
434
438 const StunClientConfig& config() const { return config_; }
439
443 void set_config(const StunClientConfig& config) { config_ = config; }
444
445private:
446 StunClientConfig config_;
447 std::mt19937 rng_;
448};
449
450// ============================================================================
451// STUN Utility Functions
452// ============================================================================
453
458uint32_t stun_crc32(const uint8_t* data, size_t length);
459
466std::array<uint8_t, 20> stun_hmac_sha1(const std::vector<uint8_t>& key,
467 const std::vector<uint8_t>& data);
468
472std::vector<uint8_t> stun_compute_long_term_key(const std::string& username,
473 const std::string& realm,
474 const std::string& password);
475
481 const std::array<uint8_t, STUN_TRANSACTION_ID_SIZE>& transaction_id);
482
486std::vector<std::pair<std::string, uint16_t>> get_public_stun_servers();
487
488} // namespace librats
STUN Client for NAT traversal.
Definition stun.h:383
StunClient(const StunClient &)=delete
void set_config(const StunClientConfig &config)
Set configuration.
Definition stun.h:443
StunClient(const StunClientConfig &config)
StunClient(StunClient &&) noexcept
const StunClientConfig & config() const
Get the configuration.
Definition stun.h:438
StunClient & operator=(const StunClient &)=delete
std::optional< StunMessage > send_request(socket_t socket, const StunMessage &request, const std::string &server, uint16_t port, int timeout_ms)
Send a raw STUN message and wait for response.
StunResult binding_request_with_socket(socket_t socket, const std::string &server, uint16_t port, int timeout_ms=0)
Send STUN Binding Request using existing socket Useful when you need to discover the mapped address f...
StunResult binding_request(const std::string &server, uint16_t port=STUN_DEFAULT_PORT, int timeout_ms=0)
Send STUN Binding Request to discover public address.
constexpr size_t STUN_HEADER_SIZE
STUN header size in bytes.
Definition stun.h:31
constexpr int STUN_MAX_RETRANSMISSIONS
Maximum retransmissions.
Definition stun.h:49
StunAddressFamily
Definition stun.h:148
StunMethod
STUN method (12 bits, only Binding is defined in RFC 5389)
Definition stun.h:68
constexpr size_t STUN_MAX_MESSAGE_SIZE
Maximum STUN message size (RFC 5389 recommends path MTU, typically ~1500)
Definition stun.h:43
constexpr uint32_t STUN_MAGIC_COOKIE
STUN Magic Cookie (fixed value per RFC 5389)
Definition stun.h:28
constexpr int STUN_DEFAULT_RTO_MS
Default retransmission timeout (ms)
Definition stun.h:46
std::vector< std::pair< std::string, uint16_t > > get_public_stun_servers()
Get a list of well-known public STUN servers.
StunMessageType
Combined STUN message type (method + class)
Definition stun.h:82
constexpr size_t STUN_TRANSACTION_ID_SIZE
STUN transaction ID size in bytes.
Definition stun.h:34
uint32_t stun_crc32(const uint8_t *data, size_t length)
Compute CRC32 for STUN FINGERPRINT attribute Uses CRC-32 as defined in RFC 5389 (ISO 3309)
StunAttributeType
Definition stun.h:118
std::vector< uint8_t > stun_compute_long_term_key(const std::string &username, const std::string &realm, const std::string &password)
Compute long-term credential key: MD5(username:realm:password)
StunMappedAddress stun_xor_address(const StunMappedAddress &addr, const std::array< uint8_t, STUN_TRANSACTION_ID_SIZE > &transaction_id)
XOR an address with the magic cookie and transaction ID Used for XOR-MAPPED-ADDRESS encoding/decoding...
std::array< uint8_t, 20 > stun_hmac_sha1(const std::vector< uint8_t > &key, const std::vector< uint8_t > &data)
Compute HMAC-SHA1 for MESSAGE-INTEGRITY attribute.
StunErrorCode
Definition stun.h:157
constexpr uint16_t STUNS_DEFAULT_PORT
STUN over TLS default port.
Definition stun.h:40
constexpr uint16_t STUN_DEFAULT_PORT
Default STUN port.
Definition stun.h:37
StunMessageClass
STUN message class (2 bits)
Definition stun.h:58
STL namespace.
int socket_t
Definition socket.h:22
STUN attribute base class.
Definition stun.h:214
StunAttributeType type
Definition stun.h:215
std::vector< uint8_t > value
Definition stun.h:216
size_t padded_length() const
Definition stun.h:221
StunAttribute(StunAttributeType t, const std::vector< uint8_t > &v)
Definition stun.h:219
STUN client configuration.
Definition stun.h:350
std::string software
Definition stun.h:354
STUN error information.
Definition stun.h:203
StunError(StunErrorCode c, const std::string &r="")
Definition stun.h:208
std::string reason
Definition stun.h:205
StunErrorCode code
Definition stun.h:204
STUN mapped address (result of binding request)
Definition stun.h:181
bool is_valid() const
Definition stun.h:190
StunMappedAddress(StunAddressFamily f, const std::string &addr, uint16_t p)
Definition stun.h:187
std::string address
Definition stun.h:183
StunAddressFamily family
Definition stun.h:182
std::string to_string() const
Definition stun.h:192
STUN message structure.
Definition stun.h:230
static bool is_stun_message(const std::vector< uint8_t > &data)
Check if data looks like a STUN message.
std::optional< StunMappedAddress > get_xor_peer_address() const
Parse XOR-PEER-ADDRESS from attributes (TURN)
void add_xor_peer_address(const StunMappedAddress &addr)
Add XOR-PEER-ADDRESS attribute (TURN)
static std::optional< StunMessage > deserialize(const std::vector< uint8_t > &data)
Deserialize message from bytes.
void add_error_code(StunErrorCode code, const std::string &reason="")
Add ERROR-CODE attribute.
void add_channel_number(uint16_t channel)
Add CHANNEL-NUMBER attribute (TURN)
std::vector< StunAttribute > attributes
Definition stun.h:233
void add_lifetime(uint32_t seconds)
Add LIFETIME attribute (TURN)
std::optional< std::string > get_realm() const
Parse REALM from attributes.
void add_realm(const std::string &realm)
Add REALM attribute.
void add_attribute(StunAttributeType attr_type, const std::vector< uint8_t > &value)
Add an attribute.
void add_xor_relayed_address(const StunMappedAddress &addr)
Add XOR-RELAYED-ADDRESS attribute (TURN)
void add_requested_transport(uint8_t protocol)
Add REQUESTED-TRANSPORT attribute (TURN)
bool is_error_response() const
Check if this is an error response.
Definition stun.h:259
std::optional< std::vector< uint8_t > > get_data() const
Parse DATA from attributes (TURN)
std::optional< uint32_t > get_lifetime() const
Parse LIFETIME from attributes (TURN)
void add_data(const std::vector< uint8_t > &data)
Add DATA attribute (TURN)
std::optional< StunError > get_error() const
Parse ERROR-CODE from attributes.
StunMethod get_method() const
Get method from type.
std::array< uint8_t, STUN_TRANSACTION_ID_SIZE > transaction_id
Definition stun.h:232
void add_nonce(const std::string &nonce)
Add NONCE attribute.
void add_xor_mapped_address(const StunMappedAddress &addr)
Add XOR-MAPPED-ADDRESS attribute.
StunMessageType type
Definition stun.h:231
const StunAttribute * find_attribute(StunAttributeType attr_type) const
Find attribute by type.
StunMessageClass get_class() const
Get message class from type.
std::optional< StunMappedAddress > get_mapped_address() const
Parse MAPPED-ADDRESS from attributes (legacy)
std::vector< uint8_t > serialize() const
Serialize message to bytes.
void generate_transaction_id()
Generate random transaction ID.
std::optional< std::string > get_nonce() const
Parse NONCE from attributes.
StunMessage(StunMessageType t)
Definition stun.h:239
void add_username(const std::string &username)
Add USERNAME attribute.
std::optional< StunMappedAddress > get_xor_mapped_address() const
Parse XOR-MAPPED-ADDRESS from attributes.
std::optional< StunMappedAddress > get_xor_relayed_address() const
Parse XOR-RELAYED-ADDRESS from attributes (TURN)
std::vector< uint8_t > serialize_with_integrity(const std::string &key) const
Serialize message with MESSAGE-INTEGRITY and FINGERPRINT.
bool is_request() const
Check if this is a request.
Definition stun.h:253
void add_software(const std::string &software)
Add SOFTWARE attribute.
bool is_success_response() const
Check if this is a success response.
Definition stun.h:256
STUN transaction result.
Definition stun.h:362
std::optional< StunError > error
Definition stun.h:365
std::optional< StunMappedAddress > mapped_address
Definition stun.h:364