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 
completed(struct stun_ctrans * ct,int err,uint16_t scode,const char * reason,const struct stun_msg * msg)44 static 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 
destructor(void * arg)68 static 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 
timeout_handler(void * arg)80 static 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 
match_handler(struct le * le,void * arg)105 static 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 
udp_recv_handler(const struct sa * src,struct mbuf * mb,void * arg)120 static 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 
tcp_recv_handler(struct mbuf * mb,void * arg)129 static 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 
tcp_estab_handler(void * arg)137 static 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 
tcp_close_handler(int err,void * arg)150 static 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  */
stun_ctrans_recv(struct stun * stun,const struct stun_msg * msg,const struct stun_unknown_attr * ua)167 int 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 
stun_ctrans_request(struct stun_ctrans ** ctp,struct stun * stun,int proto,void * sock,const struct sa * dst,struct mbuf * mb,const uint8_t tid[],uint16_t met,const uint8_t * key,size_t keylen,stun_resp_h * resph,void * arg)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 {
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 
close_handler(struct le * le,void * arg)335 static 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 
stun_ctrans_close(struct stun * stun)346 void 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 
debug_handler(struct le * le,void * arg)355 static 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 
stun_ctrans_debug(struct re_printf * pf,const struct stun * stun)373 int 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 }
387