1 | #include "stun.h" |
2 | |
3 | #include <base/system.h> |
4 | |
5 | // STUN header (from RFC 5389, section 6, figure 2): |
6 | // |
7 | // 0 1 2 3 |
8 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
9 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
10 | // |0 0| STUN Message Type | Message Length | |
11 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
12 | // | Magic Cookie | |
13 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
14 | // | | |
15 | // | Transaction ID (96 bits) | |
16 | // | | |
17 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
18 | |
19 | size_t StunMessagePrepare(unsigned char *pBuffer, size_t BufferSize, CStunData *pData) |
20 | { |
21 | dbg_assert(BufferSize >= 20, "stun message buffer too small" ); |
22 | secure_random_fill(bytes: pData->m_aSecret, length: sizeof(pData->m_aSecret)); |
23 | pBuffer[0] = 0x00; // STUN Request Message Type 1: Binding |
24 | pBuffer[1] = 0x01; |
25 | pBuffer[2] = 0x00; // Message Length: 0 (extra bytes after the header) |
26 | pBuffer[3] = 0x00; |
27 | pBuffer[4] = 0x21; // Magic Cookie: 0x2112A442 |
28 | pBuffer[5] = 0x12; |
29 | pBuffer[6] = 0xA4; |
30 | pBuffer[7] = 0x42; |
31 | mem_copy(dest: pBuffer + 8, source: pData->m_aSecret, size: sizeof(pData->m_aSecret)); // Transaction ID |
32 | return 20; |
33 | } |
34 | |
35 | bool StunMessageParse(const unsigned char *pMessage, size_t MessageSize, const CStunData *pData, bool *pSuccess, NETADDR *pAddr) |
36 | { |
37 | *pSuccess = false; |
38 | mem_zero(block: pAddr, size: sizeof(*pAddr)); |
39 | if(MessageSize < 20) |
40 | { |
41 | return true; |
42 | } |
43 | bool Parsed = true; |
44 | // STUN Success/Error Response Type 1: Binding |
45 | Parsed = Parsed && pMessage[0] == 0x01 && (pMessage[1] == 0x01 || pMessage[1] == 0x11); |
46 | uint16_t MessageLength = (pMessage[2] << 8) | pMessage[3]; |
47 | Parsed = Parsed && MessageSize >= 20 + (size_t)MessageLength && MessageLength % 4 == 0; |
48 | // Magic Cookie: 0x2112A442 |
49 | Parsed = Parsed && pMessage[4] == 0x21 && pMessage[5] == 0x12; |
50 | Parsed = Parsed && pMessage[6] == 0xA4 && pMessage[7] == 0x42; |
51 | // Transaction ID |
52 | Parsed = Parsed && mem_comp(a: pMessage + 8, b: pData->m_aSecret, size: sizeof(pData->m_aSecret)) == 0; |
53 | if(!Parsed) |
54 | { |
55 | return true; |
56 | } |
57 | |
58 | *pSuccess = pMessage[1] == 0x01; |
59 | |
60 | MessageSize = 20 + MessageLength; |
61 | size_t Offset = 20; |
62 | bool FoundAddr = false; |
63 | while(true) |
64 | { |
65 | // STUN attribute format (from RFC 5389, section 15, figure 4): |
66 | // |
67 | // 0 1 2 3 |
68 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
69 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
70 | // | Type | Length | |
71 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
72 | // | Value (variable) .... |
73 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
74 | if(MessageSize == Offset) |
75 | { |
76 | break; |
77 | } |
78 | else if(MessageSize < Offset + 4) |
79 | { |
80 | return true; |
81 | } |
82 | uint16_t Type = (pMessage[Offset] << 8) | pMessage[Offset + 1]; |
83 | uint16_t Length = (pMessage[Offset + 2] << 8) | pMessage[Offset + 3]; |
84 | if(MessageSize < Offset + 4 + Length) |
85 | { |
86 | return true; |
87 | } |
88 | if(*pSuccess && Type == 0x0020) // XOR-MAPPED-ADDRESS |
89 | { |
90 | // STUN XOR-MAPPED-ADDRESS attribute format (from RFC 5389, section 15, |
91 | // figure 6): |
92 | // |
93 | // 0 1 2 3 |
94 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
95 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
96 | // |x x x x x x x x| Family | X-Port | |
97 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
98 | // | X-Address (Variable) |
99 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
100 | |
101 | if(Length < 4) |
102 | { |
103 | return true; |
104 | } |
105 | // Only use the first found address. |
106 | uint8_t Family = pMessage[Offset + 4 + 1]; |
107 | uint16_t Port = (pMessage[Offset + 4 + 2] << 8) | pMessage[Offset + 4 + 3]; |
108 | Port ^= 0x2112; |
109 | if(Family == 0x01) // IPv4 |
110 | { |
111 | if(Length != 8) |
112 | { |
113 | return true; |
114 | } |
115 | if(!FoundAddr) |
116 | { |
117 | pAddr->type = NETTYPE_IPV4; |
118 | mem_copy(dest: pAddr->ip, source: pMessage + Offset + 4 + 4, size: 4); |
119 | pAddr->ip[0] ^= 0x21; |
120 | pAddr->ip[1] ^= 0x12; |
121 | pAddr->ip[2] ^= 0xA4; |
122 | pAddr->ip[3] ^= 0x42; |
123 | pAddr->port = Port; |
124 | FoundAddr = true; |
125 | } |
126 | } |
127 | else if(Family == 0x02) // IPv6 |
128 | { |
129 | if(Length != 20) |
130 | { |
131 | return true; |
132 | } |
133 | if(!FoundAddr) |
134 | { |
135 | pAddr->type = NETTYPE_IPV6; |
136 | mem_copy(dest: pAddr->ip, source: pMessage + Offset + 4 + 4, size: 16); |
137 | pAddr->ip[0] ^= 0x21; |
138 | pAddr->ip[1] ^= 0x12; |
139 | pAddr->ip[2] ^= 0xA4; |
140 | pAddr->ip[3] ^= 0x42; |
141 | for(size_t i = 0; i < sizeof(pData->m_aSecret); i++) |
142 | { |
143 | pAddr->ip[4 + i] ^= pData->m_aSecret[i]; |
144 | } |
145 | pAddr->port = Port; |
146 | FoundAddr = true; |
147 | } |
148 | } |
149 | } |
150 | // comprehension-required |
151 | else if(Type <= 0x7fff) |
152 | { |
153 | return true; |
154 | } |
155 | Offset += 4 + Length; |
156 | } |
157 | return *pSuccess && !FoundAddr; |
158 | } |
159 | |