Coverage Report

Created: 2025-12-11 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rtpproxy/external/libre/src/ice/stunsrv.c
Line
Count
Source
1
/**
2
 * @file stunsrv.c  Basic STUN Server for Connectivity checks
3
 *
4
 * Copyright (C) 2010 Creytiv.com
5
 */
6
#include <string.h>
7
#include <re_types.h>
8
#include <re_fmt.h>
9
#include <re_mem.h>
10
#include <re_mbuf.h>
11
#include <re_list.h>
12
#include <re_tmr.h>
13
#include <re_sa.h>
14
#include <re_stun.h>
15
#include <re_ice.h>
16
#include <re_sys.h>
17
#include "ice.h"
18
19
20
0
#define DEBUG_MODULE "stunsrv"
21
#define DEBUG_LEVEL 5
22
#include <re_dbg.h>
23
24
25
static const char *sw = "ice stunsrv v" VERSION " (" ARCH "/" OS ")";
26
27
28
static void triggered_check(struct icem *icem, struct ice_cand *lcand,
29
          struct ice_cand *rcand)
30
0
{
31
0
  struct ice_candpair *cp = NULL;
32
0
  int err;
33
34
0
  if (lcand && rcand)
35
0
    cp = icem_candpair_find(&icem->checkl, lcand, rcand);
36
37
0
  if (cp) {
38
39
0
    switch (cp->state) {
40
41
#if 0
42
      /* TODO: I am not sure why we should cancel the
43
       *       pending Connectivity check here. this
44
       *       can lead to a deadlock situation where
45
       *       both agents are stuck on sending
46
       *       triggered checks on the same candidate pair
47
       */
48
    case ICE_CANDPAIR_INPROGRESS:
49
      icem_candpair_cancel(cp);
50
      /*@fallthrough@*/
51
#endif
52
53
0
    case ICE_CANDPAIR_FAILED:
54
0
      icem_candpair_set_state(cp, ICE_CANDPAIR_WAITING);
55
      /*@fallthrough@*/
56
57
0
    case ICE_CANDPAIR_FROZEN:
58
0
    case ICE_CANDPAIR_WAITING:
59
0
      err = icem_conncheck_send(cp, false, true);
60
0
      if (err) {
61
0
        DEBUG_WARNING("triggered check failed\n");
62
0
      }
63
0
      break;
64
65
0
    case ICE_CANDPAIR_SUCCEEDED:
66
0
    default:
67
0
      break;
68
0
    }
69
0
  }
70
0
  else {
71
72
#if 0
73
    err = icem_candpair_alloc(&cp, icem, lcand, rcand);
74
    if (err) {
75
      DEBUG_WARNING("failed to allocate candpair:"
76
              " lcand=%p rcand=%p (%m)\n",
77
              lcand, rcand, err);
78
      return;
79
    }
80
81
    icem_candpair_prio_order(&icem->checkl);
82
83
    icem_candpair_set_state(cp, ICE_CANDPAIR_WAITING);
84
85
    (void)icem_conncheck_send(cp, false, true);
86
#endif
87
88
0
  }
89
0
}
90
91
92
/*
93
 * 7.2.1.  Additional Procedures for Full Implementations
94
 */
95
static int handle_stun_full(struct icem *icem,
96
          struct icem_comp *comp, const struct sa *src,
97
          uint32_t prio, bool use_cand, bool tunnel)
98
0
{
99
0
  struct ice_cand *lcand = NULL, *rcand;
100
0
  struct ice_candpair *cp = NULL;
101
0
  int err;
102
103
0
  rcand = icem_cand_find(&icem->rcandl, comp->id, src);
104
0
  if (!rcand) {
105
0
    err = icem_rcand_add_prflx(&rcand, icem, comp->id, prio, src);
106
0
    if (err)
107
0
      return err;
108
0
  }
109
110
0
  cp = icem_candpair_find_rcand(icem, rcand);
111
0
  if (cp)
112
0
    lcand = cp->lcand;
113
0
  else
114
0
    lcand = icem_lcand_find_checklist(icem, comp->id);
115
116
0
  if (!lcand) {
117
0
    DEBUG_WARNING("{%s.%u} local candidate not found"
118
0
            " (checklist=%u) (src=%J)\n",
119
0
            icem->name, comp->id,
120
0
            list_count(&icem->checkl), src);
121
0
    return 0;
122
0
  }
123
124
0
  triggered_check(icem, lcand, rcand);
125
126
0
  if (!cp) {
127
0
    cp = icem_candpair_find_rcand(icem, rcand);
128
0
    if (!cp) {
129
0
      DEBUG_WARNING("{%s.%u} candidate pair not found:"
130
0
              " source=%J\n",
131
0
              icem->name, comp->id, src);
132
0
      return 0;
133
0
    }
134
0
  }
135
136
0
#if ICE_TRACE
137
0
  icecomp_printf(comp, "Rx Binding Request from %J via %s"
138
0
           " (candpair=%s) %s\n",
139
0
           src, tunnel ? "Tunnel" : "Socket",
140
0
           cp ? ice_candpair_state2name(cp->state) : "n/a",
141
0
           use_cand ? "[USE]" : "");
142
#else
143
  (void)tunnel;
144
#endif
145
146
  /* 7.2.1.5.  Updating the Nominated Flag */
147
0
  if (use_cand) {
148
0
    if (icem->lrole == ICE_ROLE_CONTROLLED &&
149
0
        cp->state == ICE_CANDPAIR_SUCCEEDED) {
150
151
0
      if (!cp->nominated) {
152
0
        icecomp_printf(comp, "setting NOMINATED"
153
0
                 " flag on candpair [%H]\n",
154
0
                 icem_candpair_debug, cp);
155
0
      }
156
157
0
      cp->nominated = true;
158
0
    }
159
160
    /* Cancel conncheck. Choose Selected Pair */
161
0
    icem_candpair_make_valid(cp);
162
163
0
    if (icem->conf.nom == ICE_NOMINATION_REGULAR) {
164
0
      icem_candpair_cancel(cp);
165
0
      icem_comp_set_selected(comp, cp);
166
0
    }
167
0
  }
168
169
0
  return 0;
170
0
}
171
172
173
/*
174
 * 7.2.2.  Additional Procedures for Lite Implementations
175
 */
176
static int handle_stun_lite(struct icem *icem,
177
          struct icem_comp *comp, const struct sa *src,
178
          bool use_cand)
179
0
{
180
0
  struct ice_cand *lcand, *rcand;
181
0
  struct ice_candpair *cp;
182
0
  int err;
183
184
0
  if (!use_cand)
185
0
    return 0;
186
187
0
  rcand = icem_cand_find(&icem->rcandl, comp->id, src);
188
0
  if (!rcand) {
189
0
    DEBUG_WARNING("lite: could not find remote candidate\n");
190
0
    return 0;
191
0
  }
192
193
  /* find the local host candidate with the same component */
194
0
  lcand = icem_cand_find(&icem->lcandl, comp->id, NULL);
195
0
  if (!lcand) {
196
0
    DEBUG_WARNING("lite: could not find local candidate\n");
197
0
    return 0;
198
0
  }
199
200
  /* search validlist for existing candpair's */
201
0
  if (icem_candpair_find(&icem->validl, lcand, rcand))
202
0
    return 0;
203
204
0
  err = icem_candpair_alloc(&cp, icem, lcand, rcand);
205
0
  if (err) {
206
0
    DEBUG_WARNING("lite: failed to created candidate pair\n");
207
0
    return err;
208
0
  }
209
210
0
  icem_candpair_make_valid(cp);
211
0
  cp->nominated = true;
212
213
0
  return 0;
214
0
}
215
216
217
static int stunsrv_ereply(struct icem_comp *comp, const struct sa *src,
218
        size_t presz, const struct stun_msg *req,
219
        uint16_t scode, const char *reason)
220
2
{
221
2
  struct icem *icem = comp->icem;
222
223
2
  return stun_ereply(icem->proto, comp->sock, src, presz, req,
224
2
         scode, reason,
225
2
         (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 1,
226
2
         STUN_ATTR_SOFTWARE, sw);
227
2
}
228
229
230
int icem_stund_recv(struct icem_comp *comp, const struct sa *src,
231
        struct stun_msg *req, size_t presz)
232
433
{
233
433
  struct icem *icem = comp->icem;
234
433
  struct stun_attr *attr;
235
433
  struct pl lu, ru;
236
433
  enum ice_role rrole = ICE_ROLE_UNKNOWN;
237
433
  uint64_t tiebrk = 0;
238
433
  uint32_t prio_prflx;
239
433
  bool use_cand = false;
240
433
  int err;
241
242
  /* RFC 5389: Fingerprint errors are silently discarded */
243
433
  err = stun_msg_chk_fingerprint(req);
244
433
  if (err)
245
431
    return err;
246
247
2
  err = stun_msg_chk_mi(req, (uint8_t *)icem->lpwd, strlen(icem->lpwd));
248
2
  if (err) {
249
2
    if (err == EBADMSG)
250
0
      goto unauth;
251
2
    else
252
2
      goto badmsg;
253
2
  }
254
255
0
  attr = stun_msg_attr(req, STUN_ATTR_USERNAME);
256
0
  if (!attr)
257
0
    goto badmsg;
258
259
0
  err = re_regex(attr->v.username, strlen(attr->v.username),
260
0
           "[^:]+:[^]+", &lu, &ru);
261
0
  if (err) {
262
0
    DEBUG_WARNING("could not parse USERNAME attribute (%s)\n",
263
0
            attr->v.username);
264
0
    goto unauth;
265
0
  }
266
0
  if (pl_strcmp(&lu, icem->lufrag))
267
0
    goto unauth;
268
0
  if (str_isset(icem->rufrag) && pl_strcmp(&ru, icem->rufrag))
269
0
    goto unauth;
270
271
0
  attr = stun_msg_attr(req, STUN_ATTR_CONTROLLED);
272
0
  if (attr) {
273
0
    rrole = ICE_ROLE_CONTROLLED;
274
0
    tiebrk = attr->v.uint64;
275
0
  }
276
277
0
  attr = stun_msg_attr(req, STUN_ATTR_CONTROLLING);
278
0
  if (attr) {
279
0
    rrole = ICE_ROLE_CONTROLLING;
280
0
    tiebrk = attr->v.uint64;
281
0
  }
282
283
0
  if (rrole == icem->lrole) {
284
0
    if (icem->tiebrk >= tiebrk)
285
0
      ice_switch_local_role(icem);
286
0
    else
287
0
      goto conflict;
288
0
  }
289
290
0
  attr = stun_msg_attr(req, STUN_ATTR_PRIORITY);
291
0
  if (attr)
292
0
    prio_prflx = attr->v.uint32;
293
0
  else
294
0
    goto badmsg;
295
296
0
  attr = stun_msg_attr(req, STUN_ATTR_USE_CAND);
297
0
  if (attr)
298
0
    use_cand = true;
299
300
0
  if (icem->lmode == ICE_MODE_FULL) {
301
0
    err = handle_stun_full(icem, comp, src, prio_prflx,
302
0
               use_cand, presz > 0);
303
0
  }
304
0
  else {
305
0
    err = handle_stun_lite(icem, comp, src, use_cand);
306
0
  }
307
308
0
  if (err)
309
0
    goto badmsg;
310
311
0
  return stun_reply(icem->proto, comp->sock, src, presz, req,
312
0
        (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 2,
313
0
        STUN_ATTR_XOR_MAPPED_ADDR, src,
314
0
        STUN_ATTR_SOFTWARE, sw);
315
316
2
 badmsg:
317
2
  return stunsrv_ereply(comp, src, presz, req, 400, "Bad Request");
318
319
0
 unauth:
320
0
  return stunsrv_ereply(comp, src, presz, req, 401, "Unauthorized");
321
322
0
 conflict:
323
0
  return stunsrv_ereply(comp, src, presz, req, 487, "Role Conflict");
324
0
}