blob: dda3d8d7f35bff749f1e18fc67e5eda4a4293c82 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file stun/msg.c STUN message encoding
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <string.h>
7#include <re_types.h>
8#include <re_mem.h>
9#include <re_mbuf.h>
10#include <re_sa.h>
11#include <re_list.h>
12#include <re_fmt.h>
13#include <re_md5.h>
14#include <re_sha.h>
15#include <re_hmac.h>
16#include <re_crc32.h>
17#include <re_stun.h>
18#include "stun.h"
19
20
21enum {
22 MI_SIZE = 24,
23 FP_SIZE = 8
24};
25
26
27/**
28 Defines a STUN Message object
29
30 <pre>
31
32 .---------------------. /|\ /|\
33 | STUN Header | | |
34 |---------------------| | |
35 | .... | |--------. |
36 | N Attributes | | | |----.
37 | .... | \|/ | | |
38 |---------------------| | | |
39 | MESSAGE-INTEGRITY | <-(HMAC-SHA1)--' \|/ |
40 |---------------------| |
41 | FINGERPRINT | <-(CRC-32)---------------'
42 '---------------------'
43 </pre>
44*/
45struct stun_msg {
46 struct stun_hdr hdr;
47 struct list attrl;
48 struct mbuf *mb;
49 size_t start;
50};
51
52
53static uint32_t fingerprint(const uint8_t *buf, size_t len)
54{
55 return (uint32_t)crc32(0, buf, (unsigned int)len) ^ 0x5354554e;
56}
57
58
59static void destructor(void *arg)
60{
61 struct stun_msg *msg = arg;
62
63 list_flush(&msg->attrl);
64 mem_deref(msg->mb);
65}
66
67
68/**
69 * Decode a buffer to a STUN Message
70 *
71 * @param msgpp Pointer to allocation STUN message
72 * @param mb Buffer containing the raw STUN packet
73 * @param ua Unknown attributes (optional)
74 *
75 * @return 0 if success, otherwise errorcode
76 *
77 * @note `mb' will be referenced
78 */
79int stun_msg_decode(struct stun_msg **msgpp, struct mbuf *mb,
80 struct stun_unknown_attr *ua)
81{
82 struct stun_msg *msg;
83 struct stun_hdr hdr;
84 size_t start, extra;
85 int err;
86
87 if (!msgpp || !mb)
88 return EINVAL;
89
90 start = mb->pos;
91
92 err = stun_hdr_decode(mb, &hdr);
93 if (err) {
94 mb->pos = start;
95 return err;
96 }
97
98 msg = mem_zalloc(sizeof(*msg), destructor);
99 if (!msg) {
100 mb->pos = start;
101 return ENOMEM;
102 }
103
104 msg->hdr = hdr;
105 msg->mb = mem_ref(mb);
106 msg->start = start;
107
108 if (ua)
109 ua->typec = 0;
110
111 /* mbuf_get_left(mb) >= hdr.len checked in stun_hdr_decode() above */
112 extra = mbuf_get_left(mb) - hdr.len;
113
114 while (mbuf_get_left(mb) - extra >= 4) {
115
116 struct stun_attr *attr;
117
118 err = stun_attr_decode(&attr, mb, hdr.tid, ua);
119 if (err)
120 break;
121
122 list_append(&msg->attrl, &attr->le, attr);
123 }
124
125 if (err)
126 mem_deref(msg);
127 else
128 *msgpp = msg;
129
130 mb->pos = start;
131
132 return err;
133}
134
135
136/**
137 * Get the STUN message type
138 *
139 * @param msg STUN Message
140 *
141 * @return STUN Message type
142 */
143uint16_t stun_msg_type(const struct stun_msg *msg)
144{
145 return msg ? msg->hdr.type : 0;
146}
147
148
149/**
150 * Get the STUN message class
151 *
152 * @param msg STUN Message
153 *
154 * @return STUN Message class
155 */
156uint16_t stun_msg_class(const struct stun_msg *msg)
157{
158 return STUN_CLASS(stun_msg_type(msg));
159}
160
161
162/**
163 * Get the STUN message method
164 *
165 * @param msg STUN Message
166 *
167 * @return STUN Message method
168 */
169uint16_t stun_msg_method(const struct stun_msg *msg)
170{
171 return STUN_METHOD(stun_msg_type(msg));
172}
173
174
175/**
176 * Get the STUN message Transaction-ID
177 *
178 * @param msg STUN Message
179 *
180 * @return STUN Message Transaction-ID
181 */
182const uint8_t *stun_msg_tid(const struct stun_msg *msg)
183{
184 return msg ? msg->hdr.tid : NULL;
185}
186
187
188/**
189 * Check if a STUN Message has the magic cookie
190 *
191 * @param msg STUN Message
192 *
193 * @return true if Magic Cookie, otherwise false
194 */
195bool stun_msg_mcookie(const struct stun_msg *msg)
196{
197 return msg && (STUN_MAGIC_COOKIE == msg->hdr.cookie);
198}
199
200
201/**
202 * Lookup a STUN attribute in a STUN message
203 *
204 * @param msg STUN Message
205 * @param type STUN Attribute type
206 *
207 * @return STUN Attribute if found, otherwise NULL
208 */
209struct stun_attr *stun_msg_attr(const struct stun_msg *msg, uint16_t type)
210{
211 struct le *le = msg ? list_head(&msg->attrl) : NULL;
212
213 while (le) {
214 struct stun_attr *attr = le->data;
215
216 le = le->next;
217
218 if (attr->type == type)
219 return attr;
220 }
221
222 return NULL;
223}
224
225
226/**
227 * Apply a function handler to all STUN attribute
228 *
229 * @param msg STUN Message
230 * @param h Attribute handler
231 * @param arg Handler argument
232 *
233 * @return STUN attribute if handler returned true, otherwise NULL
234 */
235struct stun_attr *stun_msg_attr_apply(const struct stun_msg *msg,
236 stun_attr_h *h, void *arg)
237{
238 struct le *le = msg ? list_head(&msg->attrl) : NULL;
239
240 while (le) {
241 struct stun_attr *attr = le->data;
242
243 le = le->next;
244
245 if (h && h(attr, arg))
246 return (attr);
247 }
248
249 return NULL;
250}
251
252
253/**
254 * Encode a STUN message
255 *
256 * @param mb Buffer to encode message into
257 * @param method STUN Method
258 * @param class STUN Method class
259 * @param tid Transaction ID
260 * @param ec STUN error code (optional)
261 * @param key Authentication key (optional)
262 * @param keylen Number of bytes in authentication key
263 * @param fp Use STUN Fingerprint attribute
264 * @param padding Padding byte
265 * @param attrc Number of attributes to encode (variable arguments)
266 * @param ap Variable list of attribute-tuples
267 * Each attribute has 2 arguments, attribute type and value
268 *
269 * @return 0 if success, otherwise errorcode
270 */
271int stun_msg_vencode(struct mbuf *mb, uint16_t method, uint8_t class,
272 const uint8_t *tid, const struct stun_errcode *ec,
273 const uint8_t *key, size_t keylen, bool fp,
274 uint8_t padding, uint32_t attrc, va_list ap)
275{
276 struct stun_hdr hdr;
277 size_t start;
278 int err = 0;
279 uint32_t i;
280
281 if (!mb || !tid)
282 return EINVAL;
283
284 start = mb->pos;
285 mb->pos += STUN_HEADER_SIZE;
286
287 hdr.type = STUN_TYPE(method, class);
288 hdr.cookie = STUN_MAGIC_COOKIE;
289 memcpy(hdr.tid, tid, STUN_TID_SIZE);
290
291 if (ec)
292 err |= stun_attr_encode(mb, STUN_ATTR_ERR_CODE, ec,
293 NULL, padding);
294
295 for (i=0; i<attrc; i++) {
296
297 uint16_t type = va_arg(ap, int);
298 const void *v = va_arg(ap, const void *);
299
300 if (!v)
301 continue;
302
303 err |= stun_attr_encode(mb, type, v, hdr.tid, padding);
304 }
305
306 /* header */
307 hdr.len = mb->pos - start - STUN_HEADER_SIZE + (key ? MI_SIZE : 0);
308 mb->pos = start;
309 err |= stun_hdr_encode(mb, &hdr);
310 mb->pos += hdr.len - (key ? MI_SIZE : 0);
311
312 if (key) {
313 uint8_t mi[20];
314
315 mb->pos = start;
316 hmac_sha1(key, keylen, mbuf_buf(mb), mbuf_get_left(mb),
317 mi, sizeof(mi));
318
319 mb->pos += STUN_HEADER_SIZE + hdr.len - MI_SIZE;
320 err |= stun_attr_encode(mb, STUN_ATTR_MSG_INTEGRITY, mi,
321 NULL, padding);
322 }
323
324 if (fp) {
325 uint32_t fprnt;
326
327 /* header */
328 hdr.len = mb->pos - start - STUN_HEADER_SIZE + FP_SIZE;
329 mb->pos = start;
330 err |= stun_hdr_encode(mb, &hdr);
331
332 mb->pos = start;
333 fprnt = fingerprint(mbuf_buf(mb), mbuf_get_left(mb));
334
335 mb->pos += STUN_HEADER_SIZE + hdr.len - FP_SIZE;
336 err |= stun_attr_encode(mb, STUN_ATTR_FINGERPRINT, &fprnt,
337 NULL, padding);
338 }
339
340 return err;
341}
342
343
344/**
345 * Encode a STUN message
346 *
347 * @param mb Buffer to encode message into
348 * @param method STUN Method
349 * @param class STUN Method class
350 * @param tid Transaction ID
351 * @param ec STUN error code (optional)
352 * @param key Authentication key (optional)
353 * @param keylen Number of bytes in authentication key
354 * @param fp Use STUN Fingerprint attribute
355 * @param padding Padding byte
356 * @param attrc Number of attributes to encode (variable arguments)
357 * @param ... Variable list of attribute-tuples
358 * Each attribute has 2 arguments, attribute type and value
359 *
360 * @return 0 if success, otherwise errorcode
361 */
362int stun_msg_encode(struct mbuf *mb, uint16_t method, uint8_t class,
363 const uint8_t *tid, const struct stun_errcode *ec,
364 const uint8_t *key, size_t keylen, bool fp,
365 uint8_t padding, uint32_t attrc, ...)
366{
367 va_list ap;
368 int err;
369
370 va_start(ap, attrc);
371 err = stun_msg_vencode(mb, method, class, tid, ec, key, keylen, fp,
372 padding, attrc, ap);
373 va_end(ap);
374
375 return err;
376}
377
378
379/**
380 * Verify the Message-Integrity of a STUN message
381 *
382 * @param msg STUN Message
383 * @param key Authentication key
384 * @param keylen Number of bytes in authentication key
385 *
386 * @return 0 if verified, otherwise errorcode
387 */
388int stun_msg_chk_mi(const struct stun_msg *msg, const uint8_t *key,
389 size_t keylen)
390{
391 uint8_t hmac[SHA_DIGEST_LENGTH];
392 struct stun_attr *mi, *fp;
393
394 if (!msg)
395 return EINVAL;
396
397 mi = stun_msg_attr(msg, STUN_ATTR_MSG_INTEGRITY);
398 if (!mi)
399 return EPROTO;
400
401 msg->mb->pos = msg->start;
402
403 fp = stun_msg_attr(msg, STUN_ATTR_FINGERPRINT);
404 if (fp) {
405 ((struct stun_msg *)msg)->hdr.len -= FP_SIZE;
406 (void)stun_hdr_encode(msg->mb, &msg->hdr);
407 msg->mb->pos -= STUN_HEADER_SIZE;
408 }
409
410 hmac_sha1(key, keylen, mbuf_buf(msg->mb),
411 STUN_HEADER_SIZE + msg->hdr.len - MI_SIZE,
412 hmac, sizeof(hmac));
413
414 if (fp) {
415 ((struct stun_msg *)msg)->hdr.len += FP_SIZE;
416 (void)stun_hdr_encode(msg->mb, &msg->hdr);
417 msg->mb->pos -= STUN_HEADER_SIZE;
418 }
419
420 if (memcmp(mi->v.msg_integrity, hmac, SHA_DIGEST_LENGTH))
421 return EBADMSG;
422
423 return 0;
424}
425
426
427/**
428 * Check the Fingerprint of a STUN message
429 *
430 * @param msg STUN Message
431 *
432 * @return 0 if fingerprint matches, otherwise errorcode
433 */
434int stun_msg_chk_fingerprint(const struct stun_msg *msg)
435{
436 struct stun_attr *fp;
437 uint32_t fprnt;
438
439 if (!msg)
440 return EINVAL;
441
442 fp = stun_msg_attr(msg, STUN_ATTR_FINGERPRINT);
443 if (!fp)
444 return EPROTO;
445
446 msg->mb->pos = msg->start;
447
448 fprnt = fingerprint(mbuf_buf(msg->mb),
449 STUN_HEADER_SIZE + msg->hdr.len - FP_SIZE);
450
451 if (fprnt != fp->v.fingerprint)
452 return EBADMSG;
453
454 return 0;
455}
456
457
458static bool attr_print(const struct stun_attr *attr, void *arg)
459{
460 (void)arg;
461
462 stun_attr_dump(attr);
463
464 return false;
465}
466
467
468/**
469 * Print a STUN message to STDOUT
470 *
471 * @param msg STUN Message
472 */
473void stun_msg_dump(const struct stun_msg *msg)
474{
475 if (!msg)
476 return;
477
478 (void)re_printf("%s %s (len=%u cookie=%08x tid=%w)\n",
479 stun_method_name(stun_msg_method(msg)),
480 stun_class_name(stun_msg_class(msg)),
481 msg->hdr.len, msg->hdr.cookie,
482 msg->hdr.tid, sizeof(msg->hdr.tid));
483
484 stun_msg_attr_apply(msg, attr_print, NULL);
485}