blob: b103dba0446f87b3b0abd098a54984ee8f5924fd [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file turnc.c TURN Client implementation
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <re_types.h>
7#include <re_fmt.h>
8#include <re_mem.h>
9#include <re_mbuf.h>
10#include <re_md5.h>
11#include <re_list.h>
12#include <re_hash.h>
13#include <re_tmr.h>
14#include <re_sa.h>
15#include <re_udp.h>
16#include <re_tcp.h>
17#include <re_srtp.h>
18#include <re_tls.h>
19#include <re_stun.h>
20#include <re_turn.h>
21#include "turnc.h"
22
23
24#define DEBUG_MODULE "turnc"
25#define DEBUG_LEVEL 5
26#include <re_dbg.h>
27
28
29/** TURN Client protocol values */
30enum {
31 PERM_HASH_SIZE = 16,
32 CHAN_HASH_SIZE = 16,
33 FAILC_MAX = 16, /**< Maximum number of request errors for loopcheck. */
34 STUN_ATTR_ADDR4_SIZE = 8,
35 STUN_ATTR_ADDR6_SIZE = 20,
36};
37
38
39static const uint8_t sendind_tid[STUN_TID_SIZE];
40
41static int allocate_request(struct turnc *t);
42static int refresh_request(struct turnc *t, uint32_t lifetime, bool reset_ls,
43 stun_resp_h *resph, void *arg);
44static void refresh_resp_handler(int err, uint16_t scode, const char *reason,
45 const struct stun_msg *msg, void *arg);
46
47
48static void destructor(void *arg)
49{
50 struct turnc *turnc = arg;
51
52 if (turnc->allocated)
53 (void)refresh_request(turnc, 0, true, NULL, NULL);
54
55 tmr_cancel(&turnc->tmr);
56 mem_deref(turnc->ct);
57
58 hash_flush(turnc->perms);
59 mem_deref(turnc->perms);
60 mem_deref(turnc->chans);
61 mem_deref(turnc->username);
62 mem_deref(turnc->password);
63 mem_deref(turnc->nonce);
64 mem_deref(turnc->realm);
65 mem_deref(turnc->stun);
66 mem_deref(turnc->uh);
67 mem_deref(turnc->sock);
68}
69
70
71static void timeout(void *arg)
72{
73 struct turnc *turnc = arg;
74 int err;
75
76 err = refresh_request(turnc, turnc->lifetime, true,
77 refresh_resp_handler, turnc);
78 if (err)
79 turnc->th(err, 0, NULL, NULL, NULL, NULL, turnc->arg);
80}
81
82
83static void refresh_timer(struct turnc *turnc)
84{
85 const uint32_t t = turnc->lifetime*1000*3/4;
86
87 DEBUG_INFO("Start refresh timer.. %u seconds\n", t/1000);
88
89 tmr_start(&turnc->tmr, t, timeout, turnc);
90}
91
92
93static void allocate_resp_handler(int err, uint16_t scode, const char *reason,
94 const struct stun_msg *msg, void *arg)
95{
96 struct stun_attr *map = NULL, *rel = NULL, *ltm, *alt;
97 struct turnc *turnc = arg;
98
99 if (err || turnc_request_loops(&turnc->ls, scode))
100 goto out;
101
102 switch (scode) {
103
104 case 0:
105 map = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);
106 rel = stun_msg_attr(msg, STUN_ATTR_XOR_RELAY_ADDR);
107 ltm = stun_msg_attr(msg, STUN_ATTR_LIFETIME);
108 if (!rel || !map) {
109 DEBUG_WARNING("xor_mapped/relay addr attr missing\n");
110 err = EPROTO;
111 break;
112 }
113
114 if (ltm)
115 turnc->lifetime = ltm->v.lifetime;
116
117 turnc->allocated = true;
118 refresh_timer(turnc);
119 break;
120
121 case 300:
122 if (turnc->proto == IPPROTO_TCP ||
123 turnc->proto == STUN_TRANSP_DTLS)
124 break;
125
126 alt = stun_msg_attr(msg, STUN_ATTR_ALT_SERVER);
127 if (!alt)
128 break;
129
130 turnc->psrv = turnc->srv;
131 turnc->srv = alt->v.alt_server;
132
133 err = allocate_request(turnc);
134 if (err)
135 break;
136
137 return;
138
139 case 401:
140 case 438:
141 err = turnc_keygen(turnc, msg);
142 if (err)
143 break;
144
145 err = allocate_request(turnc);
146 if (err)
147 break;
148
149 return;
150
151 default:
152 break;
153 }
154
155 out:
156 turnc->th(err, scode, reason,
157 rel ? &rel->v.xor_relay_addr : NULL,
158 map ? &map->v.xor_mapped_addr : NULL,
159 msg,
160 turnc->arg);
161}
162
163
164static int allocate_request(struct turnc *t)
165{
166 const uint8_t proto = IPPROTO_UDP;
167
168 return stun_request(&t->ct, t->stun, t->proto, t->sock, &t->srv, 0,
169 STUN_METHOD_ALLOCATE,
170 t->realm ? t->md5_hash : NULL, sizeof(t->md5_hash),
171 false, allocate_resp_handler, t, 6,
172 STUN_ATTR_LIFETIME, &t->lifetime,
173 STUN_ATTR_REQ_TRANSPORT, &proto,
174 STUN_ATTR_USERNAME, t->realm ? t->username : NULL,
175 STUN_ATTR_REALM, t->realm,
176 STUN_ATTR_NONCE, t->nonce,
177 STUN_ATTR_SOFTWARE, stun_software);
178}
179
180
181static void refresh_resp_handler(int err, uint16_t scode, const char *reason,
182 const struct stun_msg *msg, void *arg)
183{
184 struct turnc *turnc = arg;
185 struct stun_attr *ltm;
186
187 if (err || turnc_request_loops(&turnc->ls, scode))
188 goto out;
189
190 switch (scode) {
191
192 case 0:
193 ltm = stun_msg_attr(msg, STUN_ATTR_LIFETIME);
194 if (ltm)
195 turnc->lifetime = ltm->v.lifetime;
196 refresh_timer(turnc);
197 return;
198
199 case 401:
200 case 438:
201 err = turnc_keygen(turnc, msg);
202 if (err)
203 break;
204
205 err = refresh_request(turnc, turnc->lifetime, false,
206 refresh_resp_handler, turnc);
207 if (err)
208 break;
209
210 return;
211
212 default:
213 break;
214 }
215
216 out:
217 turnc->th(err, scode, reason, NULL, NULL, msg, turnc->arg);
218}
219
220
221static int refresh_request(struct turnc *t, uint32_t lifetime, bool reset_ls,
222 stun_resp_h *resph, void *arg)
223{
224 if (!t)
225 return EINVAL;
226
227 if (reset_ls)
228 turnc_loopstate_reset(&t->ls);
229
230 if (t->ct)
231 t->ct = mem_deref(t->ct);
232
233 return stun_request(&t->ct, t->stun, t->proto, t->sock, &t->srv, 0,
234 STUN_METHOD_REFRESH,
235 t->realm ? t->md5_hash : NULL, sizeof(t->md5_hash),
236 false, resph, arg, 5,
237 STUN_ATTR_LIFETIME, &lifetime,
238 STUN_ATTR_USERNAME, t->realm ? t->username : NULL,
239 STUN_ATTR_REALM, t->realm,
240 STUN_ATTR_NONCE, t->nonce,
241 STUN_ATTR_SOFTWARE, stun_software);
242}
243
244
245static inline size_t stun_indlen(const struct sa *sa)
246{
247 size_t len = STUN_HEADER_SIZE + STUN_ATTR_HEADER_SIZE * 2;
248
249 switch (sa_af(sa)) {
250
251 case AF_INET:
252 len += STUN_ATTR_ADDR4_SIZE;
253 break;
254
255#ifdef HAVE_INET6
256 case AF_INET6:
257 len += STUN_ATTR_ADDR6_SIZE;
258 break;
259#endif
260 }
261
262 return len;
263}
264
265
266static bool udp_send_handler(int *err, struct sa *dst, struct mbuf *mb,
267 void *arg)
268{
269 struct turnc *turnc = arg;
270 size_t pos, indlen;
271 struct chan *chan;
272
273 if (mb->pos < CHAN_HDR_SIZE)
274 return false;
275
276 chan = turnc_chan_find_peer(turnc, dst);
277 if (chan) {
278 struct chan_hdr hdr;
279
280 hdr.nr = turnc_chan_numb(chan);
281 hdr.len = mbuf_get_left(mb);
282
283 mb->pos -= CHAN_HDR_SIZE;
284 *err = turnc_chan_hdr_encode(&hdr, mb);
285 mb->pos -= CHAN_HDR_SIZE;
286
287 *dst = turnc->srv;
288
289 return false;
290 }
291
292 indlen = stun_indlen(dst);
293
294 if (mb->pos < indlen)
295 return false;
296
297 mb->pos -= indlen;
298 pos = mb->pos;
299 *err = stun_msg_encode(mb, STUN_METHOD_SEND, STUN_CLASS_INDICATION,
300 sendind_tid, NULL, NULL, 0, false, 0x00, 2,
301 STUN_ATTR_XOR_PEER_ADDR, dst,
302 STUN_ATTR_DATA, mb);
303 mb->pos = pos;
304
305 *dst = turnc->srv;
306
307 return false;
308}
309
310
311static bool udp_recv_handler(struct sa *src, struct mbuf *mb, void *arg)
312{
313 struct stun_attr *peer, *data;
314 struct stun_unknown_attr ua;
315 struct turnc *turnc = arg;
316 struct stun_msg *msg;
317 bool hdld = true;
318
319 if (!sa_cmp(&turnc->srv, src, SA_ALL) &&
320 !sa_cmp(&turnc->psrv, src, SA_ALL))
321 return false;
322
323 if (stun_msg_decode(&msg, mb, &ua)) {
324
325 struct chan_hdr hdr;
326 struct chan *chan;
327
328 if (turnc_chan_hdr_decode(&hdr, mb))
329 return true;
330
331 if (mbuf_get_left(mb) < hdr.len)
332 return true;
333
334 chan = turnc_chan_find_numb(turnc, hdr.nr);
335 if (!chan)
336 return true;
337
338 *src = *turnc_chan_peer(chan);
339
340 return false;
341 }
342
343 switch (stun_msg_class(msg)) {
344
345 case STUN_CLASS_INDICATION:
346 if (ua.typec > 0)
347 break;
348
349 if (stun_msg_method(msg) != STUN_METHOD_DATA)
350 break;
351
352 peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR);
353 data = stun_msg_attr(msg, STUN_ATTR_DATA);
354 if (!peer || !data)
355 break;
356
357 *src = peer->v.xor_peer_addr;
358
359 mb->pos = data->v.data.pos;
360 mb->end = data->v.data.end;
361
362 hdld = false;
363 break;
364
365 case STUN_CLASS_ERROR_RESP:
366 case STUN_CLASS_SUCCESS_RESP:
367 (void)stun_ctrans_recv(turnc->stun, msg, &ua);
368 break;
369
370 default:
371 break;
372 }
373
374 mem_deref(msg);
375
376 return hdld;
377}
378
379
380/**
381 * Allocate a TURN Client
382 *
383 * @param turncp Pointer to allocated TURN Client
384 * @param conf Optional STUN Configuration
385 * @param proto Transport Protocol
386 * @param sock Transport socket
387 * @param layer Transport layer
388 * @param srv TURN Server IP-address
389 * @param username Authentication username
390 * @param password Authentication password
391 * @param lifetime Allocate lifetime in [seconds]
392 * @param th TURN handler
393 * @param arg Handler argument
394 *
395 * @return 0 if success, otherwise errorcode
396 */
397int turnc_alloc(struct turnc **turncp, const struct stun_conf *conf, int proto,
398 void *sock, int layer, const struct sa *srv,
399 const char *username, const char *password,
400 uint32_t lifetime, turnc_h *th, void *arg)
401{
402 struct turnc *turnc;
403 int err;
404
405 if (!turncp || !sock || !srv || !username || !password || !th)
406 return EINVAL;
407
408 turnc = mem_zalloc(sizeof(*turnc), destructor);
409 if (!turnc)
410 return ENOMEM;
411
412 err = stun_alloc(&turnc->stun, conf, NULL, NULL);
413 if (err)
414 goto out;
415
416 err = str_dup(&turnc->username, username);
417 if (err)
418 goto out;
419
420 err = str_dup(&turnc->password, password);
421 if (err)
422 goto out;
423
424 err = turnc_perm_hash_alloc(&turnc->perms, PERM_HASH_SIZE);
425 if (err)
426 goto out;
427
428 err = turnc_chan_hash_alloc(&turnc->chans, CHAN_HASH_SIZE);
429 if (err)
430 goto out;
431
432 tmr_init(&turnc->tmr);
433 turnc->proto = proto;
434 turnc->sock = mem_ref(sock);
435 turnc->psrv = *srv;
436 turnc->srv = *srv;
437 turnc->lifetime = lifetime;
438 turnc->th = th;
439 turnc->arg = arg;
440
441 switch (proto) {
442
443 case IPPROTO_UDP:
444 err = udp_register_helper(&turnc->uh, sock, layer,
445 udp_send_handler, udp_recv_handler,
446 turnc);
447 break;
448
449 default:
450 err = 0;
451 break;
452 }
453
454 if (err)
455 goto out;
456
457 err = allocate_request(turnc);
458 if (err)
459 goto out;
460
461 out:
462 if (err)
463 mem_deref(turnc);
464 else
465 *turncp = turnc;
466
467 return err;
468}
469
470
471int turnc_send(struct turnc *turnc, const struct sa *dst, struct mbuf *mb)
472{
473 size_t pos, indlen;
474 struct chan *chan;
475 int err;
476
477 if (!turnc || !dst || !mb)
478 return EINVAL;
479
480 chan = turnc_chan_find_peer(turnc, dst);
481 if (chan) {
482 struct chan_hdr hdr;
483
484 if (mb->pos < CHAN_HDR_SIZE)
485 return EINVAL;
486
487 hdr.nr = turnc_chan_numb(chan);
488 hdr.len = mbuf_get_left(mb);
489
490 mb->pos -= CHAN_HDR_SIZE;
491 pos = mb->pos;
492
493 err = turnc_chan_hdr_encode(&hdr, mb);
494 if (err)
495 return err;
496
497 if (turnc->proto == IPPROTO_TCP) {
498
499 mb->pos = mb->end;
500
501 /* padding */
502 while (hdr.len++ & 0x03) {
503 err = mbuf_write_u8(mb, 0x00);
504 if (err)
505 return err;
506 }
507 }
508
509 mb->pos = pos;
510 }
511 else {
512 indlen = stun_indlen(dst);
513
514 if (mb->pos < indlen)
515 return EINVAL;
516
517 mb->pos -= indlen;
518 pos = mb->pos;
519
520 err = stun_msg_encode(mb, STUN_METHOD_SEND,
521 STUN_CLASS_INDICATION, sendind_tid,
522 NULL, NULL, 0, false, 0x00, 2,
523 STUN_ATTR_XOR_PEER_ADDR, dst,
524 STUN_ATTR_DATA, mb);
525 if (err)
526 return err;
527
528 mb->pos = pos;
529 }
530
531 switch (turnc->proto) {
532
533 case IPPROTO_UDP:
534 err = udp_send(turnc->sock, &turnc->srv, mb);
535 break;
536
537 case IPPROTO_TCP:
538 err = tcp_send(turnc->sock, mb);
539 break;
540
541#ifdef USE_DTLS
542 case STUN_TRANSP_DTLS:
543 err = dtls_send(turnc->sock, mb);
544 break;
545#endif
546
547 default:
548 err = EPROTONOSUPPORT;
549 break;
550 }
551
552 return err;
553}
554
555
556int turnc_recv(struct turnc *turnc, struct sa *src, struct mbuf *mb)
557{
558 struct stun_attr *peer, *data;
559 struct stun_unknown_attr ua;
560 struct stun_msg *msg;
561 int err = 0;
562
563 if (!turnc || !src || !mb)
564 return EINVAL;
565
566 if (stun_msg_decode(&msg, mb, &ua)) {
567
568 struct chan_hdr hdr;
569 struct chan *chan;
570
571 if (turnc_chan_hdr_decode(&hdr, mb))
572 return EBADMSG;
573
574 if (mbuf_get_left(mb) < hdr.len)
575 return EBADMSG;
576
577 chan = turnc_chan_find_numb(turnc, hdr.nr);
578 if (!chan)
579 return EBADMSG;
580
581 *src = *turnc_chan_peer(chan);
582
583 return 0;
584 }
585
586 switch (stun_msg_class(msg)) {
587
588 case STUN_CLASS_INDICATION:
589 if (ua.typec > 0) {
590 err = ENOSYS;
591 break;
592 }
593
594 if (stun_msg_method(msg) != STUN_METHOD_DATA) {
595 err = ENOSYS;
596 break;
597 }
598
599 peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR);
600 data = stun_msg_attr(msg, STUN_ATTR_DATA);
601 if (!peer || !data) {
602 err = EPROTO;
603 break;
604 }
605
606 *src = peer->v.xor_peer_addr;
607
608 mb->pos = data->v.data.pos;
609 mb->end = data->v.data.end;
610 break;
611
612 case STUN_CLASS_ERROR_RESP:
613 case STUN_CLASS_SUCCESS_RESP:
614 (void)stun_ctrans_recv(turnc->stun, msg, &ua);
615 mb->pos = mb->end;
616 break;
617
618 default:
619 err = ENOSYS;
620 break;
621 }
622
623 mem_deref(msg);
624
625 return err;
626}
627
628
629bool turnc_request_loops(struct loop_state *ls, uint16_t scode)
630{
631 bool loop = false;
632
633 switch (scode) {
634
635 case 0:
636 ls->failc = 0;
637 break;
638
639 default:
640 if (ls->last_scode == scode)
641 loop = true;
642 /*@fallthrough@*/
643 case 300:
644 if (++ls->failc >= FAILC_MAX)
645 loop = true;
646
647 break;
648 }
649
650 ls->last_scode = scode;
651
652 return loop;
653}
654
655
656void turnc_loopstate_reset(struct loop_state *ls)
657{
658 if (!ls)
659 return;
660
661 ls->last_scode = 0;
662 ls->failc = 0;
663}
664
665
666int turnc_keygen(struct turnc *turnc, const struct stun_msg *msg)
667{
668 struct stun_attr *realm, *nonce;
669
670 realm = stun_msg_attr(msg, STUN_ATTR_REALM);
671 nonce = stun_msg_attr(msg, STUN_ATTR_NONCE);
672 if (!realm || !nonce)
673 return EPROTO;
674
675 mem_deref(turnc->realm);
676 mem_deref(turnc->nonce);
677 turnc->realm = mem_ref(realm->v.realm);
678 turnc->nonce = mem_ref(nonce->v.nonce);
679
680 return md5_printf(turnc->md5_hash, "%s:%s:%s",
681 turnc->username, turnc->realm, turnc->password);
682}