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