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