blob: 780226de7812f4738df7ba8e2596dcab02bc36b6 [file] [log] [blame]
James Kuszmaul871d0712021-01-17 11:30:43 -08001/**
2 * @file icem.c ICE Media stream
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_list.h>
11#include <re_tmr.h>
12#include <re_sa.h>
13#include <re_stun.h>
14#include <re_ice.h>
15#include <re_sys.h>
16#include <re_trice.h>
17#include "trice.h"
18
19
20#define DEBUG_MODULE "icem"
21#define DEBUG_LEVEL 5
22#include <re_dbg.h>
23
24
25static const struct trice_conf conf_default = {
26 ICE_NOMINATION_REGULAR,
27 false,
28 false,
29 false,
30 true,
31 false
32};
33
34
35static void trice_destructor(void *data)
36{
37 struct trice *icem = data;
38
39 mem_deref(icem->checklist);
40
41 list_flush(&icem->validl);
42 list_flush(&icem->checkl);
43 list_flush(&icem->lcandl);
44 list_flush(&icem->rcandl);
45 list_flush(&icem->reqbufl);
46
47 list_flush(&icem->connl);
48
49 mem_deref(icem->rufrag);
50 mem_deref(icem->rpwd);
51 mem_deref(icem->lufrag);
52 mem_deref(icem->lpwd);
53 mem_deref(icem->sw);
54}
55
56
57/**
58 * Allocate a new ICE Media object
59 *
60 * @param icemp Pointer to allocated ICE Media object
61 * @param conf ICE configuration
62 * @param role Local role
63 * @param lufrag Local username fragment
64 * @param lpwd Local password
65 * @param estabh Candidate pair established handler
66 * @param closeh Close / error handler
67 * @param arg Handler argument
68 *
69 * @return 0 if success, otherwise errorcode
70 */
71
72int trice_alloc(struct trice **icemp, const struct trice_conf *conf,
73 enum ice_role role,
74 const char *lufrag, const char *lpwd)
75{
76 struct trice *icem;
77 int err = 0;
78
79 if (!icemp || !lufrag || !lpwd)
80 return EINVAL;
81
82 if (str_len(lufrag) < 4 || str_len(lpwd) < 22) {
83 DEBUG_WARNING("alloc: lufrag/lpwd is too short\n");
84 return EINVAL;
85 }
86
87 icem = mem_zalloc(sizeof(*icem), trice_destructor);
88 if (!icem)
89 return ENOMEM;
90
91 icem->conf = conf ? *conf : conf_default;
92 list_init(&icem->reqbufl);
93 list_init(&icem->lcandl);
94 list_init(&icem->rcandl);
95 list_init(&icem->checkl);
96 list_init(&icem->validl);
97
98 icem->lrole = role;
99 icem->tiebrk = rand_u64();
100
101 err |= str_dup(&icem->lufrag, lufrag);
102 err |= str_dup(&icem->lpwd, lpwd);
103 if (err)
104 goto out;
105
106 out:
107 if (err)
108 mem_deref(icem);
109 else
110 *icemp = icem;
111
112 return err;
113}
114
115
116int trice_set_remote_ufrag(struct trice *icem, const char *rufrag)
117{
118 if (!icem || !rufrag)
119 return EINVAL;
120
121 icem->rufrag = mem_deref(icem->rufrag);
122 return str_dup(&icem->rufrag, rufrag);
123}
124
125
126int trice_set_remote_pwd(struct trice *icem, const char *rpwd)
127{
128 if (!icem || !rpwd)
129 return EINVAL;
130
131 icem->rpwd = mem_deref(icem->rpwd);
132
133 return str_dup(&icem->rpwd, rpwd);
134}
135
136
137int trice_set_software(struct trice *icem, const char *sw)
138{
139 if (!icem)
140 return EINVAL;
141
142 icem->sw = mem_deref(icem->sw);
143
144 if (sw)
145 return str_dup(&icem->sw, sw);
146
147 return 0;
148}
149
150
151struct trice_conf *trice_conf(struct trice *icem)
152{
153 return icem ? &icem->conf : NULL;
154}
155
156
157/* note: call this ONCE AFTER role has been set */
158static void trice_create_candpairs(struct trice *icem)
159{
160 struct list *lst;
161 struct le *le;
162 bool refresh_checklist = false;
163 int err;
164
165 lst = &icem->lcandl;
166 for (le = list_head(lst); le; le = le->next) {
167 struct ice_lcand *lcand = le->data;
168
169 /* pair this local-candidate with all existing
170 * remote-candidates */
171 err = trice_candpair_with_local(icem, lcand);
172 if (err) {
173 DEBUG_WARNING("trice_candpair_with_local: %m\n", err);
174 }
175 else {
176 refresh_checklist = true;
177 }
178 }
179
180 lst = &icem->rcandl;
181 for (le = list_head(lst); le; le = le->next) {
182 struct ice_rcand *rcand = le->data;
183
184 /* pair this remote-candidate with all existing
185 * local-candidates */
186 err = trice_candpair_with_remote(icem, rcand);
187 if (err) {
188 DEBUG_WARNING("trice_candpair_with_remote: %m\n", err);
189 }
190 else {
191 refresh_checklist = true;
192 }
193 }
194
195 /* new pair -- refresh the checklist timer */
196 if (refresh_checklist)
197 trice_checklist_refresh(icem);
198}
199
200
201/* note: call this AFTER role has been set AND candidate pairs
202 * have been created */
203static void trice_reqbuf_process(struct trice *icem)
204{
205 struct le *le;
206
207 le = list_head(&icem->reqbufl);
208 while (le) {
209 struct trice_reqbuf *reqbuf = le->data;
210 le = le->next;
211
212 DEBUG_PRINTF("trice_reqbuf_process: Processing buffered "
213 "request\n");
214
215 (void)trice_stund_recv_role_set(icem, reqbuf->lcand,
216 reqbuf->sock, &reqbuf->src, reqbuf->req,
217 reqbuf->presz);
218
219 mem_deref(reqbuf);
220 }
221}
222
223
224/**
225 * Set the local role to either CONTROLLING or CONTROLLED.
226 * Note: The role can be set multiple times.
227 *
228 * @param icem ICE Media object
229 * @param role New local role
230 *
231 * @return 0 if success, otherwise errorcode
232 */
233int trice_set_role(struct trice *trice, enum ice_role role)
234{
235 bool refresh;
236
237 if (!trice)
238 return EINVAL;
239
240 /* Cannot change the role to unknown */
241 if (role == ICE_ROLE_UNKNOWN)
242 return EINVAL;
243
244 if (trice->lrole == role)
245 return 0;
246
247 /* Cannot switch role manually once it has been set */
248 if (trice->lrole == ICE_ROLE_UNKNOWN)
249 refresh = false;
250 else
251 refresh = true;
252
253 trice->lrole = role;
254
255 /* Create candidate pairs and process pending requests */
256 if (refresh) {
257 trice_candpair_prio_order(&trice->checkl,
258 role == ICE_ROLE_CONTROLLING);
259 }
260 else {
261 trice_create_candpairs(trice);
262 }
263
264 trice_reqbuf_process(trice);
265
266 return 0;
267}
268
269
270/**
271 * Get the local role
272 *
273 * @param icem ICE Media object
274 *
275 * @return Local role
276 */
277enum ice_role trice_local_role(const struct trice *icem)
278{
279 if (!icem)
280 return ICE_ROLE_UNKNOWN;
281
282 return icem->lrole;
283}
284
285
286/**
287 * Print debug information for the ICE Media
288 *
289 * @param pf Print function for debug output
290 * @param icem ICE Media object
291 *
292 * @return 0 if success, otherwise errorcode
293 */
294int trice_debug(struct re_printf *pf, const struct trice *icem)
295{
296 struct le *le;
297 int err = 0;
298
299 if (!icem)
300 return 0;
301
302 err |= re_hprintf(pf, "----- ICE Media <%p> -----\n", icem);
303
304 err |= re_hprintf(pf, " local_role=%s\n",
305 ice_role2name(icem->lrole));
306 err |= re_hprintf(pf, " local_ufrag=\"%s\" local_pwd=\"%s\"\n",
307 icem->lufrag, icem->lpwd);
308
309 err |= re_hprintf(pf, " Local Candidates: %H",
310 trice_lcands_debug, &icem->lcandl);
311 err |= re_hprintf(pf, " Remote Candidates: %H",
312 trice_rcands_debug, &icem->rcandl);
313 err |= re_hprintf(pf, " Check list: ");
314 err |= trice_candpairs_debug(pf, icem->conf.ansi, &icem->checkl);
315
316 err |= re_hprintf(pf, " Valid list: ");
317 err |= trice_candpairs_debug(pf, icem->conf.ansi, &icem->validl);
318
319 err |= re_hprintf(pf, " Buffered STUN Requests: (%u)\n",
320 list_count(&icem->reqbufl));
321
322 if (icem->checklist)
323 err |= trice_checklist_debug(pf, icem->checklist);
324
325 err |= re_hprintf(pf, " TCP Connections: (%u)\n",
326 list_count(&icem->connl));
327
328 for (le = list_head(&icem->connl); le; le = le->next) {
329 struct ice_tcpconn *conn = le->data;
330
331 err |= re_hprintf(pf, " %H\n",
332 trice_conn_debug, conn);
333 }
334
335 return err;
336}
337
338
339/**
340 * Get the list of Local Candidates (struct cand)
341 *
342 * @param icem ICE Media object
343 *
344 * @return List of Local Candidates
345 */
346struct list *trice_lcandl(const struct trice *icem)
347{
348 return icem ? (struct list *)&icem->lcandl : NULL;
349}
350
351
352/**
353 * Get the list of Remote Candidates (struct cand)
354 *
355 * @param icem ICE Media object
356 *
357 * @return List of Remote Candidates
358 */
359struct list *trice_rcandl(const struct trice *icem)
360{
361 return icem ? (struct list *)&icem->rcandl : NULL;
362}
363
364
365/**
366 * Get the checklist of Candidate Pairs
367 *
368 * @param icem ICE Media object
369 *
370 * @return Checklist (struct ice_candpair)
371 */
372struct list *trice_checkl(const struct trice *icem)
373{
374 return icem ? (struct list *)&icem->checkl : NULL;
375}
376
377
378/**
379 * Get the list of valid Candidate Pairs
380 *
381 * @param icem ICE Media object
382 *
383 * @return Validlist (struct ice_candpair)
384 */
385struct list *trice_validl(const struct trice *icem)
386{
387 return icem ? (struct list *)&icem->validl : NULL;
388}
389
390
391void trice_printf(struct trice *icem, const char *fmt, ...)
392{
393 va_list ap;
394
395 if (!icem || !icem->conf.debug)
396 return;
397
398 va_start(ap, fmt);
399 (void)re_printf("%v", fmt, &ap);
400 va_end(ap);
401}
402
403
404void trice_tracef(struct trice *icem, int color, const char *fmt, ...)
405{
406 va_list ap;
407
408 if (!icem || !icem->conf.trace)
409 return;
410
411 if (icem->conf.ansi && color) {
412 re_printf("\x1b[%dm", color);
413 }
414
415 va_start(ap, fmt);
416 (void)re_printf("%v", fmt, &ap);
417 va_end(ap);
418
419 if (icem->conf.ansi && color) {
420 re_printf("\x1b[;m");
421 }
422}
423
424
425void trice_switch_local_role(struct trice *ice)
426{
427 enum ice_role new_role;
428
429 if (!ice)
430 return;
431
432 switch (ice->lrole) {
433
434 case ICE_ROLE_CONTROLLING:
435 new_role = ICE_ROLE_CONTROLLED;
436 break;
437
438 case ICE_ROLE_CONTROLLED:
439 new_role = ICE_ROLE_CONTROLLING;
440 break;
441
442 default:
443 DEBUG_WARNING("trice_switch_local_role: local role unknown\n");
444 return;
445 }
446
447 DEBUG_NOTICE("Switch local role from %s to %s\n",
448 ice_role2name(ice->lrole), ice_role2name(new_role));
449
450 ice->lrole = new_role;
451
452 /* recompute pair priorities for all media streams */
453 trice_candpair_prio_order(&ice->checkl,
454 ice->lrole == ICE_ROLE_CONTROLLING);
455}
456
457
458/* sock = [ struct udp_sock | struct tcp_conn ] */
459bool trice_stun_process(struct trice *icem, struct ice_lcand *lcand,
460 int proto, void *sock, const struct sa *src,
461 struct mbuf *mb)
462{
463 struct stun_msg *msg = NULL;
464 struct stun_unknown_attr ua;
465 size_t start = mb->pos;
466 (void)proto;
467
468 if (stun_msg_decode(&msg, mb, &ua)) {
469 return false; /* continue recv-processing */
470 }
471
472 if (STUN_METHOD_BINDING == stun_msg_method(msg)) {
473
474 switch (stun_msg_class(msg)) {
475
476 case STUN_CLASS_REQUEST:
477 (void)trice_stund_recv(icem, lcand, sock,
478 src, msg, start);
479 break;
480
481 default:
482 if (icem->checklist) {
483 (void)stun_ctrans_recv(icem->checklist->stun,
484 msg, &ua);
485 }
486 else {
487 DEBUG_NOTICE("STUN resp from %J dropped"
488 " (no checklist)\n",
489 src);
490 }
491 break;
492 }
493 }
494
495 mem_deref(msg);
496
497 return true;
498}
499
500
501static void trice_reqbuf_destructor(void *data)
502{
503 struct trice_reqbuf *reqbuf = data;
504
505 list_unlink(&reqbuf->le);
506
507 mem_deref(reqbuf->req);
508 mem_deref(reqbuf->sock);
509 mem_deref(reqbuf->lcand);
510}
511
512
513int trice_reqbuf_append(struct trice *icem, struct ice_lcand *lcand,
514 void *sock, const struct sa *src,
515 struct stun_msg *req, size_t presz)
516{
517 struct trice_reqbuf *reqbuf;
518
519 if (!icem || !src ||!req)
520 return EINVAL;
521
522 reqbuf = mem_zalloc(sizeof(*reqbuf), trice_reqbuf_destructor);
523 if (!reqbuf)
524 return ENOMEM;
525
526 DEBUG_PRINTF("trice_reqbuf_append: Buffering request\n");
527 reqbuf->lcand = mem_ref(lcand);
528 reqbuf->sock = mem_ref(sock);
529 reqbuf->src = *src;
530 reqbuf->req = mem_ref(req);
531 reqbuf->presz = presz;
532
533 list_append(&icem->reqbufl, &reqbuf->le, reqbuf);
534
535 return 0;
536}
537
538int trice_set_port_range(struct trice *trice,
539 uint16_t min_port, uint16_t max_port)
540{
541 if (!trice)
542 return EINVAL;
543
544 if (max_port < min_port)
545 return ERANGE;
546
547 trice->ports.min = min_port;
548 trice->ports.max = max_port;
549
550 return 0;
551}