1 /**
2  * @file chan.c  TURN Channels handling
3  *
4  * Copyright (C) 2010 Creytiv.com
5  */
6 #include <re_types.h>
7 #include <re_mem.h>
8 #include <re_mbuf.h>
9 #include <re_list.h>
10 #include <re_hash.h>
11 #include <re_tmr.h>
12 #include <re_sa.h>
13 #include <re_md5.h>
14 #include <re_udp.h>
15 #include <re_stun.h>
16 #include <re_turn.h>
17 #include "turnc.h"
18 
19 
20 enum {
21 	CHAN_LIFETIME = 600,
22 	CHAN_REFRESH = 250,
23 	CHAN_NUMB_MIN = 0x4000,
24 	CHAN_NUMB_MAX = 0x7fff
25 };
26 
27 
28 struct channels {
29 	struct hash *ht_numb;
30 	struct hash *ht_peer;
31 	uint16_t nr;
32 };
33 
34 
35 struct chan {
36 	struct le he_numb;
37 	struct le he_peer;
38 	struct loop_state ls;
39 	uint16_t nr;
40 	struct sa peer;
41 	struct tmr tmr;
42 	struct turnc *turnc;
43 	struct stun_ctrans *ct;
44 	turnc_chan_h *ch;
45 	void *arg;
46 };
47 
48 
49 static int chanbind_request(struct chan *chan, bool reset_ls);
50 
51 
channels_destructor(void * data)52 static void channels_destructor(void *data)
53 {
54 	struct channels *c = data;
55 
56 	/* flush from primary hash */
57 	hash_flush(c->ht_numb);
58 
59 	mem_deref(c->ht_numb);
60 	mem_deref(c->ht_peer);
61 }
62 
63 
chan_destructor(void * data)64 static void chan_destructor(void *data)
65 {
66 	struct chan *chan = data;
67 
68 	tmr_cancel(&chan->tmr);
69 	mem_deref(chan->ct);
70 	hash_unlink(&chan->he_numb);
71 	hash_unlink(&chan->he_peer);
72 }
73 
74 
numb_hash_cmp_handler(struct le * le,void * arg)75 static bool numb_hash_cmp_handler(struct le *le, void *arg)
76 {
77 	const struct chan *chan = le->data;
78 	const uint16_t *nr = arg;
79 
80 	return chan->nr == *nr;
81 }
82 
83 
peer_hash_cmp_handler(struct le * le,void * arg)84 static bool peer_hash_cmp_handler(struct le *le, void *arg)
85 {
86 	const struct chan *chan = le->data;
87 
88 	return sa_cmp(&chan->peer, arg, SA_ALL);
89 }
90 
91 
timeout(void * arg)92 static void timeout(void *arg)
93 {
94 	struct chan *chan = arg;
95 	int err;
96 
97 	err = chanbind_request(chan, true);
98 	if (err)
99 		chan->turnc->th(err, 0, NULL, NULL, NULL, NULL,
100 				chan->turnc->arg);
101 }
102 
103 
chanbind_resp_handler(int err,uint16_t scode,const char * reason,const struct stun_msg * msg,void * arg)104 static void chanbind_resp_handler(int err, uint16_t scode, const char *reason,
105 				  const struct stun_msg *msg, void *arg)
106 {
107 	struct chan *chan = arg;
108 
109 	if (err || turnc_request_loops(&chan->ls, scode))
110 		goto out;
111 
112 	switch (scode) {
113 
114 	case 0:
115 		tmr_start(&chan->tmr, CHAN_REFRESH * 1000, timeout, chan);
116 		if (chan->ch) {
117 			chan->ch(chan->arg);
118 			chan->ch  = NULL;
119 			chan->arg = NULL;
120 		}
121 		return;
122 
123 	case 401:
124 	case 438:
125 		err = turnc_keygen(chan->turnc, msg);
126 		if (err)
127 			break;
128 
129 		err = chanbind_request(chan, false);
130 		if (err)
131 			break;
132 
133 		return;
134 
135 	default:
136 		break;
137 	}
138 
139  out:
140 	chan->turnc->th(err, scode, reason, NULL, NULL, msg, chan->turnc->arg);
141 }
142 
143 
chanbind_request(struct chan * chan,bool reset_ls)144 static int chanbind_request(struct chan *chan, bool reset_ls)
145 {
146 	struct turnc *t = chan->turnc;
147 
148 	if (reset_ls)
149 		turnc_loopstate_reset(&chan->ls);
150 
151 	return stun_request(&chan->ct, t->stun, t->proto, t->sock, &t->srv, 0,
152 			    STUN_METHOD_CHANBIND,
153 			    t->realm ? t->md5_hash : NULL, sizeof(t->md5_hash),
154 			    false, chanbind_resp_handler, chan, 6,
155 			    STUN_ATTR_CHANNEL_NUMBER, &chan->nr,
156 			    STUN_ATTR_XOR_PEER_ADDR, &chan->peer,
157 			    STUN_ATTR_USERNAME, t->realm ? t->username : NULL,
158 			    STUN_ATTR_REALM, t->realm,
159 			    STUN_ATTR_NONCE, t->nonce,
160 			    STUN_ATTR_SOFTWARE, stun_software);
161 }
162 
163 
164 /**
165  * Add a TURN Channel for a peer
166  *
167  * @param turnc TURN Client
168  * @param peer  Peer IP-address
169  * @param ch    Channel handler
170  * @param arg   Handler argument
171  *
172  * @return 0 if success, otherwise errorcode
173  */
turnc_add_chan(struct turnc * turnc,const struct sa * peer,turnc_chan_h * ch,void * arg)174 int turnc_add_chan(struct turnc *turnc, const struct sa *peer,
175 		   turnc_chan_h *ch, void *arg)
176 {
177 	struct chan *chan;
178 	int err;
179 
180 	if (!turnc || !peer)
181 		return EINVAL;
182 
183 	if (turnc->chans->nr >= CHAN_NUMB_MAX)
184 		return ERANGE;
185 
186 	if (turnc_chan_find_peer(turnc, peer))
187 		return 0;
188 
189 	chan = mem_zalloc(sizeof(*chan), chan_destructor);
190 	if (!chan)
191 		return ENOMEM;
192 
193 	chan->nr = turnc->chans->nr++;
194 	chan->peer = *peer;
195 
196 	hash_append(turnc->chans->ht_numb, chan->nr, &chan->he_numb, chan);
197 	hash_append(turnc->chans->ht_peer, sa_hash(peer, SA_ALL),
198 		    &chan->he_peer, chan);
199 
200 	tmr_init(&chan->tmr);
201 	chan->turnc = turnc;
202 	chan->ch = ch;
203 	chan->arg = arg;
204 
205 	err = chanbind_request(chan, true);
206 	if (err)
207 		mem_deref(chan);
208 
209 	return err;
210 }
211 
212 
turnc_chan_hash_alloc(struct channels ** cp,uint32_t bsize)213 int turnc_chan_hash_alloc(struct channels **cp, uint32_t bsize)
214 {
215 	struct channels *c;
216 	int err;
217 
218 	if (!cp)
219 		return EINVAL;
220 
221 	c = mem_zalloc(sizeof(*c), channels_destructor);
222 	if (!c)
223 		return ENOMEM;
224 
225 	err = hash_alloc(&c->ht_numb, bsize);
226 	if (err)
227 		goto out;
228 
229 	err = hash_alloc(&c->ht_peer, bsize);
230 	if (err)
231 		goto out;
232 
233 	c->nr = CHAN_NUMB_MIN;
234 
235  out:
236 	if (err)
237 		mem_deref(c);
238 	else
239 		*cp = c;
240 
241 	return err;
242 }
243 
244 
turnc_chan_find_numb(const struct turnc * turnc,uint16_t nr)245 struct chan *turnc_chan_find_numb(const struct turnc *turnc, uint16_t nr)
246 {
247 	if (!turnc)
248 		return NULL;
249 
250 	return list_ledata(hash_lookup(turnc->chans->ht_numb, nr,
251 				       numb_hash_cmp_handler, &nr));
252 }
253 
254 
turnc_chan_find_peer(const struct turnc * turnc,const struct sa * peer)255 struct chan *turnc_chan_find_peer(const struct turnc *turnc,
256 				  const struct sa *peer)
257 {
258 	if (!turnc)
259 		return NULL;
260 
261 	return list_ledata(hash_lookup(turnc->chans->ht_peer,
262 				       sa_hash(peer, SA_ALL),
263 				       peer_hash_cmp_handler, (void *)peer));
264 }
265 
266 
turnc_chan_numb(const struct chan * chan)267 uint16_t turnc_chan_numb(const struct chan *chan)
268 {
269 	return chan ? chan->nr : 0;
270 }
271 
272 
turnc_chan_peer(const struct chan * chan)273 const struct sa *turnc_chan_peer(const struct chan *chan)
274 {
275 	return chan ? &chan->peer : NULL;
276 }
277 
278 
turnc_chan_hdr_encode(const struct chan_hdr * hdr,struct mbuf * mb)279 int turnc_chan_hdr_encode(const struct chan_hdr *hdr, struct mbuf *mb)
280 {
281 	int err;
282 
283 	if (!hdr || !mb)
284 		return EINVAL;
285 
286 	err  = mbuf_write_u16(mb, htons(hdr->nr));
287 	err |= mbuf_write_u16(mb, htons(hdr->len));
288 
289 	return err;
290 }
291 
292 
turnc_chan_hdr_decode(struct chan_hdr * hdr,struct mbuf * mb)293 int turnc_chan_hdr_decode(struct chan_hdr *hdr, struct mbuf *mb)
294 {
295 	if (!hdr || !mb)
296 		return EINVAL;
297 
298 	if (mbuf_get_left(mb) < sizeof(*hdr))
299 		return ENOENT;
300 
301 	hdr->nr  = ntohs(mbuf_read_u16(mb));
302 	hdr->len = ntohs(mbuf_read_u16(mb));
303 
304 	return 0;
305 }
306