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
19size_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
35bool 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