blob: 1e5c69f9361f7277a6ab04373c40a62b8b9e6b36 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
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
27enum {COMPID_MIN = 1, COMPID_MAX = 255};
28
29
30static 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
66static 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
80static 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
118int 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
157int 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
175void 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
196void 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
212struct 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
231static 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
250void 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
264void 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
277int 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
290int 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}