Coverage Report

Created: 2026-07-16 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rtpproxy/external/libre/src/stun/ctrans.c
Line
Count
Source
1
/**
2
 * @file stun/ctrans.c  STUN Client transactions
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_sa.h>
12
#include <re_udp.h>
13
#include <re_tcp.h>
14
#include <re_srtp.h>
15
#include <re_tls.h>
16
#include <re_list.h>
17
#include <re_tmr.h>
18
#include <re_md5.h>
19
#include <re_stun.h>
20
#include "stun.h"
21
22
23
struct stun_ctrans {
24
  struct le le;
25
  struct tmr tmr;
26
  struct sa dst;
27
  uint8_t tid[STUN_TID_SIZE];
28
  struct stun_ctrans **ctp;
29
  uint8_t *key;
30
  size_t keylen;
31
  void *sock;
32
  struct mbuf *mb;
33
  size_t pos;
34
  struct stun *stun;
35
  stun_resp_h *resph;
36
  void *arg;
37
  int proto;
38
  uint32_t txc;
39
  uint32_t ival;
40
  uint16_t met;
41
};
42
43
44
static void completed(struct stun_ctrans *ct, int err, uint16_t scode,
45
          const char *reason, const struct stun_msg *msg)
46
0
{
47
0
  stun_resp_h *resph = ct->resph;
48
0
  void *arg = ct->arg;
49
50
0
  list_unlink(&ct->le);
51
0
  tmr_cancel(&ct->tmr);
52
53
0
  if (ct->ctp) {
54
0
    *ct->ctp = NULL;
55
0
    ct->ctp = NULL;
56
0
  }
57
58
0
  ct->resph = NULL;
59
60
  /* must be destroyed before calling handler */
61
0
  mem_deref(ct);
62
63
0
  if (resph)
64
0
    resph(err, scode, reason, msg, arg);
65
0
}
66
67
68
static void destructor(void *arg)
69
0
{
70
0
  struct stun_ctrans *ct = arg;
71
72
0
  list_unlink(&ct->le);
73
0
  tmr_cancel(&ct->tmr);
74
0
  mem_deref(ct->key);
75
0
  mem_deref(ct->sock);
76
0
  mem_deref(ct->mb);
77
0
}
78
79
80
static void timeout_handler(void *arg)
81
0
{
82
0
  struct stun_ctrans *ct = arg;
83
0
  const struct stun_conf *cfg = stun_conf(ct->stun);
84
0
  int err = ETIMEDOUT;
85
86
0
  if (ct->txc++ >= cfg->rc)
87
0
    goto error;
88
89
0
  ct->mb->pos = ct->pos;
90
91
0
  err = stun_send(ct->proto, ct->sock, &ct->dst, ct->mb);
92
0
  if (err)
93
0
    goto error;
94
95
0
  ct->ival = (ct->txc >= cfg->rc) ? cfg->rto * cfg->rm : ct->ival * 2;
96
97
0
  tmr_start(&ct->tmr, ct->ival, timeout_handler, ct);
98
0
  return;
99
100
0
 error:
101
0
  completed(ct, err, 0, NULL, NULL);
102
0
}
103
104
105
static bool match_handler(struct le *le, void *arg)
106
0
{
107
0
  struct stun_ctrans *ct = le->data;
108
0
  struct stun_msg *msg = arg;
109
110
0
  if (ct->met != stun_msg_method(msg))
111
0
    return false;
112
113
0
  if (memcmp(ct->tid, stun_msg_tid(msg), STUN_TID_SIZE))
114
0
    return false;
115
116
0
  return true;
117
0
}
118
119
120
static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg)
121
0
{
122
0
  struct stun *stun = arg;
123
0
  (void)src;
124
125
0
  (void)stun_recv(stun, mb);
126
0
}
127
128
129
static void tcp_recv_handler(struct mbuf *mb, void *arg)
130
0
{
131
0
  struct stun_ctrans *ct = arg;
132
133
0
  (void)stun_recv(ct->stun, mb);
134
0
}
135
136
137
static void tcp_estab_handler(void *arg)
138
0
{
139
0
  struct stun_ctrans *ct = arg;
140
0
  int err;
141
142
0
  err = tcp_send(ct->sock, ct->mb);
143
0
  if (!err)
144
0
    return;
145
146
0
  completed(ct, err, 0, NULL, NULL);
147
0
}
148
149
150
static void tcp_close_handler(int err, void *arg)
151
0
{
152
0
  struct stun_ctrans *ct = arg;
153
154
0
  completed(ct, err, 0, NULL, NULL);
155
0
}
156
157
158
/**
159
 * Handle an incoming STUN message to a Client Transaction
160
 *
161
 * @param stun STUN instance
162
 * @param msg  STUN message
163
 * @param ua   Unknown attributes
164
 *
165
 * @return 0 if success, otherwise errorcode
166
 */
167
int stun_ctrans_recv(struct stun *stun, const struct stun_msg *msg,
168
         const struct stun_unknown_attr *ua)
169
603
{
170
603
  struct stun_errcode ec = {0, "OK"};
171
603
  struct stun_attr *errcode;
172
603
  struct stun_ctrans *ct;
173
603
  int err = 0, herr = 0;
174
175
603
  if (!stun || !msg || !ua)
176
603
    return EINVAL;
177
178
0
  switch (stun_msg_class(msg)) {
179
180
0
  case STUN_CLASS_ERROR_RESP:
181
0
    errcode = stun_msg_attr(msg, STUN_ATTR_ERR_CODE);
182
0
    if (!errcode)
183
0
      herr = EPROTO;
184
0
    else
185
0
      ec = errcode->v.err_code;
186
    /*@fallthrough@*/
187
188
0
  case STUN_CLASS_SUCCESS_RESP:
189
0
    ct = list_ledata(list_apply(&stun->ctl, true,
190
0
              match_handler, (void *)msg));
191
0
    if (!ct) {
192
0
      err = ENOENT;
193
0
      break;
194
0
    }
195
196
0
    switch (ec.code) {
197
198
0
    case 401:
199
0
    case 438:
200
0
      break;
201
202
0
    default:
203
0
      if (!ct->key)
204
0
        break;
205
206
0
      err = stun_msg_chk_mi(msg, ct->key, ct->keylen);
207
0
      break;
208
0
    }
209
210
0
    if (err)
211
0
      break;
212
213
0
    if (!herr && ua->typec > 0)
214
0
      herr = EPROTO;
215
216
0
    completed(ct, herr, ec.code, ec.reason, msg);
217
0
    break;
218
219
0
  default:
220
0
    break;
221
0
  }
222
223
0
  return err;
224
0
}
225
226
227
int stun_ctrans_request(struct stun_ctrans **ctp, struct stun *stun, int proto,
228
      void *sock, const struct sa *dst, struct mbuf *mb,
229
      const uint8_t tid[], uint16_t met, const uint8_t *key,
230
      size_t keylen, stun_resp_h *resph, void *arg)
231
0
{
232
0
  struct stun_ctrans *ct;
233
0
  int err = 0;
234
235
0
  if (!stun || !mb)
236
0
    return EINVAL;
237
238
0
  ct = mem_zalloc(sizeof(*ct), destructor);
239
0
  if (!ct)
240
0
    return ENOMEM;
241
242
0
  list_append(&stun->ctl, &ct->le, ct);
243
0
  memcpy(ct->tid, tid, STUN_TID_SIZE);
244
0
  ct->proto = proto;
245
0
  ct->sock  = mem_ref(sock);
246
0
  ct->mb    = mem_ref(mb);
247
0
  ct->pos   = mb->pos;
248
0
  ct->stun  = stun;
249
0
  ct->met   = met;
250
251
0
  if (key) {
252
0
    ct->key = mem_alloc(keylen, NULL);
253
0
    if (!ct->key) {
254
0
      err = ENOMEM;
255
0
      goto out;
256
0
    }
257
258
0
    memcpy(ct->key, key, keylen);
259
0
    ct->keylen = keylen;
260
0
  }
261
262
0
  switch (proto) {
263
264
0
  case IPPROTO_UDP:
265
0
    if (!dst) {
266
0
      err = EINVAL;
267
0
      break;
268
0
    }
269
270
0
    ct->dst = *dst;
271
0
    ct->ival = stun_conf(stun)->rto;
272
0
    tmr_start(&ct->tmr, ct->ival, timeout_handler, ct);
273
274
0
    if (!sock) {
275
0
      err = udp_listen((struct udp_sock **)&ct->sock, NULL,
276
0
           udp_recv_handler, stun);
277
0
      if (err)
278
0
        break;
279
0
    }
280
281
0
    ct->txc = 1;
282
0
    err = udp_send(ct->sock, dst, mb);
283
0
    break;
284
285
0
  case IPPROTO_TCP:
286
0
    ct->txc = stun_conf(stun)->rc;
287
0
    tmr_start(&ct->tmr, stun_conf(stun)->ti, timeout_handler, ct);
288
0
    if (sock) {
289
0
      err = tcp_send(sock, mb);
290
0
      break;
291
0
    }
292
293
0
    err = tcp_connect((struct tcp_conn **)&ct->sock, dst,
294
0
          tcp_estab_handler, tcp_recv_handler,
295
0
          tcp_close_handler, ct);
296
0
    break;
297
298
#ifdef USE_DTLS
299
  case STUN_TRANSP_DTLS:
300
    if (!sock) {
301
      err = EINVAL;
302
      break;
303
    }
304
305
    ct->ival = stun_conf(stun)->rto;
306
    tmr_start(&ct->tmr, ct->ival, timeout_handler, ct);
307
308
    ct->txc = 1;
309
    err = dtls_send(ct->sock, mb);
310
    break;
311
#endif
312
313
0
  default:
314
0
    err = EPROTONOSUPPORT;
315
0
    break;
316
0
  }
317
318
0
 out:
319
0
  if (!err) {
320
0
    if (ctp) {
321
0
      ct->ctp = ctp;
322
0
      *ctp = ct;
323
0
    }
324
325
0
    ct->resph = resph;
326
0
    ct->arg   = arg;
327
0
  }
328
0
  else
329
0
    mem_deref(ct);
330
331
0
  return err;
332
0
}
333
334
335
static bool close_handler(struct le *le, void *arg)
336
0
{
337
0
  struct stun_ctrans *ct = le->data;
338
0
  (void)arg;
339
340
0
  completed(ct, ECONNABORTED, 0, NULL, NULL);
341
342
0
  return false;
343
0
}
344
345
346
void stun_ctrans_close(struct stun *stun)
347
0
{
348
0
  if (!stun)
349
0
    return;
350
351
0
  (void)list_apply(&stun->ctl, true, close_handler, NULL);
352
0
}
353
354
355
static bool debug_handler(struct le *le, void *arg)
356
0
{
357
0
  struct stun_ctrans *ct = le->data;
358
0
  struct re_printf *pf = arg;
359
0
  int err = 0;
360
361
0
  err |= re_hprintf(pf, " method=%s", stun_method_name(ct->met));
362
0
  err |= re_hprintf(pf, " tid=%w", ct->tid, sizeof(ct->tid));
363
0
  err |= re_hprintf(pf, " rto=%ums", stun_conf(ct->stun)->rto);
364
0
  err |= re_hprintf(pf, " tmr=%llu", tmr_get_expire(&ct->tmr));
365
0
  err |= re_hprintf(pf, " n=%u", ct->txc);
366
0
  err |= re_hprintf(pf, " interval=%u", ct->ival);
367
0
  err |= re_hprintf(pf, "\n");
368
369
0
  return 0 != err;
370
0
}
371
372
373
int stun_ctrans_debug(struct re_printf *pf, const struct stun *stun)
374
0
{
375
0
  int err;
376
377
0
  if (!stun)
378
0
    return 0;
379
380
0
  err = re_hprintf(pf, "STUN client transactions: (%u)\n",
381
0
       list_count(&stun->ctl));
382
383
0
  (void)list_apply(&stun->ctl, true, debug_handler, pf);
384
385
0
  return err;
386
0
}