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