Coverage Report

Created: 2026-05-30 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rtpproxy/external/libre/src/ice/icem.c
Line
Count
Source
1
/**
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_turn.h>
15
#include <re_ice.h>
16
#include "ice.h"
17
18
19
402
#define DEBUG_MODULE "icem"
20
#define DEBUG_LEVEL 5
21
#include <re_dbg.h>
22
23
24
/*
25
 * ICE Implementation as of RFC 5245
26
 */
27
28
29
static const struct ice_conf conf_default = {
30
  ICE_NOMINATION_REGULAR,
31
  ICE_DEFAULT_RTO_RTP,
32
  ICE_DEFAULT_RC,
33
  false
34
};
35
36
37
/** Determining Role */
38
static void ice_determine_role(struct icem *icem, enum ice_role role)
39
9.33k
{
40
9.33k
  if (!icem)
41
0
    return;
42
43
9.33k
  if (icem->lmode == icem->rmode)
44
0
    icem->lrole = role;
45
9.33k
  else if (icem->lmode == ICE_MODE_FULL)
46
0
    icem->lrole = ICE_ROLE_CONTROLLING;
47
9.33k
  else
48
9.33k
    icem->lrole = ICE_ROLE_CONTROLLED;
49
9.33k
}
50
51
52
static void icem_destructor(void *data)
53
9.32k
{
54
9.32k
  struct icem *icem = data;
55
56
9.32k
  tmr_cancel(&icem->tmr_pace);
57
9.32k
  list_flush(&icem->compl);
58
9.32k
  list_flush(&icem->validl);
59
9.32k
  list_flush(&icem->checkl);
60
9.32k
  list_flush(&icem->lcandl);
61
9.32k
  list_flush(&icem->rcandl);
62
9.32k
  mem_deref(icem->lufrag);
63
9.32k
  mem_deref(icem->lpwd);
64
9.32k
  mem_deref(icem->rufrag);
65
9.32k
  mem_deref(icem->rpwd);
66
9.32k
  mem_deref(icem->stun);
67
9.32k
}
68
69
70
/**
71
 * Add a new ICE Media object to the ICE Session
72
 *
73
 * @param icemp   Pointer to allocated ICE Media object
74
 * @param mode    ICE mode
75
 * @param role    Local ICE role
76
 * @param proto   Transport protocol
77
 * @param layer   Protocol stack layer
78
 * @param tiebrk  Tie-breaker value, must be same for all media streams
79
 * @param lufrag  Local username fragment
80
 * @param lpwd    Local password
81
 * @param chkh    Connectivity check handler
82
 * @param arg     Handler argument
83
 *
84
 * @return 0 if success, otherwise errorcode
85
 */
86
int  icem_alloc(struct icem **icemp,
87
    enum ice_mode mode, enum ice_role role,
88
    int proto, int layer,
89
    uint64_t tiebrk, const char *lufrag, const char *lpwd,
90
    ice_connchk_h *chkh, void *arg)
91
9.73k
{
92
9.73k
  struct icem *icem;
93
9.73k
  int err = 0;
94
95
9.73k
  if (!icemp || !tiebrk || !lufrag || !lpwd)
96
0
    return EINVAL;
97
98
9.73k
  if (str_len(lufrag) < 4 || str_len(lpwd) < 22) {
99
402
    DEBUG_WARNING("alloc: lufrag/lpwd is too short\n");
100
402
    return EINVAL;
101
402
  }
102
103
9.33k
  if (proto != IPPROTO_UDP)
104
0
    return EPROTONOSUPPORT;
105
106
9.33k
  icem = mem_zalloc(sizeof(*icem), icem_destructor);
107
9.33k
  if (!icem)
108
0
    return ENOMEM;
109
110
9.33k
  icem->conf = conf_default;
111
112
9.33k
  tmr_init(&icem->tmr_pace);
113
9.33k
  list_init(&icem->lcandl);
114
9.33k
  list_init(&icem->rcandl);
115
9.33k
  list_init(&icem->checkl);
116
9.33k
  list_init(&icem->validl);
117
118
9.33k
  icem->layer = layer;
119
9.33k
  icem->proto = proto;
120
9.33k
  icem->state = ICE_CHECKLIST_NULL;
121
9.33k
  icem->chkh  = chkh;
122
9.33k
  icem->arg   = arg;
123
124
9.33k
  if (err)
125
0
    goto out;
126
127
9.33k
  icem->lmode = mode;
128
9.33k
  icem->tiebrk = tiebrk;
129
130
9.33k
  err |= str_dup(&icem->lufrag, lufrag);
131
9.33k
  err |= str_dup(&icem->lpwd, lpwd);
132
9.33k
  if (err)
133
0
    goto out;
134
135
9.33k
  ice_determine_role(icem, role);
136
137
9.33k
  if (ICE_MODE_FULL == icem->lmode) {
138
139
0
    err = stun_alloc(&icem->stun, NULL, NULL, NULL);
140
0
    if (err)
141
0
      goto out;
142
143
    /* Update STUN Transport */
144
0
    stun_conf(icem->stun)->rto = icem->conf.rto;
145
0
    stun_conf(icem->stun)->rc = icem->conf.rc;
146
0
  }
147
148
9.33k
 out:
149
9.33k
  if (err)
150
0
    mem_deref(icem);
151
9.33k
  else if (icemp)
152
9.33k
    *icemp = icem;
153
154
9.33k
  return err;
155
9.33k
}
156
157
158
/**
159
 * Get the ICE Configuration
160
 *
161
 * @param icem  ICE Media object
162
 *
163
 * @return ICE Configuration
164
 */
165
struct ice_conf *icem_conf(struct icem *icem)
166
0
{
167
0
  return icem ? &icem->conf : NULL;
168
0
}
169
170
171
enum ice_role icem_local_role(const struct icem *icem)
172
0
{
173
0
  return icem ? icem->lrole : ICE_ROLE_UNKNOWN;
174
0
}
175
176
177
void icem_set_conf(struct icem *icem, const struct ice_conf *conf)
178
0
{
179
0
  if (!icem || !conf)
180
0
    return;
181
182
0
  icem->conf = *conf;
183
184
0
  if (icem->stun) {
185
186
    /* Update STUN Transport */
187
0
    stun_conf(icem->stun)->rto = icem->conf.rto;
188
0
    stun_conf(icem->stun)->rc = icem->conf.rc;
189
0
  }
190
0
}
191
192
193
/**
194
 * Set the local role on the ICE Session
195
 *
196
 * @param icem    ICE Media object
197
 * @param role    Local ICE role
198
 */
199
void icem_set_role(struct icem *icem, enum ice_role role)
200
0
{
201
0
  if (!icem)
202
0
    return;
203
204
0
  ice_determine_role(icem, role);
205
0
}
206
207
208
/**
209
 * Set the name of the ICE Media object, used for debugging
210
 *
211
 * @param icem  ICE Media object
212
 * @param name  Media name
213
 */
214
void icem_set_name(struct icem *icem, const char *name)
215
0
{
216
0
  if (!icem)
217
0
    return;
218
219
0
  str_ncpy(icem->name, name, sizeof(icem->name));
220
0
}
221
222
223
/**
224
 * Add a new component to the ICE Media object
225
 *
226
 * @param icem    ICE Media object
227
 * @param compid  Component ID
228
 * @param sock    Application protocol socket
229
 *
230
 * @return 0 if success, otherwise errorcode
231
 */
232
int icem_comp_add(struct icem *icem, unsigned compid, void *sock)
233
8.34k
{
234
8.34k
  struct icem_comp *comp;
235
8.34k
  int err;
236
237
8.34k
  if (!icem)
238
0
    return EINVAL;
239
240
8.34k
  if (icem_comp_find(icem, compid))
241
0
    return EALREADY;
242
243
8.34k
  err = icem_comp_alloc(&comp, icem, compid, sock);
244
8.34k
  if (err)
245
0
    return err;
246
247
8.34k
  list_append(&icem->compl, &comp->le, comp);
248
249
8.34k
  return 0;
250
8.34k
}
251
252
253
/**
254
 * Add a new candidate to the ICE Media object
255
 *
256
 * @param icem    ICE Media object
257
 * @param compid  Component ID
258
 * @param lprio   Local priority
259
 * @param ifname  Name of the network interface
260
 * @param addr    Local network address
261
 *
262
 * @return 0 if success, otherwise errorcode
263
 */
264
int icem_cand_add(struct icem *icem, unsigned compid, uint16_t lprio,
265
      const char *ifname, const struct sa *addr)
266
8.34k
{
267
8.34k
  if (!icem_comp_find(icem, compid))
268
0
    return ENOENT;
269
270
8.34k
  return icem_lcand_add_base(icem, compid, lprio, ifname,
271
8.34k
           ICE_TRANSP_UDP, addr);
272
8.34k
}
273
274
275
static void *unique_handler(struct le *le1, struct le *le2)
276
0
{
277
0
  struct ice_cand *c1 = le1->data, *c2 = le2->data;
278
279
0
  if (c1->base != c2->base || !sa_cmp(&c1->addr, &c2->addr, SA_ALL))
280
0
    return NULL;
281
282
  /* remove candidate with lower priority */
283
0
  return c1->prio < c2->prio ? c1 : c2;
284
0
}
285
286
287
/**
288
 * Eliminating Redundant Candidates
289
 *
290
 * @param icem    ICE Media object
291
 */
292
void icem_cand_redund_elim(struct icem *icem)
293
0
{
294
0
  uint32_t n;
295
296
0
  n = ice_list_unique(&icem->lcandl, unique_handler);
297
0
  if (n > 0) {
298
0
    icem_printf(icem, "redundant candidates eliminated: %u\n", n);
299
0
  }
300
0
}
301
302
303
/**
304
 * Get the Default Local Candidate
305
 *
306
 * @param icem   ICE Media object
307
 * @param compid Component ID
308
 *
309
 * @return Default Local Candidate address if set, otherwise NULL
310
 */
311
const struct sa *icem_cand_default(struct icem *icem, unsigned compid)
312
0
{
313
0
  const struct icem_comp *comp = icem_comp_find(icem, compid);
314
315
0
  if (!comp || !comp->def_lcand)
316
0
    return NULL;
317
318
0
  return &comp->def_lcand->addr;
319
0
}
320
321
322
/**
323
 * Verifying ICE Support and set default remote candidate
324
 *
325
 * @param icem   ICE Media
326
 * @param compid Component ID
327
 * @param raddr  Address of default remote candidate
328
 *
329
 * @return True if ICE is supported, otherwise false
330
 */
331
bool icem_verify_support(struct icem *icem, unsigned compid,
332
       const struct sa *raddr)
333
0
{
334
0
  struct ice_cand *rcand;
335
0
  bool match;
336
337
0
  if (!icem)
338
0
    return false;
339
340
0
  rcand = icem_cand_find(&icem->rcandl, compid, raddr);
341
0
  match = rcand != NULL;
342
343
0
  if (!match)
344
0
    icem->mismatch = true;
345
346
0
  if (rcand) {
347
0
    icem_comp_set_default_rcand(icem_comp_find(icem, compid),
348
0
              rcand);
349
0
  }
350
351
0
  return match;
352
0
}
353
354
355
/**
356
 * Add a TURN Channel for the selected remote address
357
 *
358
 * @param icem   ICE Media object
359
 * @param compid Component ID
360
 * @param raddr  Remote network address
361
 *
362
 * @return 0 if success, otherwise errorcode
363
 */
364
int icem_add_chan(struct icem *icem, unsigned compid, const struct sa *raddr)
365
0
{
366
0
  struct icem_comp *comp;
367
368
0
  if (!icem)
369
0
    return EINVAL;
370
371
0
  comp = icem_comp_find(icem, compid);
372
0
  if (!comp)
373
0
    return ENOENT;
374
375
0
  if (comp->turnc) {
376
0
    DEBUG_NOTICE("{%s.%u} Add TURN Channel to peer %J\n",
377
0
           comp->icem->name, comp->id, raddr);
378
379
0
    return turnc_add_chan(comp->turnc, raddr, NULL, NULL);
380
0
  }
381
382
0
  return 0;
383
0
}
384
385
386
static void purge_relayed(struct icem *icem, struct icem_comp *comp)
387
0
{
388
0
  if (comp->turnc) {
389
0
    DEBUG_NOTICE("{%s.%u} purge local RELAY candidates\n",
390
0
           icem->name, comp->id);
391
0
  }
392
393
  /*
394
   * Purge all Candidate-Pairs where the Local candidate
395
   * is of type "Relay"
396
   */
397
0
  icem_candpairs_flush(&icem->checkl, ICE_CAND_TYPE_RELAY, comp->id);
398
0
  icem_candpairs_flush(&icem->validl, ICE_CAND_TYPE_RELAY, comp->id);
399
400
0
  comp->turnc = mem_deref(comp->turnc);
401
0
}
402
403
404
/**
405
 * Update the ICE Media object
406
 *
407
 * @param icem ICE Media object
408
 */
409
void icem_update(struct icem *icem)
410
0
{
411
0
  struct le *le;
412
413
0
  if (!icem)
414
0
    return;
415
416
0
  for (le = icem->compl.head; le; le = le->next) {
417
418
0
    struct icem_comp *comp = le->data;
419
420
    /* remove TURN client if not used by local "Selected" */
421
0
    if (comp->cp_sel) {
422
423
0
      if (comp->cp_sel->lcand->type != ICE_CAND_TYPE_RELAY)
424
0
        purge_relayed(icem, comp);
425
0
    }
426
0
  }
427
0
}
428
429
430
/**
431
 * Get the ICE Mismatch flag of the ICE Media object
432
 *
433
 * @param icem ICE Media object
434
 *
435
 * @return True if ICE mismatch, otherwise false
436
 */
437
bool icem_mismatch(const struct icem *icem)
438
0
{
439
0
  return icem ? icem->mismatch : true;
440
0
}
441
442
443
/**
444
 * Print debug information for the ICE Media
445
 *
446
 * @param pf   Print function for debug output
447
 * @param icem ICE Media object
448
 *
449
 * @return 0 if success, otherwise errorcode
450
 */
451
int icem_debug(struct re_printf *pf, const struct icem *icem)
452
0
{
453
0
  struct le *le;
454
0
  int err = 0;
455
456
0
  if (!icem)
457
0
    return 0;
458
459
0
  err |= re_hprintf(pf, "----- ICE Media <%s> -----\n", icem->name);
460
461
0
  err |= re_hprintf(pf, " local_mode=%s, remote_mode=%s",
462
0
        ice_mode2name(icem->lmode),
463
0
        ice_mode2name(icem->rmode));
464
0
  err |= re_hprintf(pf, ", local_role=%s\n", ice_role2name(icem->lrole));
465
0
  err |= re_hprintf(pf, " local_ufrag=\"%s\" local_pwd=\"%s\"\n",
466
0
        icem->lufrag, icem->lpwd);
467
468
0
  err |= re_hprintf(pf, " Components: (%u)\n", list_count(&icem->compl));
469
0
  for (le = icem->compl.head; le; le = le->next) {
470
0
    struct icem_comp *comp = le->data;
471
472
0
    err |= re_hprintf(pf, "  %H\n", icecomp_debug, comp);
473
0
  }
474
475
0
  err |= re_hprintf(pf, " Local Candidates: %H",
476
0
        icem_cands_debug, &icem->lcandl);
477
0
  err |= re_hprintf(pf, " Remote Candidates: %H",
478
0
        icem_cands_debug, &icem->rcandl);
479
0
  err |= re_hprintf(pf, " Check list: [state=%s]%H",
480
0
        ice_checkl_state2name(icem->state),
481
0
        icem_candpairs_debug, &icem->checkl);
482
0
  err |= re_hprintf(pf, " Valid list: %H",
483
0
        icem_candpairs_debug, &icem->validl);
484
485
0
  err |= stun_debug(pf, icem->stun);
486
487
0
  return err;
488
0
}
489
490
491
/**
492
 * Get the list of Local Candidates (struct cand)
493
 *
494
 * @param icem ICE Media object
495
 *
496
 * @return List of Local Candidates
497
 */
498
struct list *icem_lcandl(const struct icem *icem)
499
13.4k
{
500
13.4k
  return icem ? (struct list *)&icem->lcandl : NULL;
501
13.4k
}
502
503
504
/**
505
 * Get the list of Remote Candidates (struct cand)
506
 *
507
 * @param icem ICE Media object
508
 *
509
 * @return List of Remote Candidates
510
 */
511
struct list *icem_rcandl(const struct icem *icem)
512
0
{
513
0
  return icem ? (struct list *)&icem->rcandl : NULL;
514
0
}
515
516
517
/**
518
 * Get the checklist of Candidate Pairs
519
 *
520
 * @param icem ICE Media object
521
 *
522
 * @return Checklist (struct ice_candpair)
523
 */
524
struct list *icem_checkl(const struct icem *icem)
525
0
{
526
0
  return icem ? (struct list *)&icem->checkl : NULL;
527
0
}
528
529
530
/**
531
 * Get the list of valid Candidate Pairs
532
 *
533
 * @param icem ICE Media object
534
 *
535
 * @return Validlist (struct ice_candpair)
536
 */
537
struct list *icem_validl(const struct icem *icem)
538
0
{
539
0
  return icem ? (struct list *)&icem->validl : NULL;
540
0
}
541
542
543
/**
544
 * Set the default local candidates, for ICE-lite mode only
545
 *
546
 * @param icem ICE Media object
547
 *
548
 * @return 0 if success, otherwise errorcode
549
 */
550
int icem_lite_set_default_candidates(struct icem *icem)
551
0
{
552
0
  struct le *le;
553
0
  int err = 0;
554
555
0
  if (icem->lmode != ICE_MODE_LITE)
556
0
    return EINVAL;
557
558
0
  for (le = icem->compl.head; le; le = le->next) {
559
560
0
    struct icem_comp *comp = le->data;
561
562
0
    err |= icem_comp_set_default_cand(comp);
563
0
  }
564
565
0
  return err;
566
0
}
567
568
569
int icem_comps_set_default_cand(struct icem *icem)
570
0
{
571
0
  struct le *le;
572
0
  int err = 0;
573
574
0
  if (!icem)
575
0
    return EINVAL;
576
577
0
  for (le = icem->compl.head; le; le = le->next) {
578
579
0
    struct icem_comp *comp = le->data;
580
581
0
    err |= icem_comp_set_default_cand(comp);
582
0
  }
583
584
0
  return err;
585
0
}
586
587
588
struct stun *icem_stun(struct icem *icem)
589
0
{
590
0
  return icem ? icem->stun : NULL;
591
0
}
592
593
594
void icem_printf(struct icem *icem, const char *fmt, ...)
595
0
{
596
0
  va_list ap;
597
598
0
  if (!icem || !icem->conf.debug)
599
0
    return;
600
601
0
  va_start(ap, fmt);
602
0
  (void)re_printf("{%11s. } %v", icem->name, fmt, &ap);
603
  va_end(ap);
604
0
}