1 /**
2  * @file comp.c  ICE Media component
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_list.h>
12 #include <re_tmr.h>
13 #include <re_sys.h>
14 #include <re_sa.h>
15 #include <re_udp.h>
16 #include <re_stun.h>
17 #include <re_turn.h>
18 #include <re_ice.h>
19 #include "ice.h"
20 
21 
22 #define DEBUG_MODULE "icecomp"
23 #define DEBUG_LEVEL 5
24 #include <re_dbg.h>
25 
26 
27 enum {COMPID_MIN = 1, COMPID_MAX = 255};
28 
29 
helper_recv_handler(struct sa * src,struct mbuf * mb,void * arg)30 static bool helper_recv_handler(struct sa *src, struct mbuf *mb, void *arg)
31 {
32 	struct icem_comp *comp = arg;
33 	struct icem *icem = comp->icem;
34 	struct stun_msg *msg = NULL;
35 	struct stun_unknown_attr ua;
36 	const size_t start = mb->pos;
37 
38 #if 0
39 	re_printf("{%d} UDP recv_helper: %u bytes from %J\n",
40 		  comp->id, mbuf_get_left(mb), src);
41 #endif
42 
43 	if (stun_msg_decode(&msg, mb, &ua))
44 		return false;
45 
46 	if (STUN_METHOD_BINDING == stun_msg_method(msg)) {
47 
48 		switch (stun_msg_class(msg)) {
49 
50 		case STUN_CLASS_REQUEST:
51 			(void)icem_stund_recv(comp, src, msg, start);
52 			break;
53 
54 		default:
55 			(void)stun_ctrans_recv(icem->stun, msg, &ua);
56 			break;
57 		}
58 	}
59 
60 	mem_deref(msg);
61 
62 	return true;  /* handled */
63 }
64 
65 
destructor(void * arg)66 static void destructor(void *arg)
67 {
68 	struct icem_comp *comp = arg;
69 
70 	tmr_cancel(&comp->tmr_ka);
71 	mem_deref(comp->turnc);
72 	mem_deref(comp->cp_sel);
73 	mem_deref(comp->def_lcand);
74 	mem_deref(comp->def_rcand);
75 	mem_deref(comp->uh);
76 	mem_deref(comp->sock);
77 }
78 
79 
cand_default(const struct list * lcandl,unsigned compid)80 static struct ice_cand *cand_default(const struct list *lcandl,
81 				     unsigned compid)
82 {
83 	struct ice_cand *def = NULL;
84 	struct le *le;
85 
86 	/* NOTE: list must be sorted by priority */
87 	for (le = list_head(lcandl); le; le = le->next) {
88 
89 		struct ice_cand *cand = le->data;
90 
91 		if (cand->compid != compid)
92 			continue;
93 
94 		switch (cand->type) {
95 
96 		case ICE_CAND_TYPE_RELAY:
97 			return cand;
98 
99 		case ICE_CAND_TYPE_SRFLX:
100 			if (!def || ICE_CAND_TYPE_SRFLX != def->type)
101 				def = cand;
102 			break;
103 
104 		case ICE_CAND_TYPE_HOST:
105 			if (!def)
106 				def = cand;
107 			break;
108 
109 		default:
110 			break;
111 		}
112 	}
113 
114 	return def;
115 }
116 
117 
icem_comp_alloc(struct icem_comp ** cp,struct icem * icem,int id,void * sock)118 int icem_comp_alloc(struct icem_comp **cp, struct icem *icem, int id,
119 		    void *sock)
120 {
121 	struct icem_comp *comp;
122 	struct sa local;
123 	int err;
124 
125 	if (!cp || !icem || id<1 || id>255 || !sock)
126 		return EINVAL;
127 
128 	comp = mem_zalloc(sizeof(*comp), destructor);
129 	if (!comp)
130 		return ENOMEM;
131 
132 	comp->id = id;
133 	comp->sock = mem_ref(sock);
134 	comp->icem = icem;
135 
136 	err = udp_register_helper(&comp->uh, sock, icem->layer,
137 				  NULL, helper_recv_handler, comp);
138 	if (err)
139 		goto out;
140 
141 	err = udp_local_get(comp->sock, &local);
142 	if (err)
143 		goto out;
144 
145 	comp->lport = sa_port(&local);
146 
147  out:
148 	if (err)
149 		mem_deref(comp);
150 	else
151 		*cp = comp;
152 
153 	return err;
154 }
155 
156 
icem_comp_set_default_cand(struct icem_comp * comp)157 int icem_comp_set_default_cand(struct icem_comp *comp)
158 {
159 	struct ice_cand *cand;
160 
161 	if (!comp)
162 		return EINVAL;
163 
164 	cand = cand_default(&comp->icem->lcandl, comp->id);
165 	if (!cand)
166 		return ENOENT;
167 
168 	mem_deref(comp->def_lcand);
169 	comp->def_lcand = mem_ref(cand);
170 
171 	return 0;
172 }
173 
174 
icem_comp_set_default_rcand(struct icem_comp * comp,struct ice_cand * rcand)175 void icem_comp_set_default_rcand(struct icem_comp *comp,
176 				 struct ice_cand *rcand)
177 {
178 	if (!comp)
179 		return;
180 
181 	icecomp_printf(comp, "Set default remote candidate: %s:%J\n",
182 		       ice_cand_type2name(rcand->type), &rcand->addr);
183 
184 	mem_deref(comp->def_rcand);
185 	comp->def_rcand = mem_ref(rcand);
186 
187 	if (comp->turnc) {
188 		icecomp_printf(comp, "Add TURN Channel to peer %J\n",
189 			       &rcand->addr);
190 
191 		(void)turnc_add_chan(comp->turnc, &rcand->addr, NULL, NULL);
192 	}
193 }
194 
195 
icem_comp_set_selected(struct icem_comp * comp,struct ice_candpair * cp)196 void icem_comp_set_selected(struct icem_comp *comp, struct ice_candpair *cp)
197 {
198 	if (!comp || !cp)
199 		return;
200 
201 	if (cp->state != ICE_CANDPAIR_SUCCEEDED) {
202 		DEBUG_WARNING("{%s.%u} set_selected: invalid state %s\n",
203 			      comp->icem->name, comp->id,
204 			      ice_candpair_state2name(cp->state));
205 	}
206 
207 	mem_deref(comp->cp_sel);
208 	comp->cp_sel = mem_ref(cp);
209 }
210 
211 
icem_comp_find(const struct icem * icem,unsigned compid)212 struct icem_comp *icem_comp_find(const struct icem *icem, unsigned compid)
213 {
214 	struct le *le;
215 
216 	if (!icem)
217 		return NULL;
218 
219 	for (le = icem->compl.head; le; le = le->next) {
220 
221 		struct icem_comp *comp = le->data;
222 
223 		if (comp->id == compid)
224 			return comp;
225 	}
226 
227 	return NULL;
228 }
229 
230 
timeout(void * arg)231 static void timeout(void *arg)
232 {
233 	struct icem_comp *comp = arg;
234 	struct ice_candpair *cp;
235 
236 	tmr_start(&comp->tmr_ka, ICE_DEFAULT_Tr * 1000 + rand_u16() % 1000,
237 		  timeout, comp);
238 
239 	/* find selected candidate-pair */
240 	cp = comp->cp_sel;
241 	if (!cp)
242 		return;
243 
244 	(void)stun_indication(comp->icem->proto, comp->sock, &cp->rcand->addr,
245 			      (cp->lcand->type == ICE_CAND_TYPE_RELAY) ? 4 : 0,
246 			      STUN_METHOD_BINDING, NULL, 0, true, 0);
247 }
248 
249 
icem_comp_keepalive(struct icem_comp * comp,bool enable)250 void icem_comp_keepalive(struct icem_comp *comp, bool enable)
251 {
252 	if (!comp)
253 		return;
254 
255 	if (enable) {
256 		tmr_start(&comp->tmr_ka, ICE_DEFAULT_Tr * 1000, timeout, comp);
257 	}
258 	else {
259 		tmr_cancel(&comp->tmr_ka);
260 	}
261 }
262 
263 
icecomp_printf(struct icem_comp * comp,const char * fmt,...)264 void icecomp_printf(struct icem_comp *comp, const char *fmt, ...)
265 {
266 	va_list ap;
267 
268 	if (!comp || !comp->icem->conf.debug)
269 		return;
270 
271 	va_start(ap, fmt);
272 	(void)re_printf("{%11s.%u} %v", comp->icem->name, comp->id, fmt, &ap);
273 	va_end(ap);
274 }
275 
276 
icecomp_debug(struct re_printf * pf,const struct icem_comp * comp)277 int icecomp_debug(struct re_printf *pf, const struct icem_comp *comp)
278 {
279 	if (!comp)
280 		return 0;
281 
282 	return re_hprintf(pf, "id=%u ldef=%J rdef=%J concluded=%d",
283 			  comp->id,
284 			  comp->def_lcand ? &comp->def_lcand->addr : NULL,
285 			  comp->def_rcand ? &comp->def_rcand->addr : NULL,
286 			  comp->concluded);
287 }
288 
289 
icem_set_turn_client(struct icem * icem,unsigned compid,struct turnc * turnc)290 int icem_set_turn_client(struct icem *icem, unsigned compid,
291 			 struct turnc *turnc)
292 {
293 	struct icem_comp *comp;
294 
295 	comp = icem_comp_find(icem, compid);
296 	if (!comp)
297 		return ENOENT;
298 
299 	comp->turnc = mem_deref(comp->turnc);
300 
301 	if (turnc)
302 		comp->turnc = mem_ref(turnc);
303 
304 	return 0;
305 }
306